From 36b191fae5f563deed295604bf0b76864ea3f595 Mon Sep 17 00:00:00 2001 From: Zhu Guodong Date: Fri, 28 Nov 2025 01:34:03 +0800 Subject: [PATCH] remove deprecated module: minddata --- .jenkins/check/config/filter_cppcheck.txt | 9 - .jenkins/check/config/filter_cpplint.txt | 23 - .jenkins/check/config/whitelizard.txt | 5 - build.bat | 4 +- build.sh | 1 - cmake/package_lite.cmake | 130 - mindspore-lite/CMakeLists.txt | 26 +- mindspore-lite/cmake/lite_options.cmake | 10 - .../mobilenetv2_arm64/README.md | 4 - .../examples/runtime_java/app/download.gradle | 2 +- .../examples/train_lenet_cpp/Makefile | 3 +- .../examples/train_lenet_cpp/README.md | 2 - .../examples/train_lenet_cpp/README_CN.md | 2 - .../examples/transfer_learning/Makefile | 2 - .../examples/transfer_learning/README.md | 4 +- .../examples/transfer_learning/README_CN.md | 2 - mindspore-lite/minddata/CMakeLists.txt | 691 -- .../minddata/dataset/api/data_helper.cc | 191 - .../minddata/dataset/api/datasets.cc | 2186 ------ .../minddata/dataset/api/execute.cc | 747 -- .../minddata/dataset/api/iterator.cc | 183 - .../minddata/dataset/api/python/python_mp.h | 96 - .../minddata/dataset/api/samplers.cc | 141 - .../minddata/dataset/api/transforms.cc | 295 - mindspore-lite/minddata/dataset/api/vision.cc | 1553 ---- .../dataset/callback/callback_manager.cc | 146 - .../dataset/callback/callback_manager.h | 90 - .../dataset/callback/callback_param.h | 42 - .../minddata/dataset/callback/ds_callback.h | 103 - .../dataset/callback/py_ds_callback.cc | 90 - .../dataset/callback/py_ds_callback.h | 131 - .../minddata/dataset/core/ascend_resource.cc | 108 - .../minddata/dataset/core/ascend_resource.h | 57 - .../minddata/dataset/core/client.cc | 28 - mindspore-lite/minddata/dataset/core/client.h | 65 - .../minddata/dataset/core/config_manager.cc | 227 - .../minddata/dataset/core/config_manager.h | 384 - .../minddata/dataset/core/cv_tensor.cc | 162 - .../minddata/dataset/core/cv_tensor.h | 112 - .../minddata/dataset/core/data_type.cc | 192 - .../minddata/dataset/core/data_type.h | 398 - .../minddata/dataset/core/de_tensor.cc | 126 - .../minddata/dataset/core/de_tensor.h | 71 - .../minddata/dataset/core/device_resource.cc | 70 - .../minddata/dataset/core/device_resource.h | 55 - .../minddata/dataset/core/device_tensor.cc | 179 - .../minddata/dataset/core/device_tensor.h | 82 - .../minddata/dataset/core/example.proto | 36 - .../minddata/dataset/core/feature.proto | 62 - .../minddata/dataset/core/global_context.cc | 76 - .../minddata/dataset/core/global_context.h | 121 - .../minddata/dataset/core/pybind_support.h | 88 - .../minddata/dataset/core/tensor.cc | 1318 ---- mindspore-lite/minddata/dataset/core/tensor.h | 913 --- .../minddata/dataset/core/tensor_helpers.cc | 81 - .../minddata/dataset/core/tensor_helpers.h | 51 - .../minddata/dataset/core/tensor_row.cc | 245 - .../minddata/dataset/core/tensor_row.h | 345 - .../minddata/dataset/core/tensor_shape.cc | 250 - .../minddata/dataset/core/tensor_shape.h | 218 - .../minddata/dataset/core/type_id.h | 92 - mindspore-lite/minddata/dataset/core/types.cc | 526 -- .../minddata/dataset/engine/connector.h | 211 - .../consumers/pull_based_tree_consumer.cc | 261 - .../consumers/pull_based_tree_consumer.h | 126 - .../dataset/engine/consumers/tree_consumer.cc | 951 --- .../dataset/engine/consumers/tree_consumer.h | 314 - .../minddata/dataset/engine/data_schema.cc | 491 -- .../minddata/dataset/engine/data_schema.h | 217 - .../dataset/engine/dataset_iterator.cc | 226 - .../dataset/engine/dataset_iterator.h | 127 - .../dataset/engine/datasetops/barrier_op.cc | 160 - .../dataset/engine/datasetops/barrier_op.h | 99 - .../dataset/engine/datasetops/batch_op.cc | 1021 --- .../dataset/engine/datasetops/batch_op.h | 262 - .../datasetops/bucket_batch_by_length_op.cc | 241 - .../datasetops/bucket_batch_by_length_op.h | 100 - .../build_sentence_piece_vocab_op.cc | 188 - .../build_sentence_piece_vocab_op.h | 100 - .../engine/datasetops/build_vocab_op.cc | 216 - .../engine/datasetops/build_vocab_op.h | 90 - .../engine/datasetops/cache_base_op.cc | 324 - .../dataset/engine/datasetops/cache_base_op.h | 122 - .../engine/datasetops/cache_lookup_op.cc | 98 - .../engine/datasetops/cache_lookup_op.h | 64 - .../engine/datasetops/cache_merge_op.cc | 322 - .../engine/datasetops/cache_merge_op.h | 150 - .../dataset/engine/datasetops/cache_op.cc | 220 - .../dataset/engine/datasetops/cache_op.h | 99 - .../dataset/engine/datasetops/concat_op.cc | 324 - .../dataset/engine/datasetops/concat_op.h | 122 - .../engine/datasetops/data_queue_op.cc | 1314 ---- .../dataset/engine/datasetops/data_queue_op.h | 230 - .../dataset/engine/datasetops/dataset_op.cc | 513 -- .../dataset/engine/datasetops/dataset_op.h | 434 -- .../engine/datasetops/epoch_ctrl_op.cc | 114 - .../dataset/engine/datasetops/epoch_ctrl_op.h | 68 - .../dataset/engine/datasetops/filter_op.cc | 201 - .../dataset/engine/datasetops/filter_op.h | 123 - .../engine/datasetops/map_op/cpu_map_job.cc | 59 - .../engine/datasetops/map_op/cpu_map_job.h | 53 - .../engine/datasetops/map_op/gpu_map_job.cc | 28 - .../engine/datasetops/map_op/gpu_map_job.h | 50 - .../engine/datasetops/map_op/map_job.h | 97 - .../engine/datasetops/map_op/map_op.cc | 923 --- .../dataset/engine/datasetops/map_op/map_op.h | 256 - .../engine/datasetops/map_op/npu_map_job.cc | 152 - .../engine/datasetops/map_op/npu_map_job.h | 52 - .../dataset/engine/datasetops/parallel_op.h | 468 -- .../dataset/engine/datasetops/pipeline_op.cc | 49 - .../dataset/engine/datasetops/pipeline_op.h | 72 - .../dataset/engine/datasetops/project_op.cc | 130 - .../dataset/engine/datasetops/project_op.h | 101 - .../engine/datasetops/receive_bridge_op.cc | 320 - .../engine/datasetops/receive_bridge_op.h | 130 - .../dataset/engine/datasetops/rename_op.cc | 146 - .../dataset/engine/datasetops/rename_op.h | 94 - .../dataset/engine/datasetops/repeat_op.cc | 164 - .../dataset/engine/datasetops/repeat_op.h | 119 - .../engine/datasetops/send_bridge_op.cc | 227 - .../engine/datasetops/send_bridge_op.h | 119 - .../dataset/engine/datasetops/shuffle_op.cc | 301 - .../dataset/engine/datasetops/shuffle_op.h | 149 - .../dataset/engine/datasetops/skip_op.cc | 125 - .../dataset/engine/datasetops/skip_op.h | 77 - .../engine/datasetops/source/ag_news_op.cc | 55 - .../engine/datasetops/source/ag_news_op.h | 77 - .../engine/datasetops/source/album_op.cc | 440 -- .../engine/datasetops/source/album_op.h | 178 - .../datasetops/source/amazon_review_op.cc | 50 - .../datasetops/source/amazon_review_op.h | 71 - .../engine/datasetops/source/caltech_op.cc | 32 - .../engine/datasetops/source/caltech_op.h | 57 - .../engine/datasetops/source/celeba_op.cc | 337 - .../engine/datasetops/source/celeba_op.h | 143 - .../engine/datasetops/source/cifar_op.cc | 405 - .../engine/datasetops/source/cifar_op.h | 128 - .../engine/datasetops/source/cityscapes_op.cc | 275 - .../engine/datasetops/source/cityscapes_op.h | 128 - .../engine/datasetops/source/clue_op.cc | 293 - .../engine/datasetops/source/clue_op.h | 107 - .../engine/datasetops/source/cmu_arctic_op.cc | 171 - .../engine/datasetops/source/cmu_arctic_op.h | 99 - .../engine/datasetops/source/coco_op.cc | 636 -- .../engine/datasetops/source/coco_op.h | 324 - .../engine/datasetops/source/conll2000_op.cc | 177 - .../engine/datasetops/source/conll2000_op.h | 96 - .../engine/datasetops/source/csv_op.cc | 821 --- .../dataset/engine/datasetops/source/csv_op.h | 246 - .../engine/datasetops/source/dbpedia_op.cc | 53 - .../engine/datasetops/source/dbpedia_op.h | 70 - .../engine/datasetops/source/div2k_op.cc | 271 - .../engine/datasetops/source/div2k_op.h | 122 - .../engine/datasetops/source/emnist_op.cc | 146 - .../engine/datasetops/source/emnist_op.h | 84 - .../engine/datasetops/source/en_wik9_op.cc | 128 - .../engine/datasetops/source/en_wik9_op.h | 77 - .../engine/datasetops/source/fake_image_op.cc | 144 - .../engine/datasetops/source/fake_image_op.h | 111 - .../datasetops/source/fashion_mnist_op.cc | 91 - .../datasetops/source/fashion_mnist_op.h | 67 - .../engine/datasetops/source/flickr_op.cc | 239 - .../engine/datasetops/source/flickr_op.h | 104 - .../engine/datasetops/source/food101_op.cc | 164 - .../engine/datasetops/source/food101_op.h | 101 - .../engine/datasetops/source/generator_op.cc | 552 -- .../engine/datasetops/source/generator_op.h | 185 - .../engine/datasetops/source/gtzan_op.cc | 335 - .../engine/datasetops/source/gtzan_op.h | 97 - .../datasetops/source/image_folder_op.cc | 404 - .../datasetops/source/image_folder_op.h | 180 - .../engine/datasetops/source/imdb_op.cc | 232 - .../engine/datasetops/source/imdb_op.h | 134 - .../engine/datasetops/source/io_block.cc | 86 - .../engine/datasetops/source/io_block.h | 147 - .../engine/datasetops/source/iwslt_op.cc | 543 -- .../engine/datasetops/source/iwslt_op.h | 236 - .../engine/datasetops/source/kitti_op.cc | 323 - .../engine/datasetops/source/kitti_op.h | 126 - .../engine/datasetops/source/kmnist_op.cc | 87 - .../engine/datasetops/source/kmnist_op.h | 65 - .../engine/datasetops/source/lfw_op.cc | 331 - .../dataset/engine/datasetops/source/lfw_op.h | 135 - .../engine/datasetops/source/libri_tts_op.cc | 234 - .../engine/datasetops/source/libri_tts_op.h | 120 - .../engine/datasetops/source/lj_speech_op.cc | 154 - .../engine/datasetops/source/lj_speech_op.h | 86 - .../engine/datasetops/source/lsun_op.cc | 169 - .../engine/datasetops/source/lsun_op.h | 114 - .../engine/datasetops/source/manifest_op.cc | 315 - .../engine/datasetops/source/manifest_op.h | 118 - .../datasetops/source/mappable_leaf_op.cc | 252 - .../datasetops/source/mappable_leaf_op.h | 140 - .../engine/datasetops/source/mindrecord_op.cc | 473 -- .../engine/datasetops/source/mindrecord_op.h | 197 - .../engine/datasetops/source/mnist_op.cc | 343 - .../engine/datasetops/source/mnist_op.h | 146 - .../engine/datasetops/source/multi30k_op.cc | 150 - .../engine/datasetops/source/multi30k_op.h | 85 - .../datasetops/source/nonmappable_leaf_op.cc | 420 -- .../datasetops/source/nonmappable_leaf_op.h | 237 - .../engine/datasetops/source/omniglot_op.cc | 130 - .../engine/datasetops/source/omniglot_op.h | 98 - .../datasetops/source/penn_treebank_op.cc | 55 - .../datasetops/source/penn_treebank_op.h | 69 - .../engine/datasetops/source/photo_tour_op.cc | 421 -- .../engine/datasetops/source/photo_tour_op.h | 156 - .../engine/datasetops/source/places365_op.cc | 312 - .../engine/datasetops/source/places365_op.h | 137 - .../engine/datasetops/source/qmnist_op.cc | 332 - .../engine/datasetops/source/qmnist_op.h | 113 - .../datasetops/source/random_data_op.cc | 188 - .../engine/datasetops/source/random_data_op.h | 130 - .../datasetops/source/rendered_sst2_op.cc | 392 - .../datasetops/source/rendered_sst2_op.h | 176 - .../source/sampler/distributed_sampler.cc | 231 - .../source/sampler/distributed_sampler.h | 96 - .../datasetops/source/sampler/pk_sampler.cc | 154 - .../datasetops/source/sampler/pk_sampler.h | 88 - .../source/sampler/random_sampler.cc | 147 - .../source/sampler/random_sampler.h | 72 - .../datasetops/source/sampler/sampler.cc | 221 - .../datasetops/source/sampler/sampler.h | 194 - .../source/sampler/sequential_sampler.cc | 155 - .../source/sampler/sequential_sampler.h | 78 - .../sampler/skip_first_epoch_sampler.cc | 194 - .../source/sampler/skip_first_epoch_sampler.h | 69 - .../source/sampler/subset_random_sampler.cc | 74 - .../source/sampler/subset_random_sampler.h | 68 - .../source/sampler/subset_sampler.cc | 143 - .../source/sampler/subset_sampler.h | 82 - .../source/sampler/weighted_random_sampler.cc | 198 - .../source/sampler/weighted_random_sampler.h | 98 - .../engine/datasetops/source/sbu_op.cc | 223 - .../dataset/engine/datasetops/source/sbu_op.h | 122 - .../engine/datasetops/source/semeion_op.cc | 178 - .../engine/datasetops/source/semeion_op.h | 92 - .../engine/datasetops/source/sogou_news_op.cc | 52 - .../engine/datasetops/source/sogou_news_op.h | 71 - .../datasetops/source/speech_commands_op.cc | 209 - .../datasetops/source/speech_commands_op.h | 113 - .../engine/datasetops/source/squad_op.cc | 369 - .../engine/datasetops/source/squad_op.h | 156 - .../engine/datasetops/source/sst2_op.cc | 137 - .../engine/datasetops/source/sst2_op.h | 85 - .../engine/datasetops/source/stl10_op.cc | 417 -- .../engine/datasetops/source/stl10_op.h | 118 - .../engine/datasetops/source/sun397_op.cc | 236 - .../engine/datasetops/source/sun397_op.h | 116 - .../engine/datasetops/source/tedlium_op.cc | 313 - .../engine/datasetops/source/tedlium_op.h | 126 - .../engine/datasetops/source/text_file_op.cc | 258 - .../engine/datasetops/source/text_file_op.h | 124 - .../engine/datasetops/source/tf_reader_op.cc | 1348 ---- .../engine/datasetops/source/tf_reader_op.h | 372 - .../engine/datasetops/source/udpos_op.cc | 186 - .../engine/datasetops/source/udpos_op.h | 96 - .../engine/datasetops/source/usps_op.cc | 362 - .../engine/datasetops/source/usps_op.h | 137 - .../engine/datasetops/source/voc_op.cc | 380 - .../dataset/engine/datasetops/source/voc_op.h | 179 - .../engine/datasetops/source/wider_face_op.cc | 304 - .../engine/datasetops/source/wider_face_op.h | 120 - .../engine/datasetops/source/wiki_text_op.cc | 53 - .../engine/datasetops/source/wiki_text_op.h | 70 - .../datasetops/source/yahoo_answers_op.cc | 53 - .../datasetops/source/yahoo_answers_op.h | 71 - .../datasetops/source/yelp_review_op.cc | 52 - .../engine/datasetops/source/yelp_review_op.h | 71 - .../engine/datasetops/source/yes_no_op.cc | 148 - .../engine/datasetops/source/yes_no_op.h | 92 - .../dataset/engine/datasetops/take_op.cc | 92 - .../dataset/engine/datasetops/take_op.h | 91 - .../dataset/engine/datasetops/zip_op.cc | 157 - .../dataset/engine/datasetops/zip_op.h | 112 - .../minddata/dataset/engine/execution_tree.cc | 309 - .../minddata/dataset/engine/execution_tree.h | 250 - .../dataset/engine/gpu_item_connector.h | 90 - .../dataset/engine/ir/cache/dataset_cache.cc | 62 - .../dataset/engine/ir/cache/dataset_cache.h | 50 - .../engine/ir/cache/dataset_cache_impl.cc | 105 - .../engine/ir/cache/dataset_cache_impl.h | 92 - .../ir/cache/pre_built_dataset_cache.cc | 31 - .../engine/ir/cache/pre_built_dataset_cache.h | 48 - .../engine/ir/datasetops/batch_node.cc | 185 - .../dataset/engine/ir/datasetops/batch_node.h | 130 - .../datasetops/bucket_batch_by_length_node.cc | 147 - .../datasetops/bucket_batch_by_length_node.h | 85 - .../build_sentence_piece_vocab_node.cc | 104 - .../build_sentence_piece_vocab_node.h | 92 - .../engine/ir/datasetops/build_vocab_node.cc | 95 - .../engine/ir/datasetops/build_vocab_node.h | 90 - .../engine/ir/datasetops/cache_lookup_node.cc | 81 - .../engine/ir/datasetops/cache_lookup_node.h | 90 - .../engine/ir/datasetops/cache_merge_node.cc | 66 - .../engine/ir/datasetops/cache_merge_node.h | 70 - .../engine/ir/datasetops/cache_node.cc | 70 - .../dataset/engine/ir/datasetops/cache_node.h | 74 - .../engine/ir/datasetops/concat_node.cc | 187 - .../engine/ir/datasetops/concat_node.h | 112 - .../engine/ir/datasetops/data_queue_node.cc | 141 - .../engine/ir/datasetops/data_queue_node.h | 103 - .../engine/ir/datasetops/dataset_node.cc | 686 -- .../engine/ir/datasetops/dataset_node.h | 478 -- .../engine/ir/datasetops/epoch_ctrl_node.cc | 78 - .../engine/ir/datasetops/epoch_ctrl_node.h | 79 - .../engine/ir/datasetops/filter_node.cc | 82 - .../engine/ir/datasetops/filter_node.h | 87 - .../dataset/engine/ir/datasetops/map_node.cc | 261 - .../dataset/engine/ir/datasetops/map_node.h | 138 - .../engine/ir/datasetops/project_node.cc | 86 - .../engine/ir/datasetops/project_node.h | 90 - .../engine/ir/datasetops/rename_node.cc | 95 - .../engine/ir/datasetops/rename_node.h | 93 - .../engine/ir/datasetops/repeat_node.cc | 112 - .../engine/ir/datasetops/repeat_node.h | 141 - .../dataset/engine/ir/datasetops/root_node.cc | 80 - .../dataset/engine/ir/datasetops/root_node.h | 98 - .../engine/ir/datasetops/shuffle_node.cc | 92 - .../engine/ir/datasetops/shuffle_node.h | 96 - .../dataset/engine/ir/datasetops/skip_node.cc | 106 - .../dataset/engine/ir/datasetops/skip_node.h | 111 - .../ir/datasetops/source/ag_news_node.cc | 199 - .../ir/datasetops/source/ag_news_node.h | 126 - .../engine/ir/datasetops/source/album_node.cc | 175 - .../engine/ir/datasetops/source/album_node.h | 106 - .../datasetops/source/amazon_review_node.cc | 195 - .../ir/datasetops/source/amazon_review_node.h | 120 - .../ir/datasetops/source/caltech256_node.cc | 138 - .../ir/datasetops/source/caltech256_node.h | 108 - .../ir/datasetops/source/celeba_node.cc | 264 - .../engine/ir/datasetops/source/celeba_node.h | 118 - .../ir/datasetops/source/cifar100_node.cc | 147 - .../ir/datasetops/source/cifar100_node.h | 102 - .../ir/datasetops/source/cifar10_node.cc | 148 - .../ir/datasetops/source/cifar10_node.h | 102 - .../ir/datasetops/source/cityscapes_node.cc | 147 - .../ir/datasetops/source/cityscapes_node.h | 109 - .../engine/ir/datasetops/source/clue_node.cc | 291 - .../engine/ir/datasetops/source/clue_node.h | 149 - .../ir/datasetops/source/cmu_arctic_node.cc | 116 - .../ir/datasetops/source/cmu_arctic_node.h | 95 - .../engine/ir/datasetops/source/coco_node.cc | 251 - .../engine/ir/datasetops/source/coco_node.h | 119 - .../ir/datasetops/source/conll2000_node.cc | 206 - .../ir/datasetops/source/conll2000_node.h | 130 - .../engine/ir/datasetops/source/csv_node.cc | 234 - .../engine/ir/datasetops/source/csv_node.h | 150 - .../ir/datasetops/source/dbpedia_node.cc | 209 - .../ir/datasetops/source/dbpedia_node.h | 120 - .../engine/ir/datasetops/source/div2k_node.cc | 156 - .../engine/ir/datasetops/source/div2k_node.h | 108 - .../ir/datasetops/source/emnist_node.cc | 120 - .../engine/ir/datasetops/source/emnist_node.h | 110 - .../ir/datasetops/source/en_wik9_node.cc | 174 - .../ir/datasetops/source/en_wik9_node.h | 136 - .../ir/datasetops/source/fake_image_node.cc | 135 - .../ir/datasetops/source/fake_image_node.h | 99 - .../datasetops/source/fashion_mnist_node.cc | 114 - .../ir/datasetops/source/fashion_mnist_node.h | 94 - .../ir/datasetops/source/flickr_node.cc | 170 - .../engine/ir/datasetops/source/flickr_node.h | 106 - .../ir/datasetops/source/food101_node.cc | 152 - .../ir/datasetops/source/food101_node.h | 104 - .../ir/datasetops/source/generator_node.cc | 182 - .../ir/datasetops/source/generator_node.h | 144 - .../engine/ir/datasetops/source/gtzan_node.cc | 110 - .../engine/ir/datasetops/source/gtzan_node.h | 95 - .../ir/datasetops/source/image_folder_node.cc | 193 - .../ir/datasetops/source/image_folder_node.h | 123 - .../engine/ir/datasetops/source/imdb_node.cc | 143 - .../engine/ir/datasetops/source/imdb_node.h | 113 - .../ir/datasetops/source/iwslt2016_node.cc | 193 - .../ir/datasetops/source/iwslt2016_node.h | 137 - .../ir/datasetops/source/iwslt2017_node.cc | 184 - .../ir/datasetops/source/iwslt2017_node.h | 133 - .../engine/ir/datasetops/source/kitti_node.cc | 142 - .../engine/ir/datasetops/source/kitti_node.h | 104 - .../ir/datasetops/source/kmnist_node.cc | 114 - .../engine/ir/datasetops/source/kmnist_node.h | 101 - .../engine/ir/datasetops/source/lfw_node.cc | 165 - .../engine/ir/datasetops/source/lfw_node.h | 123 - .../ir/datasetops/source/libri_tts_node.cc | 121 - .../ir/datasetops/source/libri_tts_node.h | 95 - .../ir/datasetops/source/lj_speech_node.cc | 120 - .../ir/datasetops/source/lj_speech_node.h | 95 - .../engine/ir/datasetops/source/lsun_node.cc | 125 - .../engine/ir/datasetops/source/lsun_node.h | 111 - .../ir/datasetops/source/manifest_node.cc | 189 - .../ir/datasetops/source/manifest_node.h | 108 - .../ir/datasetops/source/minddata_node.cc | 263 - .../ir/datasetops/source/minddata_node.h | 124 - .../engine/ir/datasetops/source/mnist_node.cc | 140 - .../engine/ir/datasetops/source/mnist_node.h | 102 - .../ir/datasetops/source/multi30k_node.cc | 199 - .../ir/datasetops/source/multi30k_node.h | 134 - .../ir/datasetops/source/omniglot_node.cc | 125 - .../ir/datasetops/source/omniglot_node.h | 103 - .../datasetops/source/penn_treebank_node.cc | 200 - .../ir/datasetops/source/penn_treebank_node.h | 124 - .../ir/datasetops/source/photo_tour_node.cc | 131 - .../ir/datasetops/source/photo_tour_node.h | 98 - .../ir/datasetops/source/places365_node.cc | 119 - .../ir/datasetops/source/places365_node.h | 100 - .../ir/datasetops/source/qmnist_node.cc | 154 - .../engine/ir/datasetops/source/qmnist_node.h | 109 - .../ir/datasetops/source/random_node.cc | 151 - .../engine/ir/datasetops/source/random_node.h | 141 - .../datasetops/source/rendered_sst2_node.cc | 149 - .../ir/datasetops/source/rendered_sst2_node.h | 117 - .../source/samplers/distributed_sampler_ir.cc | 157 - .../source/samplers/distributed_sampler_ir.h | 81 - .../source/samplers/pk_sampler_ir.cc | 112 - .../source/samplers/pk_sampler_ir.h | 72 - .../source/samplers/prebuilt_sampler_ir.cc | 90 - .../source/samplers/prebuilt_sampler_ir.h | 64 - .../source/samplers/random_sampler_ir.cc | 106 - .../source/samplers/random_sampler_ir.h | 74 - .../datasetops/source/samplers/samplers_ir.cc | 90 - .../datasetops/source/samplers/samplers_ir.h | 95 - .../source/samplers/sequential_sampler_ir.cc | 104 - .../source/samplers/sequential_sampler_ir.h | 71 - .../samplers/skip_first_epoch_sampler_ir.cc | 68 - .../samplers/skip_first_epoch_sampler_ir.h | 56 - .../samplers/subset_random_sampler_ir.cc | 92 - .../samplers/subset_random_sampler_ir.h | 61 - .../source/samplers/subset_sampler_ir.cc | 99 - .../source/samplers/subset_sampler_ir.h | 72 - .../samplers/weighted_random_sampler_ir.cc | 102 - .../samplers/weighted_random_sampler_ir.h | 69 - .../engine/ir/datasetops/source/sbu_node.cc | 125 - .../engine/ir/datasetops/source/sbu_node.h | 95 - .../ir/datasetops/source/semeion_node.cc | 113 - .../ir/datasetops/source/semeion_node.h | 94 - .../ir/datasetops/source/sogou_news_node.cc | 196 - .../ir/datasetops/source/sogou_news_node.h | 135 - .../datasetops/source/speech_commands_node.cc | 120 - .../datasetops/source/speech_commands_node.h | 97 - .../engine/ir/datasetops/source/squad_node.cc | 176 - .../engine/ir/datasetops/source/squad_node.h | 114 - .../engine/ir/datasetops/source/sst2_node.cc | 202 - .../engine/ir/datasetops/source/sst2_node.h | 120 - .../engine/ir/datasetops/source/stl10_node.cc | 121 - .../engine/ir/datasetops/source/stl10_node.h | 96 - .../ir/datasetops/source/sun397_node.cc | 114 - .../engine/ir/datasetops/source/sun397_node.h | 103 - .../ir/datasetops/source/tedlium_node.cc | 154 - .../ir/datasetops/source/tedlium_node.h | 110 - .../ir/datasetops/source/text_file_node.cc | 194 - .../ir/datasetops/source/text_file_node.h | 116 - .../ir/datasetops/source/tf_record_node.cc | 423 -- .../ir/datasetops/source/tf_record_node.h | 208 - .../engine/ir/datasetops/source/udpos_node.cc | 208 - .../engine/ir/datasetops/source/udpos_node.h | 120 - .../engine/ir/datasetops/source/usps_node.cc | 170 - .../engine/ir/datasetops/source/usps_node.h | 120 - .../engine/ir/datasetops/source/voc_node.cc | 249 - .../engine/ir/datasetops/source/voc_node.h | 128 - .../ir/datasetops/source/wider_face_node.cc | 133 - .../ir/datasetops/source/wider_face_node.h | 103 - .../ir/datasetops/source/wiki_text_node.cc | 197 - .../ir/datasetops/source/wiki_text_node.h | 139 - .../datasetops/source/yahoo_answers_node.cc | 216 - .../ir/datasetops/source/yahoo_answers_node.h | 120 - .../ir/datasetops/source/yelp_review_node.cc | 190 - .../ir/datasetops/source/yelp_review_node.h | 135 - .../ir/datasetops/source/yes_no_node.cc | 116 - .../engine/ir/datasetops/source/yes_no_node.h | 92 - .../engine/ir/datasetops/sync_wait_node.cc | 57 - .../engine/ir/datasetops/sync_wait_node.h | 69 - .../dataset/engine/ir/datasetops/take_node.cc | 98 - .../dataset/engine/ir/datasetops/take_node.h | 103 - .../dataset/engine/ir/datasetops/zip_node.cc | 103 - .../dataset/engine/ir/datasetops/zip_node.h | 89 - .../dataset/engine/jagged_connector.h | 89 - .../dataset/engine/operator_connector.h | 67 - .../minddata/dataset/engine/opt/pass.cc | 296 - .../minddata/dataset/engine/opt/pass.h | 237 - .../engine/opt/post/auto_worker_pass.cc | 139 - .../engine/opt/post/auto_worker_pass.h | 86 - .../dataset/engine/opt/pre/add_skip_pass.cc | 156 - .../dataset/engine/opt/pre/add_skip_pass.h | 121 - .../engine/opt/pre/cache_validation_pass.cc | 189 - .../engine/opt/pre/cache_validation_pass.h | 111 - .../dataset/engine/opt/pre/debug_mode_pass.cc | 100 - .../dataset/engine/opt/pre/debug_mode_pass.h | 77 - .../dataset/engine/opt/pre/deep_copy_pass.cc | 72 - .../dataset/engine/opt/pre/deep_copy_pass.h | 61 - .../dataset/engine/opt/pre/epoch_ctrl_pass.cc | 97 - .../dataset/engine/opt/pre/epoch_ctrl_pass.h | 98 - .../dataset/engine/opt/pre/getter_pass.cc | 27 - .../dataset/engine/opt/pre/getter_pass.h | 44 - .../engine/opt/pre/input_validation_pass.cc | 45 - .../engine/opt/pre/input_validation_pass.h | 40 - .../dataset/engine/opt/pre/insert_map_pass.cc | 80 - .../dataset/engine/opt/pre/insert_map_pass.h | 44 - .../engine/opt/pre/node_removal_pass.cc | 77 - .../engine/opt/pre/node_removal_pass.h | 86 - .../engine/opt/pre/skip_pushdown_pass.cc | 185 - .../engine/opt/pre/skip_pushdown_pass.h | 177 - .../minddata/dataset/engine/perf/auto_tune.cc | 757 -- .../minddata/dataset/engine/perf/auto_tune.h | 306 - .../dataset/engine/perf/connector_size.cc | 170 - .../dataset/engine/perf/connector_size.h | 87 - .../dataset/engine/perf/cpu_sampler.cc | 782 -- .../dataset/engine/perf/cpu_sampler.h | 216 - .../dataset/engine/perf/cyclic_array.h | 197 - .../engine/perf/dataset_iterator_tracing.cc | 27 - .../engine/perf/dataset_iterator_tracing.h | 43 - .../engine/perf/device_queue_tracing.cc | 28 - .../engine/perf/device_queue_tracing.h | 43 - .../dataset/engine/perf/info_collector.cc | 53 - .../dataset/engine/perf/info_collector.h | 40 - .../minddata/dataset/engine/perf/monitor.cc | 67 - .../minddata/dataset/engine/perf/monitor.h | 61 - .../minddata/dataset/engine/perf/perf_data.h | 92 - .../minddata/dataset/engine/perf/profiling.cc | 876 --- .../minddata/dataset/engine/perf/profiling.h | 624 -- .../dataset/engine/python_runtime_context.cc | 54 - .../dataset/engine/python_runtime_context.h | 46 - .../dataset/engine/runtime_context.cc | 48 - .../minddata/dataset/engine/runtime_context.h | 68 - .../minddata/dataset/engine/serdes.cc | 487 -- .../minddata/dataset/engine/serdes.h | 247 - .../minddata/dataset/engine/tree_adapter.cc | 841 --- .../minddata/dataset/engine/tree_adapter.h | 191 - .../dataset/engine/tree_adapter_lite.cc | 180 - .../dataset/engine/tree_adapter_lite.h | 84 - .../minddata/dataset/engine/tree_modifier.cc | 70 - .../minddata/dataset/engine/tree_modifier.h | 163 - .../minddata/dataset/include/dataset/audio.h | 1437 ---- .../minddata/dataset/include/dataset/config.h | 163 - .../dataset/include/dataset/constants.h | 366 - .../dataset/include/dataset/data_helper.h | 487 -- .../dataset/include/dataset/datasets.h | 6538 ----------------- .../dataset/include/dataset/execute.h | 196 - .../dataset/include/dataset/iterator.h | 196 - .../dataset/include/dataset/samplers.h | 346 - .../minddata/dataset/include/dataset/text.h | 1092 --- .../dataset/include/dataset/transforms.h | 638 -- .../minddata/dataset/include/dataset/vision.h | 2129 ------ .../dataset/include/dataset/vision_ascend.h | 206 - .../dataset/include/dataset/vision_lite.h | 625 -- .../minddata/dataset/kernels/c_func_op.cc | 34 - .../minddata/dataset/kernels/c_func_op.h | 50 - .../dataset/kernels/data/CMakeLists.txt | 20 - .../dataset/kernels/data/compose_op.cc | 58 - .../dataset/kernels/data/compose_op.h | 106 - .../dataset/kernels/data/concatenate_op.cc | 68 - .../dataset/kernels/data/concatenate_op.h | 63 - .../dataset/kernels/data/data_utils.cc | 900 --- .../dataset/kernels/data/data_utils.h | 195 - .../dataset/kernels/data/duplicate_op.cc | 36 - .../dataset/kernels/data/duplicate_op.h | 42 - .../minddata/dataset/kernels/data/fill_op.cc | 30 - .../minddata/dataset/kernels/data/fill_op.h | 45 - .../minddata/dataset/kernels/data/mask_op.cc | 48 - .../minddata/dataset/kernels/data/mask_op.h | 53 - .../minddata/dataset/kernels/data/no_op.h | 39 - .../dataset/kernels/data/one_hot_op.cc | 47 - .../dataset/kernels/data/one_hot_op.h | 46 - .../dataset/kernels/data/pad_end_op.cc | 39 - .../dataset/kernels/data/pad_end_op.h | 48 - .../dataset/kernels/data/parse_example_op.cc | 1462 ---- .../dataset/kernels/data/parse_example_op.h | 76 - .../dataset/kernels/data/random_apply_op.cc | 66 - .../dataset/kernels/data/random_apply_op.h | 77 - .../dataset/kernels/data/random_choice_op.cc | 89 - .../dataset/kernels/data/random_choice_op.h | 75 - .../minddata/dataset/kernels/data/slice_op.cc | 29 - .../minddata/dataset/kernels/data/slice_op.h | 55 - .../dataset/kernels/data/to_float16_op.cc | 36 - .../dataset/kernels/data/to_float16_op.h | 48 - .../dataset/kernels/data/type_cast_op.cc | 40 - .../dataset/kernels/data/type_cast_op.h | 51 - .../dataset/kernels/data/unique_op.cc | 56 - .../minddata/dataset/kernels/data/unique_op.h | 44 - .../dataset/kernels/image/CMakeLists.txt | 90 - .../kernels/image/adjust_brightness_op.cc | 30 - .../kernels/image/adjust_brightness_op.h | 45 - .../kernels/image/adjust_contrast_op.cc | 30 - .../kernels/image/adjust_contrast_op.h | 45 - .../dataset/kernels/image/adjust_gamma_op.cc | 43 - .../dataset/kernels/image/adjust_gamma_op.h | 54 - .../dataset/kernels/image/adjust_hue_op.cc | 30 - .../dataset/kernels/image/adjust_hue_op.h | 45 - .../kernels/image/adjust_saturation_op.cc | 30 - .../kernels/image/adjust_saturation_op.h | 45 - .../dataset/kernels/image/affine_op.cc | 63 - .../dataset/kernels/image/affine_op.h | 61 - .../dataset/kernels/image/auto_augment_op.cc | 155 - .../dataset/kernels/image/auto_augment_op.h | 66 - .../dataset/kernels/image/auto_contrast_op.cc | 31 - .../dataset/kernels/image/auto_contrast_op.h | 58 - .../dataset/kernels/image/bounding_box.cc | 234 - .../dataset/kernels/image/bounding_box.h | 137 - .../kernels/image/bounding_box_augment_op.cc | 77 - .../kernels/image/bounding_box_augment_op.h | 63 - .../dataset/kernels/image/center_crop_op.cc | 143 - .../dataset/kernels/image/center_crop_op.h | 56 - .../dataset/kernels/image/convert_color_op.cc | 33 - .../dataset/kernels/image/convert_color_op.h | 45 - .../minddata/dataset/kernels/image/crop_op.cc | 56 - .../minddata/dataset/kernels/image/crop_op.h | 67 - .../dataset/kernels/image/cut_out_op.cc | 49 - .../dataset/kernels/image/cut_out_op.h | 72 - .../dataset/kernels/image/cutmix_batch_op.cc | 257 - .../dataset/kernels/image/cutmix_batch_op.h | 80 - .../dataset/kernels/image/decode_op.cc | 73 - .../dataset/kernels/image/decode_op.h | 51 - .../dataset/kernels/image/decode_video_op.cc | 59 - .../dataset/kernels/image/decode_video_op.h | 45 - .../dataset/kernels/image/dvpp/CMakeLists.txt | 22 - .../dataset/kernels/image/dvpp/acl_adapter.cc | 760 -- .../dataset/kernels/image/dvpp/acl_adapter.h | 267 - .../image/dvpp/ascend310/dvpp_crop_jpeg_op.cc | 162 - .../image/dvpp/ascend310/dvpp_crop_jpeg_op.h | 60 - .../dvpp/ascend310/dvpp_decode_jpeg_op.cc | 148 - .../dvpp/ascend310/dvpp_decode_jpeg_op.h | 57 - .../dvpp/ascend310/dvpp_decode_png_op.cc | 143 - .../image/dvpp/ascend310/dvpp_decode_png_op.h | 57 - .../dvpp_decode_resize_crop_jpeg_op.cc | 151 - .../dvpp_decode_resize_crop_jpeg_op.h | 65 - .../ascend310/dvpp_decode_resize_jpeg_op.cc | 144 - .../ascend310/dvpp_decode_resize_jpeg_op.h | 60 - .../dvpp/ascend310/dvpp_decode_video_op.cc | 90 - .../dvpp/ascend310/dvpp_decode_video_op.h | 72 - .../image/dvpp/ascend310/dvpp_normalize_op.cc | 41 - .../image/dvpp/ascend310/dvpp_normalize_op.h | 51 - .../dvpp/ascend310/dvpp_resize_jpeg_op.cc | 162 - .../dvpp/ascend310/dvpp_resize_jpeg_op.h | 61 - .../kernels/image/dvpp/utils/AclLiteError.h | 191 - .../kernels/image/dvpp/utils/AclLiteType.h | 102 - .../kernels/image/dvpp/utils/AclLiteUtils.cc | 524 -- .../kernels/image/dvpp/utils/AclLiteUtils.h | 473 -- .../kernels/image/dvpp/utils/CMakeLists.txt | 41 - .../kernels/image/dvpp/utils/CommonDataType.h | 172 - .../kernels/image/dvpp/utils/DvppCommon.cc | 1829 ----- .../kernels/image/dvpp/utils/DvppCommon.h | 249 - .../kernels/image/dvpp/utils/ErrorCode.cpp | 54 - .../kernels/image/dvpp/utils/ErrorCode.h | 291 - .../kernels/image/dvpp/utils/MDAclProcess.cc | 1052 --- .../kernels/image/dvpp/utils/MDAclProcess.h | 161 - .../image/dvpp/utils/ResourceManager.cc | 142 - .../image/dvpp/utils/ResourceManager.h | 79 - .../image/dvpp/utils/ThreadSafeQueue.h | 131 - .../kernels/image/dvpp/utils/VdecHelper.cc | 366 - .../kernels/image/dvpp/utils/VdecHelper.h | 90 - .../kernels/image/dvpp/utils/acl_env_guard.cc | 94 - .../kernels/image/dvpp/utils/acl_env_guard.h | 53 - .../kernels/image/dvpp/utils/acl_plugin.cc | 630 -- .../kernels/image/dvpp/utils/acl_plugin.h | 179 - .../image/dvpp/utils/dvpp_image_utils.cc | 2840 ------- .../image/dvpp/utils/dvpp_image_utils.h | 356 - .../kernels/image/dvpp/utils/dvpp_video.cc | 701 -- .../kernels/image/dvpp/utils/dvpp_video.h | 234 - .../kernels/image/dvpp/utils/resouce_info.h | 58 - .../dataset/kernels/image/equalize_op.cc | 28 - .../dataset/kernels/image/equalize_op.h | 41 - .../dataset/kernels/image/erase_op.cc | 43 - .../minddata/dataset/kernels/image/erase_op.h | 52 - .../dataset/kernels/image/exif_utils.cc | 161 - .../dataset/kernels/image/exif_utils.h | 29 - .../dataset/kernels/image/gaussian_blur_op.cc | 33 - .../dataset/kernels/image/gaussian_blur_op.h | 63 - .../kernels/image/horizontal_flip_op.cc | 55 - .../kernels/image/horizontal_flip_op.h | 40 - .../dataset/kernels/image/hwc_to_chw_op.cc | 47 - .../dataset/kernels/image/hwc_to_chw_op.h | 39 - .../dataset/kernels/image/image_utils.cc | 2867 -------- .../dataset/kernels/image/image_utils.h | 601 -- .../dataset/kernels/image/invert_op.cc | 35 - .../dataset/kernels/image/invert_op.h | 41 - .../kernels/image/lite_cv/CMakeLists.txt | 0 .../dataset/kernels/image/lite_cv/canny.cc | 284 - .../kernels/image/lite_cv/gaussian_blur.cc | 89 - .../kernels/image/lite_cv/image_process.cc | 2160 ------ .../kernels/image/lite_cv/image_process.h | 657 -- .../dataset/kernels/image/lite_cv/lite_mat.cc | 749 -- .../dataset/kernels/image/lite_cv/lite_mat.h | 481 -- .../kernels/image/lite_cv/warp_affine.cc | 578 -- .../dataset/kernels/image/lite_image_utils.cc | 1219 --- .../dataset/kernels/image/lite_image_utils.h | 239 - .../dataset/kernels/image/math_utils.cc | 87 - .../dataset/kernels/image/math_utils.h | 51 - .../dataset/kernels/image/mixup_batch_op.cc | 171 - .../dataset/kernels/image/mixup_batch_op.h | 52 - .../dataset/kernels/image/normalize_op.cc | 88 - .../dataset/kernels/image/normalize_op.h | 48 - .../dataset/kernels/image/normalize_pad_op.cc | 48 - .../dataset/kernels/image/normalize_pad_op.h | 49 - .../minddata/dataset/kernels/image/pad_op.cc | 59 - .../minddata/dataset/kernels/image/pad_op.h | 70 - .../dataset/kernels/image/pad_to_size_op.cc | 90 - .../dataset/kernels/image/pad_to_size_op.h | 48 - .../dataset/kernels/image/perspective_op.cc | 31 - .../dataset/kernels/image/perspective_op.h | 49 - .../dataset/kernels/image/posterize_op.cc | 30 - .../dataset/kernels/image/posterize_op.h | 46 - .../dataset/kernels/image/rand_augment_op.cc | 93 - .../dataset/kernels/image/rand_augment_op.h | 66 - .../image/random_adjust_sharpness_op.cc | 42 - .../image/random_adjust_sharpness_op.h | 51 - .../dataset/kernels/image/random_affine_op.cc | 91 - .../dataset/kernels/image/random_affine_op.h | 62 - .../kernels/image/random_auto_contrast_op.cc | 48 - .../kernels/image/random_auto_contrast_op.h | 55 - .../kernels/image/random_color_adjust_op.cc | 93 - .../kernels/image/random_color_adjust_op.h | 73 - .../dataset/kernels/image/random_color_op.cc | 67 - .../dataset/kernels/image/random_color_op.h | 67 - .../image/random_crop_and_resize_op.cc | 199 - .../kernels/image/random_crop_and_resize_op.h | 69 - .../random_crop_and_resize_with_bbox_op.cc | 56 - .../random_crop_and_resize_with_bbox_op.h | 46 - .../image/random_crop_decode_resize_op.cc | 65 - .../image/random_crop_decode_resize_op.h | 52 - .../dataset/kernels/image/random_crop_op.cc | 226 - .../dataset/kernels/image/random_crop_op.h | 92 - .../kernels/image/random_crop_with_bbox_op.cc | 74 - .../kernels/image/random_crop_with_bbox_op.h | 48 - .../kernels/image/random_equalize_op.cc | 44 - .../kernels/image/random_equalize_op.h | 51 - .../image/random_horizontal_flip_op.cc | 67 - .../kernels/image/random_horizontal_flip_op.h | 55 - .../random_horizontal_flip_with_bbox_op.cc | 58 - .../random_horizontal_flip_with_bbox_op.h | 53 - .../dataset/kernels/image/random_invert_op.cc | 45 - .../dataset/kernels/image/random_invert_op.h | 52 - .../kernels/image/random_lighting_op.cc | 36 - .../kernels/image/random_lighting_op.h | 47 - .../kernels/image/random_posterize_op.cc | 35 - .../kernels/image/random_posterize_op.h | 46 - .../dataset/kernels/image/random_resize_op.cc | 37 - .../dataset/kernels/image/random_resize_op.h | 55 - .../image/random_resize_with_bbox_op.cc | 34 - .../image/random_resize_with_bbox_op.h | 53 - .../kernels/image/random_rotation_op.cc | 85 - .../kernels/image/random_rotation_op.h | 73 - .../image/random_select_subpolicy_op.cc | 93 - .../image/random_select_subpolicy_op.h | 76 - .../kernels/image/random_sharpness_op.cc | 40 - .../kernels/image/random_sharpness_op.h | 50 - .../kernels/image/random_solarize_op.cc | 46 - .../kernels/image/random_solarize_op.h | 47 - .../kernels/image/random_vertical_flip_op.cc | 44 - .../kernels/image/random_vertical_flip_op.h | 49 - .../random_vertical_flip_with_bbox_op.cc | 56 - .../image/random_vertical_flip_with_bbox_op.h | 47 - .../dataset/kernels/image/rescale_op.cc | 35 - .../dataset/kernels/image/rescale_op.h | 50 - .../kernels/image/resize_bilinear_op.h | 56 - .../dataset/kernels/image/resize_cubic_op.cc | 303 - .../dataset/kernels/image/resize_cubic_op.h | 56 - .../dataset/kernels/image/resize_op.cc | 124 - .../dataset/kernels/image/resize_op.h | 68 - .../kernels/image/resize_preserve_ar_op.cc | 38 - .../kernels/image/resize_preserve_ar_op.h | 54 - .../kernels/image/resize_with_bbox_op.cc | 52 - .../kernels/image/resize_with_bbox_op.h | 59 - .../dataset/kernels/image/resized_crop_op.cc | 93 - .../dataset/kernels/image/resized_crop_op.h | 59 - .../dataset/kernels/image/rgb_to_bgr_op.cc | 36 - .../dataset/kernels/image/rgb_to_bgr_op.h | 41 - .../dataset/kernels/image/rgb_to_gray_op.cc | 31 - .../dataset/kernels/image/rgb_to_gray_op.h | 41 - .../dataset/kernels/image/rgba_to_bgr_op.cc | 28 - .../dataset/kernels/image/rgba_to_bgr_op.h | 42 - .../dataset/kernels/image/rgba_to_rgb_op.cc | 28 - .../dataset/kernels/image/rgba_to_rgb_op.h | 42 - .../dataset/kernels/image/rotate_op.cc | 153 - .../dataset/kernels/image/rotate_op.h | 74 - .../dataset/kernels/image/sharpness_op.cc | 32 - .../dataset/kernels/image/sharpness_op.h | 52 - .../dataset/kernels/image/slice_patches_op.cc | 52 - .../dataset/kernels/image/slice_patches_op.h | 59 - .../dataset/kernels/image/solarize_op.cc | 29 - .../dataset/kernels/image/solarize_op.h | 46 - .../dataset/kernels/image/swap_red_blue_op.cc | 28 - .../dataset/kernels/image/swap_red_blue_op.h | 49 - .../dataset/kernels/image/to_tensor_op.cc | 38 - .../dataset/kernels/image/to_tensor_op.h | 45 - .../kernels/image/trivial_augment_wide_op.cc | 90 - .../kernels/image/trivial_augment_wide_op.h | 64 - .../dataset/kernels/image/uniform_aug_op.cc | 62 - .../dataset/kernels/image/uniform_aug_op.h | 54 - .../dataset/kernels/image/vertical_flip_op.cc | 56 - .../dataset/kernels/image/vertical_flip_op.h | 40 - .../dataset/kernels/image/video_utils.cc | 1463 ---- .../dataset/kernels/image/video_utils.h | 60 - .../dataset/kernels/ir/data/transforms_ir.cc | 533 -- .../dataset/kernels/ir/data/transforms_ir.h | 372 - .../dataset/kernels/ir/tensor_operation.h | 61 - .../dataset/kernels/ir/transforms_ir.cc | 533 -- .../minddata/dataset/kernels/ir/validators.cc | 227 - .../minddata/dataset/kernels/ir/validators.h | 131 - .../kernels/ir/vision/adjust_brightness_ir.cc | 92 - .../kernels/ir/vision/adjust_brightness_ir.h | 59 - .../kernels/ir/vision/adjust_contrast_ir.cc | 92 - .../kernels/ir/vision/adjust_contrast_ir.h | 59 - .../kernels/ir/vision/adjust_gamma_ir.cc | 63 - .../kernels/ir/vision/adjust_gamma_ir.h | 59 - .../kernels/ir/vision/adjust_hue_ir.cc | 93 - .../dataset/kernels/ir/vision/adjust_hue_ir.h | 59 - .../kernels/ir/vision/adjust_saturation_ir.cc | 92 - .../kernels/ir/vision/adjust_saturation_ir.h | 59 - .../kernels/ir/vision/adjust_sharpness_ir.cc | 92 - .../kernels/ir/vision/adjust_sharpness_ir.h | 59 - .../dataset/kernels/ir/vision/affine_ir.cc | 158 - .../dataset/kernels/ir/vision/affine_ir.h | 67 - .../kernels/ir/vision/ascend_vision_ir.cc | 492 -- .../kernels/ir/vision/ascend_vision_ir.h | 201 - .../kernels/ir/vision/auto_augment_ir.cc | 81 - .../kernels/ir/vision/auto_augment_ir.h | 63 - .../kernels/ir/vision/auto_contrast_ir.cc | 117 - .../kernels/ir/vision/auto_contrast_ir.h | 61 - .../ir/vision/bounding_box_augment_ir.cc | 77 - .../ir/vision/bounding_box_augment_ir.h | 58 - .../kernels/ir/vision/center_crop_ir.cc | 64 - .../kernels/ir/vision/center_crop_ir.h | 59 - .../kernels/ir/vision/convert_color_ir.cc | 100 - .../kernels/ir/vision/convert_color_ir.h | 61 - .../dataset/kernels/ir/vision/crop_ir.cc | 115 - .../dataset/kernels/ir/vision/crop_ir.h | 62 - .../kernels/ir/vision/cutmix_batch_ir.cc | 75 - .../kernels/ir/vision/cutmix_batch_ir.h | 58 - .../dataset/kernels/ir/vision/cutout_ir.cc | 76 - .../dataset/kernels/ir/vision/cutout_ir.h | 58 - .../dataset/kernels/ir/vision/decode_ir.cc | 87 - .../dataset/kernels/ir/vision/decode_ir.h | 59 - .../kernels/ir/vision/decode_video_ir.cc | 44 - .../kernels/ir/vision/decode_video_ir.h | 53 - .../dataset/kernels/ir/vision/equalize_ir.cc | 89 - .../dataset/kernels/ir/vision/equalize_ir.h | 58 - .../dataset/kernels/ir/vision/erase_ir.cc | 137 - .../dataset/kernels/ir/vision/erase_ir.h | 68 - .../kernels/ir/vision/gaussian_blur_ir.cc | 116 - .../kernels/ir/vision/gaussian_blur_ir.h | 60 - .../kernels/ir/vision/horizontal_flip_ir.cc | 91 - .../kernels/ir/vision/horizontal_flip_ir.h | 57 - .../kernels/ir/vision/hwc_to_chw_ir.cc | 41 - .../dataset/kernels/ir/vision/hwc_to_chw_ir.h | 51 - .../dataset/kernels/ir/vision/invert_ir.cc | 88 - .../dataset/kernels/ir/vision/invert_ir.h | 58 - .../kernels/ir/vision/mixup_batch_ir.cc | 58 - .../kernels/ir/vision/mixup_batch_ir.h | 56 - .../dataset/kernels/ir/vision/normalize_ir.cc | 97 - .../dataset/kernels/ir/vision/normalize_ir.h | 63 - .../kernels/ir/vision/normalize_pad_ir.cc | 80 - .../kernels/ir/vision/normalize_pad_ir.h | 61 - .../dataset/kernels/ir/vision/pad_ir.cc | 156 - .../dataset/kernels/ir/vision/pad_ir.h | 63 - .../kernels/ir/vision/pad_to_size_ir.cc | 91 - .../kernels/ir/vision/pad_to_size_ir.h | 58 - .../kernels/ir/vision/perspective_ir.cc | 128 - .../kernels/ir/vision/perspective_ir.h | 65 - .../dataset/kernels/ir/vision/posterize_ir.cc | 99 - .../dataset/kernels/ir/vision/posterize_ir.h | 59 - .../kernels/ir/vision/rand_augment_ir.cc | 97 - .../kernels/ir/vision/rand_augment_ir.h | 65 - .../ir/vision/random_adjust_sharpness_ir.cc | 69 - .../ir/vision/random_adjust_sharpness_ir.h | 59 - .../kernels/ir/vision/random_affine_ir.cc | 179 - .../kernels/ir/vision/random_affine_ir.h | 64 - .../ir/vision/random_auto_contrast_ir.cc | 77 - .../ir/vision/random_auto_contrast_ir.h | 60 - .../ir/vision/random_color_adjust_ir.cc | 123 - .../ir/vision/random_color_adjust_ir.h | 61 - .../kernels/ir/vision/random_color_ir.cc | 77 - .../kernels/ir/vision/random_color_ir.h | 57 - .../ir/vision/random_crop_decode_resize_ir.cc | 101 - .../ir/vision/random_crop_decode_resize_ir.h | 57 - .../kernels/ir/vision/random_crop_ir.cc | 147 - .../kernels/ir/vision/random_crop_ir.h | 62 - .../ir/vision/random_crop_with_bbox_ir.cc | 149 - .../ir/vision/random_crop_with_bbox_ir.h | 62 - .../kernels/ir/vision/random_equalize_ir.cc | 62 - .../kernels/ir/vision/random_equalize_ir.h | 58 - .../ir/vision/random_horizontal_flip_ir.cc | 61 - .../ir/vision/random_horizontal_flip_ir.h | 56 - .../random_horizontal_flip_with_bbox_ir.cc | 64 - .../random_horizontal_flip_with_bbox_ir.h | 56 - .../kernels/ir/vision/random_invert_ir.cc | 61 - .../kernels/ir/vision/random_invert_ir.h | 58 - .../kernels/ir/vision/random_lighting_ir.cc | 63 - .../kernels/ir/vision/random_lighting_ir.h | 57 - .../kernels/ir/vision/random_posterize_ir.cc | 90 - .../kernels/ir/vision/random_posterize_ir.h | 57 - .../kernels/ir/vision/random_resize_ir.cc | 77 - .../kernels/ir/vision/random_resize_ir.h | 57 - .../ir/vision/random_resize_with_bbox_ir.cc | 79 - .../ir/vision/random_resize_with_bbox_ir.h | 57 - .../ir/vision/random_resized_crop_ir.cc | 117 - .../ir/vision/random_resized_crop_ir.h | 68 - .../random_resized_crop_with_bbox_ir.cc | 115 - .../vision/random_resized_crop_with_bbox_ir.h | 64 - .../kernels/ir/vision/random_rotation_ir.cc | 145 - .../kernels/ir/vision/random_rotation_ir.h | 62 - .../ir/vision/random_select_subpolicy_ir.cc | 132 - .../ir/vision/random_select_subpolicy_ir.h | 59 - .../kernels/ir/vision/random_sharpness_ir.cc | 79 - .../kernels/ir/vision/random_sharpness_ir.h | 57 - .../kernels/ir/vision/random_solarize_ir.cc | 84 - .../kernels/ir/vision/random_solarize_ir.h | 57 - .../ir/vision/random_vertical_flip_ir.cc | 61 - .../ir/vision/random_vertical_flip_ir.h | 56 - .../random_vertical_flip_with_bbox_ir.cc | 64 - .../random_vertical_flip_with_bbox_ir.h | 56 - .../dataset/kernels/ir/vision/rescale_ir.cc | 68 - .../dataset/kernels/ir/vision/rescale_ir.h | 57 - .../dataset/kernels/ir/vision/resize_ir.cc | 116 - .../dataset/kernels/ir/vision/resize_ir.h | 62 - .../ir/vision/resize_preserve_ar_ir.cc | 69 - .../kernels/ir/vision/resize_preserve_ar_ir.h | 58 - .../kernels/ir/vision/resize_with_bbox_ir.cc | 88 - .../kernels/ir/vision/resize_with_bbox_ir.h | 58 - .../kernels/ir/vision/resized_crop_ir.cc | 136 - .../kernels/ir/vision/resized_crop_ir.h | 66 - .../kernels/ir/vision/rgb_to_bgr_ir.cc | 41 - .../dataset/kernels/ir/vision/rgb_to_bgr_ir.h | 53 - .../kernels/ir/vision/rgb_to_gray_ir.cc | 41 - .../kernels/ir/vision/rgb_to_gray_ir.h | 51 - .../kernels/ir/vision/rgba_to_bgr_ir.cc | 48 - .../kernels/ir/vision/rgba_to_bgr_ir.h | 51 - .../kernels/ir/vision/rgba_to_rgb_ir.cc | 48 - .../kernels/ir/vision/rgba_to_rgb_ir.h | 51 - .../dataset/kernels/ir/vision/rotate_ir.cc | 189 - .../dataset/kernels/ir/vision/rotate_ir.h | 71 - .../kernels/ir/vision/slice_patches_ir.cc | 78 - .../kernels/ir/vision/slice_patches_ir.h | 60 - .../dataset/kernels/ir/vision/solarize_ir.cc | 106 - .../dataset/kernels/ir/vision/solarize_ir.h | 60 - .../kernels/ir/vision/swap_red_blue_ir.cc | 48 - .../kernels/ir/vision/swap_red_blue_ir.h | 51 - .../dataset/kernels/ir/vision/to_tensor_ir.cc | 61 - .../dataset/kernels/ir/vision/to_tensor_ir.h | 57 - .../ir/vision/trivial_augment_wide_ir.cc | 78 - .../ir/vision/trivial_augment_wide_ir.h | 61 - .../kernels/ir/vision/uniform_aug_ir.cc | 93 - .../kernels/ir/vision/uniform_aug_ir.h | 58 - .../kernels/ir/vision/vertical_flip_ir.cc | 91 - .../kernels/ir/vision/vertical_flip_ir.h | 57 - .../minddata/dataset/kernels/tensor_op.cc | 83 - .../minddata/dataset/kernels/tensor_op.h | 367 - .../dataset/liteapi/include/datasets.h | 712 -- .../minddata/dataset/util/allocator.h | 204 - mindspore-lite/minddata/dataset/util/arena.cc | 293 - mindspore-lite/minddata/dataset/util/arena.h | 156 - .../minddata/dataset/util/auto_index.h | 100 - mindspore-lite/minddata/dataset/util/bit.h | 75 - mindspore-lite/minddata/dataset/util/btree.h | 529 -- .../minddata/dataset/util/btree_impl.tpp | 533 -- .../minddata/dataset/util/btree_iterator.tpp | 364 - mindspore-lite/minddata/dataset/util/buddy.cc | 415 -- mindspore-lite/minddata/dataset/util/buddy.h | 127 - .../minddata/dataset/util/circular_pool.cc | 227 - .../minddata/dataset/util/circular_pool.h | 109 - .../minddata/dataset/util/cond_var.cc | 124 - .../minddata/dataset/util/cond_var.h | 66 - .../minddata/dataset/util/ftok_key.cc | 83 - .../minddata/dataset/util/ftok_key.h | 58 - .../minddata/dataset/util/gil_scoped.h | 60 - .../minddata/dataset/util/intrp_resource.h | 52 - .../minddata/dataset/util/intrp_service.cc | 99 - .../minddata/dataset/util/intrp_service.h | 61 - .../minddata/dataset/util/json_helper.cc | 161 - .../minddata/dataset/util/json_helper.h | 259 - mindspore-lite/minddata/dataset/util/list.h | 216 - mindspore-lite/minddata/dataset/util/lock.cc | 170 - mindspore-lite/minddata/dataset/util/lock.h | 173 - .../minddata/dataset/util/log_adapter.h | 27 - .../minddata/dataset/util/md_log_adapter.cc | 84 - .../minddata/dataset/util/md_log_adapter.h | 41 - .../minddata/dataset/util/memory_pool.cc | 61 - .../minddata/dataset/util/memory_pool.h | 59 - .../minddata/dataset/util/monitor.cc | 61 - .../minddata/dataset/util/monitor.h | 38 - mindspore-lite/minddata/dataset/util/path.cc | 413 -- mindspore-lite/minddata/dataset/util/path.h | 131 - mindspore-lite/minddata/dataset/util/queue.h | 319 - .../minddata/dataset/util/queue_map.h | 165 - mindspore-lite/minddata/dataset/util/random.h | 78 - mindspore-lite/minddata/dataset/util/rdr.cc | 170 - mindspore-lite/minddata/dataset/util/rdr.h | 64 - .../minddata/dataset/util/semaphore.cc | 38 - .../minddata/dataset/util/semaphore.h | 54 - .../minddata/dataset/util/service.cc | 82 - .../minddata/dataset/util/service.h | 53 - .../minddata/dataset/util/services.cc | 122 - .../minddata/dataset/util/services.h | 119 - .../minddata/dataset/util/shared_mem.cc | 120 - .../minddata/dataset/util/shared_mem.h | 52 - .../minddata/dataset/util/sig_handler.cc | 225 - .../minddata/dataset/util/sig_handler.h | 38 - mindspore-lite/minddata/dataset/util/slice.cc | 38 - mindspore-lite/minddata/dataset/util/slice.h | 133 - .../minddata/dataset/util/status.cc | 90 - mindspore-lite/minddata/dataset/util/status.h | 141 - .../minddata/dataset/util/system_pool.h | 80 - mindspore-lite/minddata/dataset/util/task.cc | 273 - mindspore-lite/minddata/dataset/util/task.h | 154 - .../minddata/dataset/util/task_manager.cc | 403 - .../minddata/dataset/util/task_manager.h | 199 - mindspore-lite/minddata/dataset/util/treap.h | 407 - .../minddata/dataset/util/validators.cc | 22 - .../minddata/dataset/util/validators.h | 227 - .../minddata/dataset/util/wait_post.cc | 45 - .../minddata/dataset/util/wait_post.h | 53 - .../minddata/example/CMakeLists.txt | 43 - mindspore-lite/minddata/example/testlenet.cpp | 58 - .../minddata/example/testlitecv.cpp | 83 - .../minddata/example/testresize.cpp | 66 - mindspore-lite/minddata/wrapper/MDToDApi.cc | 471 -- mindspore-lite/minddata/wrapper/MDToDApi.h | 76 - .../minddata/wrapper/album_op_android.cc | 518 -- .../minddata/wrapper/album_op_android.h | 193 - .../minddata/wrapper/jni-example.cc | 86 - .../wrapper/testCifar10Data/data_batch_1.bin | Bin 30730000 -> 0 bytes .../minddata/wrapper/x86-example.cc | 51 - .../providers/siteai/CMakeLists.txt | 3 - mindspore-lite/src/CMakeLists.txt | 37 +- mindspore-lite/test/CMakeLists.txt | 4 - mindspore-lite/test/runtest.sh | 5 - .../test/st/scripts/base_functions.sh | 5 - .../test/st/scripts/run_net_train.sh | 7 - .../test/ut/src/dataset/eager_test.cc | 78 - .../converter/adapter/acl/CMakeLists.txt | 1 - .../acl/cxx_api_lite/cxx_api/serialization.cc | 1 - .../tools/dataset/cropper/.gitignore | 1 - .../tools/dataset/cropper/CMakeLists.txt | 33 - .../tools/dataset/cropper/README.md | 71 - .../tools/dataset/cropper/associations.txt | 1 - .../tools/dataset/cropper/build_lib.py | 160 - mindspore-lite/tools/dataset/cropper/crop.sh | 201 - .../dataset/cropper/cropper_configure.py | 396 - .../tools/dataset/cropper/dependencies.txt | 1 - .../tools/dataset/cropper/parser.py | 85 - 1037 files changed, 9 insertions(+), 173226 deletions(-) delete mode 100644 mindspore-lite/minddata/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/api/data_helper.cc delete mode 100644 mindspore-lite/minddata/dataset/api/datasets.cc delete mode 100644 mindspore-lite/minddata/dataset/api/execute.cc delete mode 100644 mindspore-lite/minddata/dataset/api/iterator.cc delete mode 100644 mindspore-lite/minddata/dataset/api/python/python_mp.h delete mode 100644 mindspore-lite/minddata/dataset/api/samplers.cc delete mode 100644 mindspore-lite/minddata/dataset/api/transforms.cc delete mode 100644 mindspore-lite/minddata/dataset/api/vision.cc delete mode 100644 mindspore-lite/minddata/dataset/callback/callback_manager.cc delete mode 100644 mindspore-lite/minddata/dataset/callback/callback_manager.h delete mode 100644 mindspore-lite/minddata/dataset/callback/callback_param.h delete mode 100644 mindspore-lite/minddata/dataset/callback/ds_callback.h delete mode 100644 mindspore-lite/minddata/dataset/callback/py_ds_callback.cc delete mode 100644 mindspore-lite/minddata/dataset/callback/py_ds_callback.h delete mode 100644 mindspore-lite/minddata/dataset/core/ascend_resource.cc delete mode 100644 mindspore-lite/minddata/dataset/core/ascend_resource.h delete mode 100644 mindspore-lite/minddata/dataset/core/client.cc delete mode 100644 mindspore-lite/minddata/dataset/core/client.h delete mode 100644 mindspore-lite/minddata/dataset/core/config_manager.cc delete mode 100644 mindspore-lite/minddata/dataset/core/config_manager.h delete mode 100644 mindspore-lite/minddata/dataset/core/cv_tensor.cc delete mode 100644 mindspore-lite/minddata/dataset/core/cv_tensor.h delete mode 100644 mindspore-lite/minddata/dataset/core/data_type.cc delete mode 100644 mindspore-lite/minddata/dataset/core/data_type.h delete mode 100644 mindspore-lite/minddata/dataset/core/de_tensor.cc delete mode 100644 mindspore-lite/minddata/dataset/core/de_tensor.h delete mode 100644 mindspore-lite/minddata/dataset/core/device_resource.cc delete mode 100644 mindspore-lite/minddata/dataset/core/device_resource.h delete mode 100644 mindspore-lite/minddata/dataset/core/device_tensor.cc delete mode 100644 mindspore-lite/minddata/dataset/core/device_tensor.h delete mode 100644 mindspore-lite/minddata/dataset/core/example.proto delete mode 100644 mindspore-lite/minddata/dataset/core/feature.proto delete mode 100644 mindspore-lite/minddata/dataset/core/global_context.cc delete mode 100644 mindspore-lite/minddata/dataset/core/global_context.h delete mode 100644 mindspore-lite/minddata/dataset/core/pybind_support.h delete mode 100644 mindspore-lite/minddata/dataset/core/tensor.cc delete mode 100644 mindspore-lite/minddata/dataset/core/tensor.h delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_helpers.cc delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_helpers.h delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_row.cc delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_row.h delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_shape.cc delete mode 100644 mindspore-lite/minddata/dataset/core/tensor_shape.h delete mode 100644 mindspore-lite/minddata/dataset/core/type_id.h delete mode 100644 mindspore-lite/minddata/dataset/core/types.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/connector.h delete mode 100644 mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h delete mode 100644 mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h delete mode 100644 mindspore-lite/minddata/dataset/engine/data_schema.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/data_schema.h delete mode 100644 mindspore-lite/minddata/dataset/engine/dataset_iterator.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/dataset_iterator.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/batch_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/concat_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/filter_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/project_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/project_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/rename_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/skip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h delete mode 100755 mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.cc delete mode 100755 mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h delete mode 100755 mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.cc delete mode 100755 mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/take_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/take_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/zip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h delete mode 100644 mindspore-lite/minddata/dataset/engine/execution_tree.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/execution_tree.h delete mode 100644 mindspore-lite/minddata/dataset/engine/gpu_item_connector.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h delete mode 100755 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.cc delete mode 100755 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h delete mode 100755 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.cc delete mode 100755 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h delete mode 100644 mindspore-lite/minddata/dataset/engine/jagged_connector.h delete mode 100644 mindspore-lite/minddata/dataset/engine/operator_connector.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/auto_tune.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/auto_tune.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/connector_size.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/connector_size.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/cyclic_array.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/info_collector.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/info_collector.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/monitor.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/monitor.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/perf_data.h delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/profiling.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/perf/profiling.h delete mode 100644 mindspore-lite/minddata/dataset/engine/python_runtime_context.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/python_runtime_context.h delete mode 100644 mindspore-lite/minddata/dataset/engine/runtime_context.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/runtime_context.h delete mode 100644 mindspore-lite/minddata/dataset/engine/serdes.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/serdes.h delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_adapter.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_adapter.h delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_adapter_lite.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_modifier.cc delete mode 100644 mindspore-lite/minddata/dataset/engine/tree_modifier.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/audio.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/config.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/constants.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/data_helper.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/datasets.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/execute.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/iterator.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/samplers.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/text.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/transforms.h delete mode 100755 mindspore-lite/minddata/dataset/include/dataset/vision.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h delete mode 100644 mindspore-lite/minddata/dataset/include/dataset/vision_lite.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/c_func_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/c_func_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/compose_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/compose_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/concatenate_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/data_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/data_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/duplicate_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/fill_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/fill_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/mask_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/mask_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/no_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/one_hot_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/pad_end_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/parse_example_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/random_apply_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/random_choice_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/slice_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/slice_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/to_float16_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/to_float16_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/type_cast_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/unique_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/data/unique_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/affine_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/affine_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/bounding_box.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/bounding_box.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/center_crop_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/convert_color_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/crop_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/crop_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/cut_out_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/decode_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/decode_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/decode_video_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.cpp delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ThreadSafeQueue.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/equalize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/equalize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/erase_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/erase_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/exif_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/exif_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/image_utils.cc delete mode 100755 mindspore-lite/minddata/dataset/kernels/image/image_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/invert_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/invert_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/canny.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_cv/warp_affine.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/math_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/math_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/normalize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/normalize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/pad_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/pad_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/perspective_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/perspective_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/posterize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/posterize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_affine_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_color_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_color_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_invert_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_resize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rescale_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rescale_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_bilinear_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rotate_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/rotate_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/sharpness_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/solarize_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/solarize_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/video_utils.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/image/video_utils.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/transforms_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/validators.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/validators.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h delete mode 100644 mindspore-lite/minddata/dataset/kernels/tensor_op.cc delete mode 100644 mindspore-lite/minddata/dataset/kernels/tensor_op.h delete mode 100644 mindspore-lite/minddata/dataset/liteapi/include/datasets.h delete mode 100644 mindspore-lite/minddata/dataset/util/allocator.h delete mode 100644 mindspore-lite/minddata/dataset/util/arena.cc delete mode 100644 mindspore-lite/minddata/dataset/util/arena.h delete mode 100644 mindspore-lite/minddata/dataset/util/auto_index.h delete mode 100644 mindspore-lite/minddata/dataset/util/bit.h delete mode 100644 mindspore-lite/minddata/dataset/util/btree.h delete mode 100644 mindspore-lite/minddata/dataset/util/btree_impl.tpp delete mode 100644 mindspore-lite/minddata/dataset/util/btree_iterator.tpp delete mode 100644 mindspore-lite/minddata/dataset/util/buddy.cc delete mode 100644 mindspore-lite/minddata/dataset/util/buddy.h delete mode 100644 mindspore-lite/minddata/dataset/util/circular_pool.cc delete mode 100644 mindspore-lite/minddata/dataset/util/circular_pool.h delete mode 100644 mindspore-lite/minddata/dataset/util/cond_var.cc delete mode 100644 mindspore-lite/minddata/dataset/util/cond_var.h delete mode 100644 mindspore-lite/minddata/dataset/util/ftok_key.cc delete mode 100644 mindspore-lite/minddata/dataset/util/ftok_key.h delete mode 100644 mindspore-lite/minddata/dataset/util/gil_scoped.h delete mode 100644 mindspore-lite/minddata/dataset/util/intrp_resource.h delete mode 100644 mindspore-lite/minddata/dataset/util/intrp_service.cc delete mode 100644 mindspore-lite/minddata/dataset/util/intrp_service.h delete mode 100644 mindspore-lite/minddata/dataset/util/json_helper.cc delete mode 100644 mindspore-lite/minddata/dataset/util/json_helper.h delete mode 100644 mindspore-lite/minddata/dataset/util/list.h delete mode 100644 mindspore-lite/minddata/dataset/util/lock.cc delete mode 100644 mindspore-lite/minddata/dataset/util/lock.h delete mode 100644 mindspore-lite/minddata/dataset/util/log_adapter.h delete mode 100644 mindspore-lite/minddata/dataset/util/md_log_adapter.cc delete mode 100644 mindspore-lite/minddata/dataset/util/md_log_adapter.h delete mode 100644 mindspore-lite/minddata/dataset/util/memory_pool.cc delete mode 100644 mindspore-lite/minddata/dataset/util/memory_pool.h delete mode 100644 mindspore-lite/minddata/dataset/util/monitor.cc delete mode 100644 mindspore-lite/minddata/dataset/util/monitor.h delete mode 100644 mindspore-lite/minddata/dataset/util/path.cc delete mode 100644 mindspore-lite/minddata/dataset/util/path.h delete mode 100644 mindspore-lite/minddata/dataset/util/queue.h delete mode 100644 mindspore-lite/minddata/dataset/util/queue_map.h delete mode 100644 mindspore-lite/minddata/dataset/util/random.h delete mode 100644 mindspore-lite/minddata/dataset/util/rdr.cc delete mode 100644 mindspore-lite/minddata/dataset/util/rdr.h delete mode 100644 mindspore-lite/minddata/dataset/util/semaphore.cc delete mode 100644 mindspore-lite/minddata/dataset/util/semaphore.h delete mode 100644 mindspore-lite/minddata/dataset/util/service.cc delete mode 100644 mindspore-lite/minddata/dataset/util/service.h delete mode 100644 mindspore-lite/minddata/dataset/util/services.cc delete mode 100644 mindspore-lite/minddata/dataset/util/services.h delete mode 100644 mindspore-lite/minddata/dataset/util/shared_mem.cc delete mode 100644 mindspore-lite/minddata/dataset/util/shared_mem.h delete mode 100644 mindspore-lite/minddata/dataset/util/sig_handler.cc delete mode 100644 mindspore-lite/minddata/dataset/util/sig_handler.h delete mode 100644 mindspore-lite/minddata/dataset/util/slice.cc delete mode 100644 mindspore-lite/minddata/dataset/util/slice.h delete mode 100644 mindspore-lite/minddata/dataset/util/status.cc delete mode 100644 mindspore-lite/minddata/dataset/util/status.h delete mode 100644 mindspore-lite/minddata/dataset/util/system_pool.h delete mode 100644 mindspore-lite/minddata/dataset/util/task.cc delete mode 100644 mindspore-lite/minddata/dataset/util/task.h delete mode 100644 mindspore-lite/minddata/dataset/util/task_manager.cc delete mode 100644 mindspore-lite/minddata/dataset/util/task_manager.h delete mode 100644 mindspore-lite/minddata/dataset/util/treap.h delete mode 100644 mindspore-lite/minddata/dataset/util/validators.cc delete mode 100644 mindspore-lite/minddata/dataset/util/validators.h delete mode 100644 mindspore-lite/minddata/dataset/util/wait_post.cc delete mode 100644 mindspore-lite/minddata/dataset/util/wait_post.h delete mode 100644 mindspore-lite/minddata/example/CMakeLists.txt delete mode 100644 mindspore-lite/minddata/example/testlenet.cpp delete mode 100644 mindspore-lite/minddata/example/testlitecv.cpp delete mode 100644 mindspore-lite/minddata/example/testresize.cpp delete mode 100644 mindspore-lite/minddata/wrapper/MDToDApi.cc delete mode 100644 mindspore-lite/minddata/wrapper/MDToDApi.h delete mode 100644 mindspore-lite/minddata/wrapper/album_op_android.cc delete mode 100644 mindspore-lite/minddata/wrapper/album_op_android.h delete mode 100644 mindspore-lite/minddata/wrapper/jni-example.cc delete mode 100644 mindspore-lite/minddata/wrapper/testCifar10Data/data_batch_1.bin delete mode 100644 mindspore-lite/minddata/wrapper/x86-example.cc delete mode 100644 mindspore-lite/test/ut/src/dataset/eager_test.cc delete mode 100644 mindspore-lite/tools/dataset/cropper/.gitignore delete mode 100644 mindspore-lite/tools/dataset/cropper/CMakeLists.txt delete mode 100644 mindspore-lite/tools/dataset/cropper/README.md delete mode 100644 mindspore-lite/tools/dataset/cropper/associations.txt delete mode 100644 mindspore-lite/tools/dataset/cropper/build_lib.py delete mode 100755 mindspore-lite/tools/dataset/cropper/crop.sh delete mode 100644 mindspore-lite/tools/dataset/cropper/cropper_configure.py delete mode 100644 mindspore-lite/tools/dataset/cropper/dependencies.txt delete mode 100644 mindspore-lite/tools/dataset/cropper/parser.py diff --git a/.jenkins/check/config/filter_cppcheck.txt b/.jenkins/check/config/filter_cppcheck.txt index 9e203905e..5141d1d16 100644 --- a/.jenkins/check/config/filter_cppcheck.txt +++ b/.jenkins/check/config/filter_cppcheck.txt @@ -22,15 +22,6 @@ "mindspore-lite/mindspore-lite/test/" "syntaxError" "mindspore-lite/mindspore-lite/test/ut/tools/converter/registry/pass_registry_test.cc" "unknownMacro" -# MindData -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc" "nullPointerRedundantCheck" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc" "unsignedLessThanZero" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc" "constParameter" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc" "constParameter" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc" "useStlAlgorithm" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.cc" "unreadVariable" -"mindspore-lite/mindspore-lite/minddata/dataset/util/arena.cc" "useStlAlgorithm" - # other "mindspore-lite/mindspore-lite/examples/quick_start_micro/" "syntaxError" "mindspore-lite/mindspore-lite/python/src/pybind_module.cc" "syntaxError" diff --git a/.jenkins/check/config/filter_cpplint.txt b/.jenkins/check/config/filter_cpplint.txt index 501b7f4d3..868a4167e 100644 --- a/.jenkins/check/config/filter_cpplint.txt +++ b/.jenkins/check/config/filter_cpplint.txt @@ -34,29 +34,6 @@ "mindspore-lite/mindspore-lite/src/extendrt/delegate/ascend_acl/model_infer.cc" "whitespace/indent_namespace" "mindspore-lite/mindspore-lite/src/litert/delegate/coreml/coreml_executor.h" "whitespace/comments" -# MindData -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.cc" "build/include_what_you_use" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.cc" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc" "runtime/string" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc" "runtime/threadsafe_fn" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.cc" "build/storage_class" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" "runtime/string" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.cc" "build/namespaces" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/image_utils.cc" "runtime/int" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.cc" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h" "runtime/explicit" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.cc" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/kernels/tensor_op.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/util/bit.h" "runtime/references" -"mindspore-lite/mindspore-lite/minddata/dataset/util/btree.h" "build/include" - # other "mindspore-lite/mindspore-lite/examples/quick_start_c/main.c" "readability/casting" "mindspore-lite/mindspore-lite/examples/quick_start_c/main.c" "runtime/threadsafe_fn" diff --git a/.jenkins/check/config/whitelizard.txt b/.jenkins/check/config/whitelizard.txt index 7a2f817a0..26137c106 100644 --- a/.jenkins/check/config/whitelizard.txt +++ b/.jenkins/check/config/whitelizard.txt @@ -30,11 +30,6 @@ mindspore-lite/mindspore-lite/src/train/train_loop.cc:mindspore::lite::TrainLoop mindspore-lite/mindspore-lite/src/common/utils.h:mindspore::lite::DataTypeSize mindspore-lite/mindspore-lite/src/litert/scheduler.cc:mindspore::lite::Scheduler::FindProviderKernel -# minddata -mindspore-lite/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.cc:mindspore::dataset::DataQueueOp::SendDataToAscend -mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc:mindspore::dataset::DvppConvertColor, mindspore::dataset::DvppErase, mindspore::dataset::DvppNormalize, mindspore::dataset::DvppPerspective, mindspore::dataset::DvppResizedCrop, mindspore::dataset::DvppRotate, mindspore::dataset::CreateAclTensor -mindspore-lite/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.cc:DvppVideo::SaveYuvFile - # other mindspore-lite/mindspore-lite/providers/nnie_proposal/src/proposal.cc:mindspore::proposal::Rpn mindspore-lite/mindspore-lite/providers/nnie/src/custom_infer.cc:mindspore::nnie::CustomInterface::Infer diff --git a/build.bat b/build.bat index 1405ccd75..41dd5bb9b 100644 --- a/build.bat +++ b/build.bat @@ -70,10 +70,10 @@ echo "======Start building MindSpore Lite %VERSION_STR%======" rd /s /q "%BASE_PATH%\output" (git log -1 | findstr "^commit") > %BUILD_PATH%\.commit_id IF defined VisualStudioVersion ( - cmake -DMSLITE_MINDDATA_IMPLEMENT=off -DMSLITE_ENABLE_TRAIN=off -DVERSION_STR=%VERSION_STR% ^ + cmake -DMSLITE_ENABLE_TRAIN=off -DVERSION_STR=%VERSION_STR% ^ -DCMAKE_BUILD_TYPE=Release -G "Ninja" "%BASE_PATH%/mindspore-lite" ) ELSE ( - cmake -DMSLITE_MINDDATA_IMPLEMENT=off -DMSLITE_ENABLE_TRAIN=off -DVERSION_STR=%VERSION_STR% ^ + cmake -DMSLITE_ENABLE_TRAIN=off -DVERSION_STR=%VERSION_STR% ^ -DCMAKE_BUILD_TYPE=Release -G "CodeBlocks - MinGW Makefiles" "%BASE_PATH%/mindspore-lite" ) diff --git a/build.sh b/build.sh index 80bbf0f1f..65e28707c 100755 --- a/build.sh +++ b/build.sh @@ -53,7 +53,6 @@ echo "---------------- MindSpore-Lite: build start ----------------" init_default_options process_options "$@" -export COMPILE_MINDDATA_LITE export ENABLE_VERBOSE export LITE_PLATFORM export LITE_ENABLE_AAR diff --git a/cmake/package_lite.cmake b/cmake/package_lite.cmake index a7c5e91fb..cef65ccec 100644 --- a/cmake/package_lite.cmake +++ b/cmake/package_lite.cmake @@ -199,134 +199,6 @@ function(__install_white_list_ops) ) endfunction() -# full mode will also package the files of lite_cv mode. -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" - AND NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - # full header files - install(FILES - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/constants.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/data_helper.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/execute.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/iterator.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/samplers.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/transforms.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/vision_lite.h - ${TOP_DIR}/mindspore-lite/minddata/dataset/liteapi/include/datasets.h - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - - if(PLATFORM_ARM64) - if((MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) AND MSLITE_ENABLE_ACL) - install(FILES ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${BUILD_DIR}/minddata/kernels-dvpp-image/utils/libdvpp_utils.so - DESTINATION ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.a DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${JPEGTURBO_LIB_LIST} DESTINATION ${TURBO_DIR}/lib COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${securec_LIBPATH}/libsecurec.a DESTINATION ${SECUREC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - elseif(PLATFORM_ARM32) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.a DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${JPEGTURBO_LIB_LIST} DESTINATION ${TURBO_DIR}/lib COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${securec_LIBPATH}/libsecurec.a DESTINATION ${SECUREC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - else() - if((MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) AND MSLITE_ENABLE_ACL) - install(FILES ${TOP_DIR}/mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${BUILD_DIR}/minddata/kernels-dvpp-image/utils/libdvpp_utils.so - DESTINATION ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.a DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${jpeg_turbo_LIBPATH}/libjpeg.so.62.4.0 DESTINATION ${TURBO_DIR}/lib - RENAME libjpeg.so.62 COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${jpeg_turbo_LIBPATH}/libturbojpeg.so.0.3.0 DESTINATION ${TURBO_DIR}/lib - RENAME libturbojpeg.so.0 COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${securec_LIBPATH}/libsecurec.a DESTINATION ${SECUREC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() - - # lite_cv header files - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/kernels/image/lite_cv - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h") -endif() - -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "wrapper" - AND NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/include/ DESTINATION ${MIND_DATA_INC_DIR} - COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h" PATTERN "vision.h" EXCLUDE) - if(PLATFORM_ARM64) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${JPEGTURBO_LIB_LIST} DESTINATION ${TURBO_DIR}/lib COMPONENT ${RUNTIME_COMPONENT_NAME}) - elseif(PLATFORM_ARM32) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${JPEGTURBO_LIB_LIST} DESTINATION ${TURBO_DIR}/lib COMPONENT ${RUNTIME_COMPONENT_NAME}) - else() - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${jpeg_turbo_LIBPATH}/libjpeg.so.62.4.0 DESTINATION ${TURBO_DIR}/lib RENAME libjpeg.so.62 - COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${jpeg_turbo_LIBPATH}/libturbojpeg.so.0.3.0 DESTINATION ${TURBO_DIR}/lib RENAME libturbojpeg.so.0 - COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() -endif() - -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite" - AND NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/include/ DESTINATION ${MIND_DATA_INC_DIR} - COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h") - if(PLATFORM_ARM64) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so DESTINATION ${TURBO_DIR}/lib - COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so DESTINATION ${TURBO_DIR}/lib - COMPONENT ${RUNTIME_COMPONENT_NAME}) - elseif(PLATFORM_ARM32) - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so DESTINATION ${TURBO_DIR}/lib - COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so DESTINATION ${TURBO_DIR}/lib - COMPONENT ${RUNTIME_COMPONENT_NAME}) - else() - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so.62.4.0 - DESTINATION ${TURBO_DIR}/lib RENAME libjpeg.so.62 COMPONENT ${RUNTIME_COMPONENT_NAME}) - install(FILES ${TOP_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so.0.3.0 - DESTINATION ${TURBO_DIR}/lib RENAME libturbojpeg.so.0 COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() -endif() - -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite_cv" AND - NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - if(PLATFORM_ARM64) - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/kernels/image/lite_cv - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h") - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so - DESTINATION ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - elseif(PLATFORM_ARM32) - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/kernels/image/lite_cv - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h") - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - else() - install(DIRECTORY ${TOP_DIR}/mindspore-lite/minddata/dataset/kernels/image/lite_cv - DESTINATION ${MIND_DATA_INC_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.h") - install(FILES ${BUILD_DIR}/minddata/libminddata-lite.so DESTINATION - ${RUNTIME_LIB_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME}) - endif() -endif() - if(WIN32) install(FILES ${TOP_DIR}/build/.commit_id DESTINATION ${RUNTIME_PKG_NAME} COMPONENT ${RUNTIME_COMPONENT_NAME}) @@ -585,8 +457,6 @@ if(PLATFORM_ARM64) COMPONENT ${RUNTIME_COMPONENT_NAME}) install(DIRECTORY ${BUILD_DIR}/src/ DESTINATION ${TEST_CASE_DIR} COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.so") - install(DIRECTORY ${BUILD_DIR}/minddata/ DESTINATION ${TEST_CASE_DIR} - COMPONENT ${RUNTIME_COMPONENT_NAME} FILES_MATCHING PATTERN "*.so") install(FILES ${JPEGTURBO_LIB_LIST} DESTINATION ${TEST_CASE_DIR}) if(SUPPORT_NPU) install(FILES ${DDK_LIB_PATH}/libhiai.so DESTINATION ${TEST_CASE_DIR} diff --git a/mindspore-lite/CMakeLists.txt b/mindspore-lite/CMakeLists.txt index fd8510513..0e6b40860 100644 --- a/mindspore-lite/CMakeLists.txt +++ b/mindspore-lite/CMakeLists.txt @@ -54,10 +54,7 @@ option(MSLITE_ENABLE_FAST_HASH_TABLE "enable fast hash table" ON) # pre/post process option(MSLITE_ENABLE_OPENCV "enable opencv" on) -if(NOT((MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE))) - set(MSLITE_MINDDATA_IMPLEMENT "full" CACHE STRING "minddata mode, \ - currently supported : off/full/wrapper/lite/lite_cv") -endif() + # tests option(MSLITE_ENABLE_TESTCASES "enable testcase" off) option(MSLITE_ENABLE_COVERAGE "enable code coverage" off) @@ -233,10 +230,7 @@ endif() if(DEFINED ENV{MSLITE_ENABLE_OPENCV}) set(MSLITE_ENABLE_OPENCV $ENV{MSLITE_ENABLE_OPENCV}) endif() -if(DEFINED ENV{MSLITE_MINDDATA_IMPLEMENT} AND -NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(MSLITE_MINDDATA_IMPLEMENT $ENV{MSLITE_MINDDATA_IMPLEMENT}) -endif() + if(DEFINED ENV{MSLITE_TARGET_SITEAI}) set(MSLITE_TARGET_SITEAI $ENV{MSLITE_TARGET_SITEAI}) endif() @@ -562,7 +556,6 @@ message(STATUS "\tMSLITE_ENABLE_AUTO_PARALLEL = \t${MSLITE_ENABLE message(STATUS "\tMSLITE_ENABLE_WEIGHT_DECODE = \t${MSLITE_ENABLE_WEIGHT_DECODE}") message(STATUS "\tMSLITE_ENABLE_CUSTOM_KERNEL = \t${MSLITE_ENABLE_CUSTOM_KERNEL}") message(STATUS "\tMSLITE_ENABLE_MINDRT = \t${MSLITE_ENABLE_MINDRT}") -message(STATUS "\tMSLITE_MINDDATA_IMPLEMENT = \t${MSLITE_MINDDATA_IMPLEMENT}") message(STATUS "\tMSLITE_ENABLE_DELEGATE = \t${MSLITE_ENABLE_DELEGATE}") message(STATUS "\tMSLITE_ENABLE_ACL = \t${MSLITE_ENABLE_ACL}") message(STATUS "\tMSLITE_ENABLE_FP16 = \t${MSLITE_ENABLE_FP16}") @@ -755,9 +748,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/litert/kernel/cpu) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/) include_directories(${TOP_DIR}/third_party) include_directories(${CMAKE_BINARY_DIR}) -if(NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/minddata/dataset) -endif() include(${TOP_DIR}/cmake/utils.cmake) include(${TOP_DIR}/cmake/dependency_utils.cmake) include(${TOP_DIR}/cmake/external_libs/securec.cmake) @@ -793,11 +783,6 @@ if(MSLITE_ENABLE_CONVERTER OR MSLITE_ENABLE_TOOLS OR MSLITE_ENABLE_KERNEL_EXECUT set(MSLITE_DEPS_JSON on) endif() -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" OR MSLITE_MINDDATA_IMPLEMENT STREQUAL "wrapper" - AND NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(MSLITE_DEPS_JSON on) -endif() - if(DEFINED ARCHS) add_definitions(-DMS_COMPILE_IOS) endif() @@ -1026,13 +1011,6 @@ if(NOT PLATFORM_ARM) endif() endif() -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite" OR MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" - OR MSLITE_MINDDATA_IMPLEMENT STREQUAL "wrapper" OR MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite_cv" - AND NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - add_compile_definitions(ENABLE_ANDROID) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/minddata) -endif() - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/common/ops) if(ANDROID_NDK_TOOLCHAIN_INCLUDED OR TARGET_OHOS_LITE OR TARGET_HIMIX) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tools/converter/micro/coder) diff --git a/mindspore-lite/cmake/lite_options.cmake b/mindspore-lite/cmake/lite_options.cmake index f99f9a643..5eb5c88e3 100644 --- a/mindspore-lite/cmake/lite_options.cmake +++ b/mindspore-lite/cmake/lite_options.cmake @@ -10,7 +10,6 @@ set(MSLITE_REGISTRY_DEVICE "off" CACHE STRING "Compile Mindspore Lite that suppo currently supported devices: Hi3516D/Hi3519A/Hi3559A/SD3403") set(MSLITE_MICRO_PLATFORM "auto" CACHE STRING "Platform of micro static library micro static, \ currently supported : cortex-m7/auto") -set(MSLITE_MINDDATA_IMPLEMENT "lite_cv" CACHE STRING "off, lite_cv, cloud, or full") option(MSLITE_ENABLE_NPU "enable npu, only arm64 or arm32 support" off) option(MSLITE_ENABLE_COREML "enable coreML, only apple ios devices support" off) @@ -149,10 +148,6 @@ endif() if(DEFINED ENV{MSLITE_ENABLE_ACL}) set(MSLITE_ENABLE_ACL $ENV{MSLITE_ENABLE_ACL}) endif() -if(DEFINED ENV{MSLITE_MINDDATA_IMPLEMENT} AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(MSLITE_MINDDATA_IMPLEMENT $ENV{MSLITE_MINDDATA_IMPLEMENT}) -endif() if(DEFINED ENV{MSLITE_ENABLE_MODEL_ENCRYPTION}) if((${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND PLATFORM_X86_64) OR((PLATFORM_ARM64 OR PLATFORM_ARM32) AND ANDROID_NDK_TOOLCHAIN_INCLUDED)) @@ -312,10 +307,6 @@ endif() if(MSLITE_ENABLE_TRAIN) set(SUPPORT_TRAIN on) - if(NOT MSLITE_MINDDATA_IMPLEMENT STREQUAL "off" OR NOT PLATFORM_ARM AND - NOT(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(MSLITE_MINDDATA_IMPLEMENT full) - endif() endif() if(MSLITE_ENABLE_NPU) @@ -371,7 +362,6 @@ message(STATUS "\tMSLITE_ENABLE_AUTO_PARALLEL = \t${MSLITE_ENABLE message(STATUS "\tMSLITE_ENABLE_WEIGHT_DECODE = \t${MSLITE_ENABLE_WEIGHT_DECODE}") message(STATUS "\tMSLITE_ENABLE_CUSTOM_KERNEL = \t${MSLITE_ENABLE_CUSTOM_KERNEL}") message(STATUS "\tMSLITE_ENABLE_MINDRT = \t${MSLITE_ENABLE_MINDRT}") -message(STATUS "\tMSLITE_MINDDATA_IMPLEMENT = \t${MSLITE_MINDDATA_IMPLEMENT}") message(STATUS "\tMSLITE_ENABLE_DELEGATE = \t${MSLITE_ENABLE_DELEGATE}") message(STATUS "\tMSLITE_ENABLE_ACL = \t${MSLITE_ENABLE_ACL}") message(STATUS "\tMSLITE_ENABLE_FP16 = \t${MSLITE_ENABLE_FP16}") diff --git a/mindspore-lite/examples/quick_start_micro/mobilenetv2_arm64/README.md b/mindspore-lite/examples/quick_start_micro/mobilenetv2_arm64/README.md index f96cfaf80..d6bf581f9 100755 --- a/mindspore-lite/examples/quick_start_micro/mobilenetv2_arm64/README.md +++ b/mindspore-lite/examples/quick_start_micro/mobilenetv2_arm64/README.md @@ -62,10 +62,6 @@ mindspore-lite-{version}-inference-android-{arch} │ ├── lib # 推理框架库 │ │ ├── libmindspore-lite.a # MindSpore Lite推理框架的静态库 │ │ └── libmindspore-lite.so # MindSpore Lite推理框架的动态库 -│ ├── minddata # 图像处理库 -│ │ ├── include -│ │ └── lib -│ │ └── libminddata-lite.so # 图像处理动态库文件 │ └── third_party # NPU库 │ └── hiai_ddk └── tools diff --git a/mindspore-lite/examples/runtime_java/app/download.gradle b/mindspore-lite/examples/runtime_java/app/download.gradle index 495eda3a4..a011ed678 100644 --- a/mindspore-lite/examples/runtime_java/app/download.gradle +++ b/mindspore-lite/examples/runtime_java/app/download.gradle @@ -1,6 +1,6 @@ /** * To download necessary library from HuaWei server. - * Including mindspore-lite .so file, minddata-lite .so file and model file. + * Including mindspore-lite .so file, and model file. * The libraries can be downloaded manually. */ def targetModelFile = "src/main/assets/mobilenetv2.ms" diff --git a/mindspore-lite/examples/train_lenet_cpp/Makefile b/mindspore-lite/examples/train_lenet_cpp/Makefile index 465ece34d..e3c95b9ec 100644 --- a/mindspore-lite/examples/train_lenet_cpp/Makefile +++ b/mindspore-lite/examples/train_lenet_cpp/Makefile @@ -1,7 +1,7 @@ BASE_DIR=$(realpath ../../../../) APP:=bin/net_runner INF_APP:=bin/infer -LMSTLIB:=-lmindspore-lite-train -lminddata-lite +LMSTLIB:=-lmindspore-lite-train LMSLIB:=-lmindspore-lite MSDIR:=$(realpath package-$(TARGET)/lib) ifneq ("$(wildcard $(MSDIR)/libhiai.so)","") @@ -20,7 +20,6 @@ CFLAGS := -Ofast -std=c++17 \ -I . \ -I ./msl/runtime \ -I ./msl/runtime/include \ - -I ./msl/runtime/minddata \ -I ./msl/tools/third_party/flatbuffers/include diff --git a/mindspore-lite/examples/train_lenet_cpp/README.md b/mindspore-lite/examples/train_lenet_cpp/README.md index b085811b6..bce0a5030 100644 --- a/mindspore-lite/examples/train_lenet_cpp/README.md +++ b/mindspore-lite/examples/train_lenet_cpp/README.md @@ -129,8 +129,6 @@ When the `prepare_and_run.sh` script is run, the following folder is prepared. I │   ├── eval.sh # on-device script that load the train model and evaluates its accuracy │   ├── lib │   │   ├── libjpeg.so.62 -│   │   ├── libminddata-lite.a -│   │   ├── libminddata-lite.so │   │   ├── libmindspore-lite.a │   │   ├── libmindspore-lite-jni.so │   │   ├── libmindspore-lite.so diff --git a/mindspore-lite/examples/train_lenet_cpp/README_CN.md b/mindspore-lite/examples/train_lenet_cpp/README_CN.md index a1b654692..b5229ee63 100644 --- a/mindspore-lite/examples/train_lenet_cpp/README_CN.md +++ b/mindspore-lite/examples/train_lenet_cpp/README_CN.md @@ -118,8 +118,6 @@ train_lenet/ │   ├── eval.sh # on-device script that load the train model and evaluates its accuracy │   ├── lib │   │   ├── libjpeg.so.62 -│   │   ├── libminddata-lite.a -│   │   ├── libminddata-lite.so │   │   ├── libmindspore-lite.a │   │   ├── libmindspore-lite-jni.so │   │   ├── libmindspore-lite.so diff --git a/mindspore-lite/examples/transfer_learning/Makefile b/mindspore-lite/examples/transfer_learning/Makefile index 2caefe485..d5cfa58d1 100644 --- a/mindspore-lite/examples/transfer_learning/Makefile +++ b/mindspore-lite/examples/transfer_learning/Makefile @@ -1,7 +1,6 @@ BASE_DIR=$(realpath ../../../../) APP:=bin/net_runner LMSLIB:=-lmindspore-lite-train -lmindspore-lite -LMDLIB:=-lminddata-lite MSDIR:=$(realpath package-$(TARGET)/lib) ifneq ("$(wildcard $(MSDIR)/libhiai.so)","") LHIAILIB:=-lhiai_ir_build -lhiai_ir -lhiai @@ -16,7 +15,6 @@ CFLAGS := -Ofast -std=c++17 \ -I . \ -I ./msl/runtime \ -I ./msl/runtime/include \ - -I ./msl/runtime/minddata \ -I ./msl/tools/third_party/flatbuffers/include diff --git a/mindspore-lite/examples/transfer_learning/README.md b/mindspore-lite/examples/transfer_learning/README.md index 174e9cea3..359a53543 100644 --- a/mindspore-lite/examples/transfer_learning/README.md +++ b/mindspore-lite/examples/transfer_learning/README.md @@ -27,7 +27,7 @@ For this demo we will use only the [validation data of small images](http://plac - Dataset size:501M,36,500 224*224 images in 365 classes - Dataiset format:jpg files - - Note:In the current release, data is customely loaded using a proprietary DataSet class (provided in dataset.cc). In the upcoming releases loading will be done using MindSpore MindData infrastructure. In order to fit the data to the model it will be preprocessed using [ImageMagick convert tool](https://imagemagick.org/), namely croping and converting to bmp format. + - Note:In the current release, data is customely loaded using a proprietary DataSet class (provided in dataset.cc). In order to fit the data to the model it will be preprocessed using [ImageMagick convert tool](https://imagemagick.org/), namely croping and converting to bmp format. - Note: Only 10 classes out of the 365 will be used in this demo - Note: 60% of the data will be used for training, 20% will be used for testing and the remaining 20% for validation @@ -144,8 +144,6 @@ package-arm64/ │   │   └── 99.bmp ├── lib │  ├── libjpeg.so.62 -│   ├── libminddata-lite.a -│   ├── libminddata-lite.so │   ├── libmindspore-lite.a │   ├── libmindspore-lite-jni.so │   ├── libmindspore-lite.so diff --git a/mindspore-lite/examples/transfer_learning/README_CN.md b/mindspore-lite/examples/transfer_learning/README_CN.md index 319c6ce4d..97a9ca193 100644 --- a/mindspore-lite/examples/transfer_learning/README_CN.md +++ b/mindspore-lite/examples/transfer_learning/README_CN.md @@ -129,8 +129,6 @@ package-arm64/ │   │   └── 99.bmp ├── lib │  ├── libjpeg.so.62 -│   ├── libminddata-lite.a -│   ├── libminddata-lite.so │   ├── libmindspore-lite.a │   ├── libmindspore-lite-jni.so │   ├── libmindspore-lite.so diff --git a/mindspore-lite/minddata/CMakeLists.txt b/mindspore-lite/minddata/CMakeLists.txt deleted file mode 100644 index a7b68b511..000000000 --- a/mindspore-lite/minddata/CMakeLists.txt +++ /dev/null @@ -1,691 +0,0 @@ -find_package(Patch) -if(NOT Patch_FOUND) - message(FATAL_ERROR "Patch not found, please set environment variable MS_PATCH_PATH to path where Patch is located, " - "usually found in GIT_PATH/usr/bin on Windows") -endif() - -include(${TOP_DIR}/cmake/external_libs/jpeg_turbo.cmake) -include(${TOP_DIR}/cmake/external_libs/zlib.cmake) - -set(CMAKE_CXX_STANDARD 17) - -set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -g2 -ggdb -fno-inline-functions -fno-omit-frame-pointer \ - -D_LIBCPP_INLINE_VISIBILITY='' -D_LIBCPP_DISABLE_EXTERN_TEMPLATE=1 -DHALF_ENABLE_CPP11_USER_LITERALS=0 \ - -D_FORTIFY_SOURCE=2 -Wno-cpp") -if(TARGET_AOS_ARM) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-sign-compare -Wno-overloaded-virtual \ - -Wno-unused-variable") - set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -std=c++17 -Wall -fPIC -march=armv8.2-a -funsafe-math-optimizations \ - -ftree-vectorize -flax-vector-conversions") -else() - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -Wno-return-std-move -Wno-unused-private-field \ - -Wno-unused-lambda-capture -Wno-sign-compare -Wno-overloaded-virtual -Wno-unneeded-internal-declaration \ - -Wno-unused-variable -Wno-pessimizing-move -Wno-inconsistent-missing-override") - set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -I/usr/local/include -std=c++17 -Wall -fPIC") -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OPTION_CXX_FLAGS}") - -if(PLATFORM_ARM) - if(TARGET_AOS_ARM) - set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O2 -Wno-sign-compare -Wno-overloaded-virtual \ - -Wno-unused-variable -DHALF_ENABLE_CPP11_USER_LITERALS=0 -D_FORTIFY_SOURCE=2 -O2") - else() - set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O2 -Werror -Wno-return-std-move -Wno-unused-private-field \ - -Wno-unused-lambda-capture -Wno-sign-compare -Wno-overloaded-virtual -Wno-unneeded-internal-declaration \ - -Wno-unused-variable -Wno-pessimizing-move -Wno-inconsistent-missing-override \ - -DHALF_ENABLE_CPP11_USER_LITERALS=0 -D_FORTIFY_SOURCE=2") - endif() -else() - set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O2 -Wl,--allow-shlib-undefined -DHALF_ENABLE_CPP11_USER_LITERALS=0 \ - -D_FORTIFY_SOURCE=2") -endif() -if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - # debug mode do not change flags -else() - set(CMAKE_C_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -Werror -fstack-protector-strong -Wno-attributes \ - -Wno-deprecated-declarations -Wno-missing-braces ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -Werror -fstack-protector-strong -Wno-attributes \ - -Wno-deprecated-declarations -Wno-missing-braces -Wno-overloaded-virtual ${CMAKE_CXX_FLAGS}") - if(TARGET_AOS_ARM) - set(CMAKE_C_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -fstack-protector-strong -Wno-attributes \ - -Wno-deprecated-declarations -Wno-missing-braces ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -fstack-protector-strong -Wno-attributes \ - -Wno-deprecated-declarations -Wno-missing-braces -Wno-overloaded-virtual ${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_C_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -Werror -fstack-protector-strong -Wno-attributes \ - -Wno-deprecated-declarations -Wno-missing-braces ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-fPIC -fPIE -D_FORTIFY_SOURCE=2 -O2 -Wall -Werror -fstack-protector-strong \ - -Wno-attributes -Wno-deprecated-declarations -Wno-missing-braces -Wno-overloaded-virtual ${CMAKE_CXX_FLAGS}") - endif() -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-private-field") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sequence-point") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") -if(PLATFORM_ARM) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-uninitialized") -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-abstract-non-virtual-dtor") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") -endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default") - -set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS} -s") - -if(MSLITE_ENABLE_RUNTIME_GLOG AND (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - add_definitions(-DUSE_GLOG) - string(REPLACE "-fno-rtti" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) - string(REPLACE "-fno-rtti" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -endif() - -message(STATUS "Compile minddata in [${MSLITE_MINDDATA_IMPLEMENT}] mode") -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full") - include_directories("./") - include_directories("dataset") - include_directories("dataset/kernels/image") - include_directories("dataset/liteapi") - include_directories("${TOP_DIR}/mindspore-lite") - include_directories("${TOP_DIR}") - include_directories("${NNACL_DIR}/../") - - if(MSLITE_ENABLE_ACL) - include_directories(${CCSRC_DIR}) - endif() - - set(LITE_SRC_FILES - ${TOP_DIR}/mindspore-lite/src/litert/cxx_api/types.cc - ${TOP_DIR}/mindspore-lite/src/litert/cxx_api/tensor_utils.cc - ${TOP_DIR}/mindspore-lite/src/litert/cxx_api/tensor/tensor_impl.cc - ${TOP_DIR}/mindspore-lite/src/tensor.cc - ${NNACL_DIR}/tensor_c_utils.c - ${TOP_DIR}/mindspore-lite/src/common/utils.cc - ${TOP_DIR}/mindspore-lite/src/common/string_util.cc) - - set(MINDDATA_API_SRC_FILES - dataset/api/execute.cc - dataset/api/transforms.cc - dataset/api/datasets.cc - dataset/api/samplers.cc - dataset/api/iterator.cc - dataset/api/data_helper.cc - dataset/api/vision.cc) - - set(MINDDATA_UTIL_SRC_FILES - dataset/util/memory_pool.cc - dataset/util/path.cc - dataset/util/status.cc - dataset/util/service.cc - dataset/util/json_helper.cc - dataset/util/cond_var.cc - dataset/util/task_manager.cc - dataset/util/services.cc - dataset/util/task.cc - dataset/util/circular_pool.cc - dataset/util/lock.cc - dataset/util/wait_post.cc - dataset/util/intrp_service.cc - dataset/util/arena.cc) - - set(MINDDATA_CORE_SRC_FILES - dataset/core/config_manager.cc - dataset/core/data_type.cc - dataset/core/de_tensor.cc - dataset/core/global_context.cc - dataset/core/tensor.cc - dataset/core/tensor_helpers.cc - dataset/core/tensor_row.cc - dataset/core/tensor_shape.cc - dataset/core/client.cc) - - set(MINDDATA_KERNELS_SRC_FILES - dataset/kernels/tensor_op.cc) - - set(MINDDATA_KERNELS_IMAGE_SRC_FILES - dataset/kernels/image/affine_op.cc - dataset/kernels/image/lite_image_utils.cc - dataset/kernels/image/center_crop_op.cc - dataset/kernels/image/crop_op.cc - dataset/kernels/image/decode_op.cc - dataset/kernels/image/gaussian_blur_op.cc - dataset/kernels/image/hwc_to_chw_op.cc - dataset/kernels/image/normalize_op.cc - dataset/kernels/image/resize_op.cc - dataset/kernels/image/resize_preserve_ar_op.cc - dataset/kernels/image/rgb_to_bgr_op.cc - dataset/kernels/image/rgb_to_gray_op.cc - dataset/kernels/image/rotate_op.cc - dataset/kernels/image/random_affine_op.cc - dataset/kernels/image/math_utils.cc - dataset/kernels/image/exif_utils.cc) - - set(MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES - dataset/kernels/image/lite_cv/canny.cc - dataset/kernels/image/lite_cv/gaussian_blur.cc - dataset/kernels/image/lite_cv/image_process.cc - dataset/kernels/image/lite_cv/lite_mat.cc - dataset/kernels/image/lite_cv/warp_affine.cc) - - set(MINDDATA_KERNELS_DATA_SRC_FILES - dataset/kernels/data/compose_op.cc - dataset/kernels/data/data_utils.cc - dataset/kernels/data/duplicate_op.cc - dataset/kernels/data/one_hot_op.cc - dataset/kernels/data/random_apply_op.cc - dataset/kernels/data/random_choice_op.cc - dataset/kernels/data/type_cast_op.cc) - - set(MINDDATA_KERNELS_IR_SRC_FILES - dataset/kernels/ir/validators.cc) - - set(MINDDATA_KERNELS_IR_DATA_SRC_FILES - dataset/kernels/ir/data/transforms_ir.cc) - - set(MINDDATA_KERNELS_IR_VISION_SRC_FILES - dataset/kernels/ir/vision/affine_ir.cc - dataset/kernels/ir/vision/auto_contrast_ir.cc - dataset/kernels/ir/vision/bounding_box_augment_ir.cc - dataset/kernels/ir/vision/center_crop_ir.cc - dataset/kernels/ir/vision/crop_ir.cc - dataset/kernels/ir/vision/cutmix_batch_ir.cc - dataset/kernels/ir/vision/cutout_ir.cc - dataset/kernels/ir/vision/decode_ir.cc - dataset/kernels/ir/vision/equalize_ir.cc - dataset/kernels/ir/vision/gaussian_blur_ir.cc - dataset/kernels/ir/vision/hwc_to_chw_ir.cc - dataset/kernels/ir/vision/invert_ir.cc - dataset/kernels/ir/vision/mixup_batch_ir.cc - dataset/kernels/ir/vision/normalize_ir.cc - dataset/kernels/ir/vision/normalize_pad_ir.cc - dataset/kernels/ir/vision/pad_ir.cc - dataset/kernels/ir/vision/random_affine_ir.cc - dataset/kernels/ir/vision/random_color_adjust_ir.cc - dataset/kernels/ir/vision/random_color_ir.cc - dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc - dataset/kernels/ir/vision/random_crop_ir.cc - dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc - dataset/kernels/ir/vision/random_horizontal_flip_ir.cc - dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc - dataset/kernels/ir/vision/random_posterize_ir.cc - dataset/kernels/ir/vision/random_resized_crop_ir.cc - dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc - dataset/kernels/ir/vision/random_resize_ir.cc - dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc - dataset/kernels/ir/vision/random_rotation_ir.cc - dataset/kernels/ir/vision/random_select_subpolicy_ir.cc - dataset/kernels/ir/vision/random_sharpness_ir.cc - dataset/kernels/ir/vision/random_solarize_ir.cc - dataset/kernels/ir/vision/random_vertical_flip_ir.cc - dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc - dataset/kernels/ir/vision/rescale_ir.cc - dataset/kernels/ir/vision/resize_ir.cc - dataset/kernels/ir/vision/resize_preserve_ar_ir.cc - dataset/kernels/ir/vision/resize_with_bbox_ir.cc - dataset/kernels/ir/vision/rgba_to_bgr_ir.cc - dataset/kernels/ir/vision/rgba_to_rgb_ir.cc - dataset/kernels/ir/vision/rgb_to_bgr_ir.cc - dataset/kernels/ir/vision/rgb_to_gray_ir.cc - dataset/kernels/ir/vision/rotate_ir.cc - dataset/kernels/ir/vision/swap_red_blue_ir.cc - dataset/kernels/ir/vision/uniform_aug_ir.cc) - - set(MINDDATA_ENGINE_SRC_FILES - dataset/engine/tree_adapter_lite.cc - dataset/engine/tree_modifier.cc - dataset/engine/runtime_context.cc - dataset/engine/tree_adapter.cc - dataset/engine/execution_tree.cc - dataset/engine/dataset_iterator.cc - dataset/engine/data_schema.cc) - - set(MINDDATA_ENGINE_CONSUMERS_SRC_FILES - dataset/engine/consumers/pull_based_tree_consumer.cc - dataset/engine/consumers/tree_consumer.cc) - - set(MINDDATA_ENGINE_IR_DATASETOPS_SRC_FILES - dataset/engine/ir/datasetops/dataset_node.cc - dataset/engine/ir/datasetops/epoch_ctrl_node.cc - dataset/engine/ir/datasetops/batch_node.cc - dataset/engine/ir/datasetops/map_node.cc - dataset/engine/ir/datasetops/root_node.cc - dataset/engine/ir/datasetops/repeat_node.cc - dataset/engine/ir/datasetops/project_node.cc - dataset/engine/ir/datasetops/shuffle_node.cc - dataset/engine/ir/datasetops/skip_node.cc) - - set(MINDDATA_ENGINE_IR_DATASETOPS_SOURCE_SRC_FILES - dataset/engine/ir/datasetops/source/album_node.cc - dataset/engine/ir/datasetops/source/mnist_node.cc) - - set(MINDDATA_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SRC_FILES - dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc - dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc - dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc) - - set(MINDDATA_ENGINE_DATASETOPS_SRC_FILES - dataset/engine/datasetops/dataset_op.cc - dataset/engine/datasetops/repeat_op.cc - dataset/engine/datasetops/epoch_ctrl_op.cc - dataset/engine/datasetops/data_queue_op.cc - dataset/engine/datasetops/project_op.cc - dataset/engine/datasetops/shuffle_op.cc - dataset/engine/datasetops/skip_op.cc - dataset/engine/datasetops/pipeline_op.cc - dataset/engine/datasetops/batch_op.cc) - - set(MINDDATA_ENGINE_DATASETOPS_MAP_OP_SRC_FILES - dataset/engine/datasetops/map_op/map_op.cc - dataset/engine/datasetops/map_op/cpu_map_job.cc) - - set(MINDDATA_ENGINE_DATASETOPS_SOURCE_SRC_FILES - dataset/engine/datasetops/source/album_op.cc - dataset/engine/datasetops/source/mnist_op.cc - dataset/engine/datasetops/source/mappable_leaf_op.cc - dataset/engine/datasetops/source/io_block.cc) - - set(MINDDATA_ENGINE_DATASETOPS_SOURCE_SAMPLER_SRC_FILES - dataset/engine/datasetops/source/sampler/sampler.cc - dataset/engine/datasetops/source/sampler/subset_sampler.cc - dataset/engine/datasetops/source/sampler/distributed_sampler.cc - dataset/engine/datasetops/source/sampler/pk_sampler.cc - dataset/engine/datasetops/source/sampler/random_sampler.cc - dataset/engine/datasetops/source/sampler/sequential_sampler.cc - dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc - dataset/engine/datasetops/source/sampler/subset_random_sampler.cc - dataset/engine/datasetops/source/sampler/weighted_random_sampler.cc) - - set(MINDDATA_ENGINE_OPT_PRE_SRC_FILES - dataset/engine/opt/pre/add_skip_pass.cc - dataset/engine/opt/pre/cache_validation_pass.cc - dataset/engine/opt/pre/debug_mode_pass.cc - dataset/engine/opt/pre/deep_copy_pass.cc - dataset/engine/opt/pre/epoch_ctrl_pass.cc - dataset/engine/opt/pre/getter_pass.cc - dataset/engine/opt/pre/input_validation_pass.cc - dataset/engine/opt/pre/insert_map_pass.cc - dataset/engine/opt/pre/node_removal_pass.cc - dataset/engine/opt/pre/skip_pushdown_pass.cc) - - set(MINDDATA_ENGINE_OPT_POST_SRC_FILES - dataset/engine/opt/post/auto_worker_pass.cc) - - set(MINDDATA_ENGINE_OPT_SRC_FILES - dataset/engine/opt/pass.cc) - - set(MINDDATA_ENGINE_PERF_SRC_FILES - dataset/engine/perf/auto_tune.cc - dataset/engine/perf/connector_size.cc - dataset/engine/perf/dataset_iterator_tracing.cc - dataset/engine/perf/device_queue_tracing.cc - dataset/engine/perf/info_collector.cc - dataset/engine/perf/monitor.cc - dataset/engine/perf/profiling.cc) - - set(MINDDATA_CALLBACK_SRC_FILES - dataset/callback/callback_manager.cc) - - set(MINDDATA_FULL_SRC - ${LITE_SRC_FILES} - ${MINDDATA_API_SRC_FILES} - ${MINDDATA_UTIL_SRC_FILES} - ${MINDDATA_CORE_SRC_FILES} - ${MINDDATA_KERNELS_SRC_FILES} - ${MINDDATA_KERNELS_IMAGE_SRC_FILES} - ${MINDDATA_KERNELS_DATA_SRC_FILES} - ${MINDDATA_KERNELS_IR_SRC_FILES} - ${MINDDATA_KERNELS_IR_DATA_SRC_FILES} - ${MINDDATA_KERNELS_IR_VISION_SRC_FILES} - ${MINDDATA_ENGINE_SRC_FILES} - ${MINDDATA_ENGINE_CONSUMERS_SRC_FILES} - ${MINDDATA_ENGINE_IR_DATASETOPS_SRC_FILES} - ${MINDDATA_ENGINE_IR_DATASETOPS_SOURCE_SRC_FILES} - ${MINDDATA_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SRC_FILES} - ${MINDDATA_ENGINE_DATASETOPS_SRC_FILES} - ${MINDDATA_ENGINE_DATASETOPS_MAP_OP_SRC_FILES} - ${MINDDATA_ENGINE_DATASETOPS_SOURCE_SRC_FILES} - ${MINDDATA_ENGINE_DATASETOPS_SOURCE_SAMPLER_SRC_FILES} - ${MINDDATA_ENGINE_OPT_PRE_SRC_FILES} - ${MINDDATA_ENGINE_OPT_POST_SRC_FILES} - ${MINDDATA_ENGINE_OPT_SRC_FILES} - ${MINDDATA_ENGINE_PERF_SRC_FILES} - ${MINDDATA_CALLBACK_SRC_FILES} - ${CORE_DIR}/utils/status.cc - ) - - if(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) - set(MINDDATA_FULL_SRC - ${MINDDATA_FULL_SRC} - dataset/kernels/image/pad_op.cc - dataset/kernels/ir/vision/pad_ir.cc - dataset/kernels/image/swap_red_blue_op.cc - dataset/kernels/ir/vision/swap_red_blue_ir.cc - dataset/kernels/image/rescale_op.cc - dataset/kernels/ir/vision/rescale_ir.cc - dataset/core/cv_tensor.cc - dataset/kernels/image/resize_cubic_op.cc) - - if(MSLITE_ENABLE_ACL) - add_definitions(-DENABLE_DVPP) - set(MINDDATA_FULL_SRC - ${MINDDATA_FULL_SRC} - dataset/core/device_tensor.cc - dataset/kernels/ir/vision/ascend_vision_ir.cc) - add_subdirectory(dataset/kernels/image/dvpp kernels-dvpp-image) - endif() - endif() - - add_library(minddata-lite-obj OBJECT - ${MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES} - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/log.cc - ${CORE_DIR}/utils/ms_utils.cc - ${MINDDATA_FULL_SRC} - ) - add_dependencies(minddata-lite-obj fbs_src) - set(minddata_lite_submodules - $ - ) - if((MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) AND MSLITE_ENABLE_ACL) - add_dependencies(minddata-lite-obj kernels-dvpp-image) - set(minddata_lite_submodules - ${minddata_lite_submodules} - $ - ) - endif() - - add_library(minddata-lite SHARED ${minddata_lite_submodules}) - add_library(minddata-lite_static STATIC ${minddata_lite_submodules}) - set_target_properties(minddata-lite_static PROPERTIES OUTPUT_NAME "minddata-lite") - - if(TARGET_AOS_ARM) - set(THREADS_PREFER_PTHREAD_FLAG ON) - target_link_libraries(minddata-lite - mindspore::securec - mindspore::jpeg_turbo - mindspore::turbojpeg - mindspore::json - pthread - ) - target_link_libraries(minddata-lite_static - mindspore::securec - mindspore::jpeg_turbo - mindspore::turbojpeg - mindspore::json - pthread - ) - else() - find_package(Threads REQUIRED) - target_link_libraries(minddata-lite - mindspore::securec - mindspore::jpeg_turbo - mindspore::turbojpeg - mindspore::json - Threads::Threads - ) - target_link_libraries(minddata-lite_static - mindspore::securec - mindspore::jpeg_turbo - mindspore::turbojpeg - mindspore::json - Threads::Threads - ) - endif() - - if((MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) AND MSLITE_ENABLE_ACL) - target_link_libraries(minddata-lite - mindspore_core - mindspore_ops - mindspore::opencv_core - mindspore::opencv_imgcodecs - mindspore::opencv_imgproc - ) - target_link_libraries(minddata-lite_static - mindspore_core - mindspore_ops - mindspore::opencv_core - mindspore::opencv_imgcodecs - mindspore::opencv_imgproc - ) - endif() - - # ref: https://github.com/android/ndk/issues/1202 - if(ANDROID_NDK) - if(PLATFORM_ARM32) - file(GLOB_RECURSE LIBCLANG_RT_LIB $ENV{ANDROID_NDK}/libclang_rt.builtins-arm-android.a) - if(LIBCLANG_RT_LIB STREQUAL "") - MESSAGE(FATAL_ERROR "Cannot find libclang_rt.builtins-arm-androi2d.a in $ENV{ANDROID_NDK}") - endif() - target_link_libraries(minddata-lite ${LIBCLANG_RT_LIB}) - target_link_libraries(minddata-lite_static ${LIBCLANG_RT_LIB}) - endif() - - if(PLATFORM_ARM32 OR PLATFORM_ARM64) - target_link_libraries(minddata-lite log) - target_link_libraries(minddata-lite_static log) - endif() - endif() -elseif(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite") - include_directories(./) - include_directories(dataset) - include_directories("${TOP_DIR}/mindspore-lite") - - if(MSLITE_ENABLE_ACL) - include_directories(${CCSRC_DIR}) - endif() - - set(MINDDATA_API_SRC_FILES - dataset/api/execute.cc - dataset/api/transforms.cc) - - set(MINDDATA_UTIL_SRC_FILES - dataset/util/memory_pool.cc - dataset/util/path.cc - dataset/util/status.cc) - - set(MINDDATA_CORE_SRC_FILES - dataset/core/ascend_resource.cc - dataset/core/config_manager.cc - dataset/core/data_type.cc - dataset/core/de_tensor.cc - dataset/core/device_resource.cc - dataset/core/device_tensor.cc - dataset/core/global_context.cc - dataset/core/tensor.cc - dataset/core/tensor_helpers.cc - dataset/core/tensor_row.cc - dataset/core/tensor_shape.cc - dataset/core/types.cc) - - set(MINDDATA_KERNELS_SRC_FILES - dataset/kernels/c_func_op.cc - dataset/kernels/tensor_op.cc) - - set(MINDDATA_KERNELS_IMAGE_SRC_FILES - dataset/kernels/image/adjust_brightness_op.cc - dataset/kernels/image/adjust_contrast_op.cc - dataset/kernels/image/adjust_gamma_op.cc - dataset/kernels/image/adjust_hue_op.cc - dataset/kernels/image/adjust_saturation_op.cc - dataset/kernels/image/auto_augment_op.cc - dataset/kernels/image/convert_color_op.cc - dataset/kernels/image/crop_op.cc - dataset/kernels/image/decode_op.cc - dataset/kernels/image/decode_video_op.cc - dataset/kernels/image/erase_op.cc - dataset/kernels/image/exif_utils.cc - dataset/kernels/image/gaussian_blur_op.cc - dataset/kernels/image/horizontal_flip_op.cc - dataset/kernels/image/lite_image_utils.cc - dataset/kernels/image/math_utils.cc - dataset/kernels/image/normalize_op.cc - dataset/kernels/image/normalize_pad_op.cc - dataset/kernels/image/pad_to_size_op.cc - dataset/kernels/image/perspective_op.cc - dataset/kernels/image/rand_augment_op.cc - dataset/kernels/image/random_adjust_sharpness_op.cc - dataset/kernels/image/random_auto_contrast_op.cc - dataset/kernels/image/random_equalize_op.cc - dataset/kernels/image/random_invert_op.cc - dataset/kernels/image/random_lighting_op.cc - dataset/kernels/image/resize_cubic_op.cc - dataset/kernels/image/resize_op.cc - dataset/kernels/image/resized_crop_op.cc - dataset/kernels/image/rgb_to_bgr_op.cc - dataset/kernels/image/rotate_op.cc - dataset/kernels/image/slice_patches_op.cc - dataset/kernels/image/to_tensor_op.cc - dataset/kernels/image/trivial_augment_wide_op.cc - dataset/kernels/image/vertical_flip_op.cc - dataset/kernels/image/video_utils.cc) - - set(MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES - dataset/kernels/image/lite_cv/canny.cc - dataset/kernels/image/lite_cv/gaussian_blur.cc - dataset/kernels/image/lite_cv/image_process.cc - dataset/kernels/image/lite_cv/lite_mat.cc - dataset/kernels/image/lite_cv/warp_affine.cc) - - set(MINDDATA_KERNELS_DATA_SRC_FILES - dataset/kernels/data/compose_op.cc - dataset/kernels/data/concatenate_op.cc - dataset/kernels/data/data_utils.cc - dataset/kernels/data/duplicate_op.cc - dataset/kernels/data/fill_op.cc - dataset/kernels/data/mask_op.cc - dataset/kernels/data/one_hot_op.cc - dataset/kernels/data/pad_end_op.cc - dataset/kernels/data/parse_example_op.cc - dataset/kernels/data/random_apply_op.cc - dataset/kernels/data/random_choice_op.cc - dataset/kernels/data/slice_op.cc - dataset/kernels/data/to_float16_op.cc - dataset/kernels/data/type_cast_op.cc - dataset/kernels/data/unique_op.cc) - - set(MINDDATA_KERNELS_IR_SRC_FILES - dataset/kernels/ir/validators.cc) - - set(MINDDATA_KERNELS_IR_DATA_SRC_FILES - dataset/kernels/ir/data/transforms_ir.cc) - - set(MINDDATA_KERNELS_IR_VISION_SRC_FILES - dataset/kernels/ir/vision/affine_ir.cc - dataset/kernels/ir/vision/auto_contrast_ir.cc - dataset/kernels/ir/vision/bounding_box_augment_ir.cc - dataset/kernels/ir/vision/center_crop_ir.cc - dataset/kernels/ir/vision/crop_ir.cc - dataset/kernels/ir/vision/cutmix_batch_ir.cc - dataset/kernels/ir/vision/cutout_ir.cc - dataset/kernels/ir/vision/decode_ir.cc - dataset/kernels/ir/vision/equalize_ir.cc - dataset/kernels/ir/vision/gaussian_blur_ir.cc - dataset/kernels/ir/vision/hwc_to_chw_ir.cc - dataset/kernels/ir/vision/invert_ir.cc - dataset/kernels/ir/vision/mixup_batch_ir.cc - dataset/kernels/ir/vision/normalize_ir.cc - dataset/kernels/ir/vision/normalize_pad_ir.cc - dataset/kernels/ir/vision/pad_ir.cc - dataset/kernels/ir/vision/random_affine_ir.cc - dataset/kernels/ir/vision/random_color_adjust_ir.cc - dataset/kernels/ir/vision/random_color_ir.cc - dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc - dataset/kernels/ir/vision/random_crop_ir.cc - dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc - dataset/kernels/ir/vision/random_horizontal_flip_ir.cc - dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc - dataset/kernels/ir/vision/random_posterize_ir.cc - dataset/kernels/ir/vision/random_resized_crop_ir.cc - dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc - dataset/kernels/ir/vision/random_resize_ir.cc - dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc - dataset/kernels/ir/vision/random_rotation_ir.cc - dataset/kernels/ir/vision/random_select_subpolicy_ir.cc - dataset/kernels/ir/vision/random_sharpness_ir.cc - dataset/kernels/ir/vision/random_solarize_ir.cc - dataset/kernels/ir/vision/random_vertical_flip_ir.cc - dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc - dataset/kernels/ir/vision/rescale_ir.cc - dataset/kernels/ir/vision/resize_ir.cc - dataset/kernels/ir/vision/resize_preserve_ar_ir.cc - dataset/kernels/ir/vision/resize_with_bbox_ir.cc - dataset/kernels/ir/vision/rgba_to_bgr_ir.cc - dataset/kernels/ir/vision/rgba_to_rgb_ir.cc - dataset/kernels/ir/vision/rgb_to_gray_ir.cc - dataset/kernels/ir/vision/rotate_ir.cc - dataset/kernels/ir/vision/swap_red_blue_ir.cc - dataset/kernels/ir/vision/uniform_aug_ir.cc) - - add_library(minddata-lite SHARED - ${MINDDATA_API_SRC_FILES} - ${MINDDATA_UTIL_SRC_FILES} - ${MINDDATA_CORE_SRC_FILES} - ${MINDDATA_KERNELS_SRC_FILES} - ${MINDDATA_KERNELS_IMAGE_SRC_FILES} - ${MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES} - ${MINDDATA_KERNELS_DATA_SRC_FILES} - ${MINDDATA_KERNELS_IR_SRC_FILES} - ${MINDDATA_KERNELS_IR_DATA_SRC_FILES} - ${MINDDATA_KERNELS_IR_VISION_SRC_FILES} - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/log.cc - ${CORE_DIR}/utils/ms_utils.cc) - - target_link_libraries(minddata-lite - mindspore::securec - jpeg-turbo - jpeg - mindspore::json) - - # ref: https://github.com/android/ndk/issues/1202 - if(PLATFORM_ARM32) - file(GLOB_RECURSE LIBCLANG_RT_LIB $ENV{ANDROID_NDK}/libclang_rt.builtins-arm-android.a) - if(LIBCLANG_RT_LIB STREQUAL "") - MESSAGE(FATAL_ERROR "Cannot find libclang_rt.builtins-arm-androi2d.a in $ENV{ANDROID_NDK}") - endif() - target_link_libraries(minddata-lite ${LIBCLANG_RT_LIB}) - endif() -elseif(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite_cv") - include_directories(./) - include_directories(dataset) - include_directories(dataset/kernels/image) - include_directories("${TOP_DIR}/mindspore-lite") - - if(MSLITE_ENABLE_ACL) - include_directories(${CCSRC_DIR}) - endif() - - set(MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES - dataset/kernels/image/lite_cv/canny.cc - dataset/kernels/image/lite_cv/gaussian_blur.cc - dataset/kernels/image/lite_cv/image_process.cc - dataset/kernels/image/lite_cv/lite_mat.cc - dataset/kernels/image/lite_cv/warp_affine.cc) - - add_library(minddata-lite SHARED - ${MINDDATA_KERNELS_IMAGE_LITE_CV_SRC_FILES}) - - # ref: https://github.com/android/ndk/issues/1202 - if(PLATFORM_ARM32) - file(GLOB_RECURSE LIBCLANG_RT_LIB $ENV{ANDROID_NDK}/libclang_rt.builtins-arm-android.a) - if(LIBCLANG_RT_LIB STREQUAL "") - MESSAGE(FATAL_ERROR "Cannot find libclang_rt.builtins-arm-androi2d.a in $ENV{ANDROID_NDK}") - endif() - target_link_libraries(minddata-lite ${LIBCLANG_RT_LIB}) - endif() -endif() - -if(MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE) - target_link_libraries(minddata-lite ${PYTHON_LIBRARIES}) -endif() diff --git a/mindspore-lite/minddata/dataset/api/data_helper.cc b/mindspore-lite/minddata/dataset/api/data_helper.cc deleted file mode 100644 index fbe7bd12d..000000000 --- a/mindspore-lite/minddata/dataset/api/data_helper.cc +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/include/dataset/data_helper.h" - -#include "mindspore-lite/minddata/dataset/util/json_helper.h" -#include "include/api/status.h" - -namespace mindspore { -namespace dataset { -// Create a numbered json file from image folder -Status DataHelper::CreateAlbumIF(const std::vector &in_dir, const std::vector &out_dir) { - auto jh = JsonHelper(); - return jh.CreateAlbum(CharToString(in_dir), CharToString(out_dir)); -} - -// A print method typically used for debugging -void DataHelper::Print(std::ostream &out) const { - out << " Data Helper" - << "\n"; -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector> &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), VectorCharToString(value), CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateArray(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), CharToString(value), CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const bool &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const int8_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint8_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const int16_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint16_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const int32_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint32_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const int64_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint64_t &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const float &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::UpdateValueIF(const std::vector &in_file, const std::vector &key, const double &value, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.UpdateValue(CharToString(in_file), CharToString(key), value, CharToString(out_file)); -} - -Status DataHelper::RemoveKeyIF(const std::vector &in_file, const std::vector &key, - const std::vector &out_file) { - auto jh = JsonHelper(); - return jh.RemoveKey(CharToString(in_file), CharToString(key), CharToString(out_file)); -} - -size_t DataHelper::DumpData(const unsigned char *tensor_addr, const size_t &tensor_size, void *addr, - const size_t &buffer_size) { - auto jh = JsonHelper(); - return jh.DumpData(tensor_addr, tensor_size, addr, buffer_size); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/datasets.cc b/mindspore-lite/minddata/dataset/api/datasets.cc deleted file mode 100644 index 8bd4fc5de..000000000 --- a/mindspore-lite/minddata/dataset/api/datasets.cc +++ /dev/null @@ -1,2186 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/runtime_context.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/iterator.h" -#include "mindspore-lite/minddata/dataset/include/dataset/samplers.h" -#include "mindspore-lite/minddata/dataset/kernels/c_func_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h" -#include "mindspore-lite/minddata/dataset/include/dataset/text.h" -#endif - -// Sampler headers (in alphabetical order) -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" - -// IR dataset node -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -// IR non-leaf nodes -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h" -#endif - -// IR leaf nodes -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h" -#endif - -namespace mindspore { -namespace dataset { -// convert MSTensorVec to DE TensorRow, return empty if fails -TensorRow VecToRow(const MSTensorVec &v) { - TensorRow row; - row.reserve(v.size()); - for (const MSTensor &t : v) { - std::shared_ptr rt; - Status rc = Tensor::CreateFromMSTensor(t, &rt); - if (rc.IsError()) { - MS_LOG(ERROR) << "Convert from MSTensor to DETensor failed:" << rc.ToString() << "."; - return {}; - } - row.emplace_back(rt); - } - return row; -} - -// convert DE TensorRow to MSTensorVec, won't fail -MSTensorVec RowToVec(const TensorRow &v) { - MSTensorVec rv; - rv.reserve(v.size()); - std::transform(v.begin(), v.end(), std::back_inserter(rv), [](const std::shared_ptr &t) -> MSTensor { - return mindspore::MSTensor(std::make_shared(t)); - }); - return rv; -} - -// Convert a std::function to std::function with this helper -TensorRow FuncPtrConverter(const std::function &func, const TensorRow &in_row) { - return VecToRow(func(RowToVec(in_row))); -} - -// Function to create the iterator, which will build and launch the execution tree. -std::shared_ptr Dataset::CreateIteratorCharIF(int32_t num_epochs) { - std::shared_ptr iter; - try { - auto ds = shared_from_this(); - iter = std::make_shared(); - Status rc = iter->BuildAndLaunchTree(ds, num_epochs); - if (rc.IsError()) { - MS_LOG(ERROR) << "CreateIterator failed." << rc; - return nullptr; - } - } catch (const std::exception &err) { - MS_LOG(ERROR) << "CreateIterator: Iterator exception caught: " << err.what(); - return nullptr; - } - - return iter; -} - -// Function to create the iterator, which will build and launch the execution tree. -std::shared_ptr Dataset::CreatePullBasedIterator() { - auto ds = shared_from_this(); - std::shared_ptr iter = std::make_shared(); - Status rc = iter->BuildAndLaunchTree(ds, 1); - if (rc.IsError()) { - MS_LOG(ERROR) << "CreateIterator: Iterator exception caught: " << rc; - } - RETURN_SECOND_IF_ERROR(rc, nullptr); - return iter; -} - -// Function to return a transferred Node that transfers data through a device. -bool Dataset::DeviceQueueCharIF(const std::vector &queue_name, const std::vector &device_type, - int32_t device_id, int32_t num_epochs, bool send_epoch_end, int32_t total_batches, - bool create_data_info_queue) { -#ifndef ENABLE_ANDROID - Status rc; - - // Build and launch tree - std::unique_ptr runtime_context = std::make_unique(); - rc = runtime_context->Init(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Failed to init runtime context. Error status: " << rc; - return false; - } - - // Add DataQueueNode IR on top of dataset - auto ds = - std::make_shared(shared_from_this()->IRNode(), CharToString(queue_name), CharToString(device_type), - device_id, send_epoch_end, total_batches, create_data_info_queue); - - // Get ToDevice consumer - auto consumer = std::make_unique(num_epochs); - ToDevice *consumer_ptr = consumer.get(); - if (consumer_ptr == nullptr) { - MS_LOG(ERROR) << "ToDevice: Failed to get consumer."; - return false; - } - rc = consumer->Init(ds); - if (rc.IsError()) { - MS_LOG(ERROR) << "ToDevice: Failed to init. Error status: " << rc; - return false; - } - runtime_context->AssignConsumer(std::move(consumer)); - - // Send data to device - rc = consumer_ptr->Send(); - if (rc.IsError()) { - MS_LOG(ERROR) << "ToDevice: Failed to send data to device. Error status: " << rc; - return false; - } - - return true; -#else - MS_LOG(ERROR) << "DeviceQueueCharIF is not support for Android."; - return false; -#endif -} - -#ifndef ENABLE_ANDROID -// Function to create the saver, which will build and launch the execution tree and save data -bool Dataset::SaveCharIF(const std::vector &dataset_path, int32_t num_files, - const std::vector &dataset_type) { - Status rc; - // Build and launch tree - auto ds = shared_from_this(); - std::unique_ptr runtime_context = std::make_unique(); - rc = runtime_context->Init(); - if (rc.IsError()) { - MS_LOG(ERROR) << "CreateSaver failed." << rc; - return false; - } - - // Get SaveToDisk consumer - auto consumer = std::make_unique(CharToString(dataset_path), num_files, CharToString(dataset_type)); - rc = consumer->ValidateParams(); - if (rc.IsError()) { - MS_LOG(ERROR) << "CreateSaver failed." << rc; - return false; - } - SaveToDisk *consumer_ptr = consumer.get(); - if (consumer_ptr == nullptr) { - MS_LOG(ERROR) << "ToDevice: Failed to get consumer."; - return false; - } - rc = consumer->Init(ds->IRNode()); - if (rc.IsError()) { - MS_LOG(ERROR) << "CreateSaver failed." << rc; - return false; - } - - runtime_context->AssignConsumer(std::move(consumer)); - - // Save data into file - rc = consumer_ptr->Save(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Saver: Failed to save data into file. Error status: " << rc; - return false; - } - - // Shut down the data pipeline - rc = runtime_context->Terminate(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Saver: Failed to shut down pipeline. Error status: " << rc; - return false; - } - - return true; -} -#endif - -// Constructor -Dataset::Dataset() { tree_getters_ = std::make_shared(); } - -int64_t Dataset::GetDatasetSize(bool estimate) { - int64_t dataset_size = -1; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), -1); - std::shared_ptr size_getter = std::make_shared(); - DatasetSizeGetter *consumer = size_getter.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "DatasetSizeGetter: Failed to get consumer."; - return -1; - } - runtime_context->AssignConsumer(size_getter); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), -1); - RETURN_SECOND_IF_ERROR(consumer->GetDatasetSize(&dataset_size, estimate), -1); - return dataset_size; -} - -std::vector Dataset::GetOutputTypes() { - std::vector types; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), {}); - TreeGetters *consumer = tree_getters_.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "TreeGetters: Failed to get consumer."; - return std::vector(); - } - runtime_context->AssignConsumer(tree_getters_); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), {}); - RETURN_SECOND_IF_ERROR(consumer->GetOutputTypes(&types), {}); - std::vector ret_types; - std::transform( - types.begin(), types.end(), std::back_inserter(ret_types), - [](const DataType &d) -> mindspore::DataType { return static_cast(DETypeToMSType(d)); }); - return ret_types; -} - -std::vector> Dataset::GetOutputShapes() { - std::vector shapes; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), {}); - TreeGetters *consumer = tree_getters_.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "TreeGetters: Failed to get consumer."; - return std::vector>(); - } - runtime_context->AssignConsumer(tree_getters_); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), {}); - RETURN_SECOND_IF_ERROR(consumer->GetOutputShapes(&shapes), {}); - std::vector> ret_shapes; - std::transform(shapes.begin(), shapes.end(), std::back_inserter(ret_shapes), - [](const TensorShape &s) -> std::vector { return s.AsVector(); }); - return ret_shapes; -} - -int64_t Dataset::GetNumClasses() { - int64_t num_classes = -1; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), -1); - TreeGetters *consumer = tree_getters_.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "TreeGetters: Failed to get consumer."; - return -1; - } - runtime_context->AssignConsumer(tree_getters_); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), -1); - RETURN_SECOND_IF_ERROR(consumer->GetNumClasses(&num_classes), -1); - return num_classes; -} - -std::vector> Dataset::GetColumnNamesCharIF() { - std::vector col_names; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), {}); - TreeGetters *consumer = tree_getters_.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "TreeGetters: Failed to get consumer."; - return std::vector>(); - } - runtime_context->AssignConsumer(tree_getters_); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), {}); - RETURN_SECOND_IF_ERROR(consumer->GetColumnNames(&col_names), {}); - return VectorStringToChar(col_names); -} - -std::vector, std::vector>> Dataset::GetClassIndexingCharIF() { - std::vector>> output_class_indexing; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), {}); - TreeGetters *consumer = tree_getters_.get(); - if (consumer == nullptr) { - MS_LOG(ERROR) << "TreeGetters: Failed to get consumer."; - return std::vector, std::vector>>(); - } - runtime_context->AssignConsumer(tree_getters_); - RETURN_SECOND_IF_ERROR(consumer->Init(this->IRNode()), {}); - RETURN_SECOND_IF_ERROR(consumer->GetClassIndexing(&output_class_indexing), {}); - return ClassIndexStringToChar(output_class_indexing); -} - -/// \brief Function to create a SchemaObj -/// \param[in] schema_file Path of schema file -/// \return Shared pointer to the current schema -std::shared_ptr SchemaCharIF(const std::vector &schema_file) { - auto schema = std::make_shared(CharToString(schema_file)); - return schema->Init() ? schema : nullptr; -} - -// FUNCTIONS TO CREATE DATASETS FOR DATASET OPS -// (In alphabetical order) - -// Function to create a Batch dataset -BatchDataset::BatchDataset(const std::shared_ptr &input, int32_t batch_size, bool drop_remainder) { - // Default values - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), batch_size, drop_remainder); - ir_node_ = std::static_pointer_cast(ds); - } -} - -#ifndef ENABLE_ANDROID -// Function to create a BucketBatchByLength dataset -BucketBatchByLengthDataset::BucketBatchByLengthDataset( - const std::shared_ptr &input, const std::vector> &column_names, - const std::vector &bucket_boundaries, const std::vector &bucket_batch_sizes, - const std::function &element_length_function, - const std::map, std::pair, MSTensor>> &pad_info, bool pad_to_bucket_boundary, - bool drop_remainder) { - std::shared_ptr c_func = nullptr; - if (element_length_function != nullptr) { - c_func = std::make_shared(std::bind(FuncPtrConverter, element_length_function, std::placeholders::_1)); - } - - std::map, std::pair>> map; - for (auto const &p : pad_info) { - const MSTensor &t = p.second.second; - std::shared_ptr rt; - Status rc = Tensor::CreateFromMemory(TensorShape(t.Shape()), MSTypeToDEType(static_cast(t.DataType())), - (const uchar *)(t.Data().get()), t.DataSize(), &rt); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to create DETensor from MSTensor for pad_info: " << rc.ToString() << "."; - map.clear(); - break; - } - map.insert({p.first, {TensorShape(p.second.first), rt}}); - } - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), VectorCharToString(column_names), - bucket_boundaries, bucket_batch_sizes, c_func, - MapCharToString(map), pad_to_bucket_boundary, drop_remainder); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -ConcatDataset::ConcatDataset(const std::vector> &datasets) { - std::vector> all_datasets; - (void)std::transform(datasets.begin(), datasets.end(), std::back_inserter(all_datasets), - [](const std::shared_ptr &dataset) -> std::shared_ptr { - return (dataset != nullptr) ? dataset->IRNode() : nullptr; - }); - auto ds = std::make_shared(all_datasets); - - ir_node_ = std::static_pointer_cast(ds); -} - -FilterDataset::FilterDataset(const std::shared_ptr &input, - const std::function &predicate, - const std::vector> &input_columns) { - std::shared_ptr c_func = nullptr; - if (predicate) { - c_func = std::make_shared(std::bind(FuncPtrConverter, predicate, std::placeholders::_1)); - } - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), c_func, VectorCharToString(input_columns)); - ir_node_ = std::static_pointer_cast(ds); - } -} -#endif - -MapDataset::MapDataset(const std::shared_ptr &input, - const std::vector> &operations, - const std::vector> &input_columns, - const std::vector> &output_columns, const std::shared_ptr &cache, - const std::vector> &callbacks) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), operations, VectorCharToString(input_columns), - VectorCharToString(output_columns), cache, callbacks); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -ProjectDataset::ProjectDataset(const std::shared_ptr &input, const std::vector> &columns) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), VectorCharToString(columns)); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -#ifndef ENABLE_ANDROID -RenameDataset::RenameDataset(const std::shared_ptr &input, const std::vector> &input_columns, - const std::vector> &output_columns) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), VectorCharToString(input_columns), - VectorCharToString(output_columns)); - - ir_node_ = std::static_pointer_cast(ds); - } -} -#endif - -RepeatDataset::RepeatDataset(const std::shared_ptr &input, int32_t count) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), count); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -ShuffleDataset::ShuffleDataset(const std::shared_ptr &input, int32_t buffer_size) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - // Pass in reshuffle_each_epoch with true - auto ds = std::make_shared(input->IRNode(), buffer_size, true); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -#ifndef ENABLE_ANDROID -SkipDataset::SkipDataset(const std::shared_ptr &input, int32_t count) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), count); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -TakeDataset::TakeDataset(const std::shared_ptr &input, int32_t count) { - if (input == nullptr) { - ir_node_ = nullptr; - } else { - auto ds = std::make_shared(input->IRNode(), count); - - ir_node_ = std::static_pointer_cast(ds); - } -} - -ZipDataset::ZipDataset(const std::vector> &datasets) { - std::vector> all_datasets; - (void)std::transform(datasets.begin(), datasets.end(), std::back_inserter(all_datasets), - [](const std::shared_ptr &dataset) -> std::shared_ptr { - return (dataset != nullptr) ? dataset->IRNode() : nullptr; - }); - auto ds = std::make_shared(all_datasets); - - ir_node_ = std::static_pointer_cast(ds); -} -#endif - -int64_t Dataset::GetBatchSize() { - int64_t batch_size = -1; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), -1); - RETURN_SECOND_IF_ERROR(tree_getters_->Init(this->IRNode()), -1); - RETURN_SECOND_IF_ERROR(tree_getters_->GetBatchSize(&batch_size), -1); - return batch_size; -} - -int64_t Dataset::GetRepeatCount() { - int64_t repeat_count = 0; - std::unique_ptr runtime_context = std::make_unique(); - RETURN_SECOND_IF_ERROR(runtime_context->Init(), -1); - RETURN_SECOND_IF_ERROR(tree_getters_->Init(this->IRNode()), 0); - RETURN_SECOND_IF_ERROR(tree_getters_->GetRepeatCount(&repeat_count), 0); - return repeat_count; -} - -std::shared_ptr Dataset::SetNumWorkers(int32_t num_workers) { - if (ir_node_ == nullptr || ir_node_->SetNumWorkers(num_workers) == nullptr) { - return nullptr; - } - return shared_from_this(); -} - -#ifndef ENABLE_ANDROID -std::shared_ptr Dataset::BuildSentencePieceVocabCharIF( - const std::vector> &col_names, int32_t vocab_size, float character_coverage, - SentencePieceModel model_type, const std::map, std::vector> ¶ms) { - auto vocab = std::make_shared(); - auto ds = std::make_shared(IRNode(), vocab, VectorCharToString(col_names), vocab_size, - character_coverage, model_type, UnorderedMapCharToString(params)); - - std::unique_ptr runtime_context = std::make_unique(); - Status rc = runtime_context->Init(); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildSentencePieceVocab: Failed to init runtime context. Error status: " << rc; - return nullptr; - } - - auto consumer = std::make_unique(); - BuildVocabConsumer *bv_consumer = consumer.get(); - if (bv_consumer == nullptr) { - MS_LOG(ERROR) << "BuildVocabConsumer: Failed to get bv_consumer."; - return nullptr; - } - rc = consumer->Init(ds); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildSentencePieceVocab: Failed to init consumer. Error status: " << rc; - return nullptr; - } - runtime_context->AssignConsumer(std::move(consumer)); - - // Run tree here to starting building SentencePieceVocab - rc = bv_consumer->Start(); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildSentencePieceVocab: Failed to start consumer. Error status: " << rc; - return nullptr; - } - return vocab; -} - -std::shared_ptr Dataset::BuildVocabCharIF(const std::vector> &columns, - const std::pair &freq_range, int64_t top_k, - const std::vector> &special_tokens, - bool special_first) { - auto vocab = std::make_shared(); - auto ds = std::make_shared(IRNode(), vocab, VectorCharToString(columns), freq_range, top_k, - VectorCharToString(special_tokens), special_first); - - std::unique_ptr runtime_context = std::make_unique(); - Status rc = runtime_context->Init(); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildVocab: Failed to init runtime context. Error status: " << rc; - return nullptr; - } - - auto consumer = std::make_unique(); - BuildVocabConsumer *bv_consumer = consumer.get(); - if (bv_consumer == nullptr) { - MS_LOG(ERROR) << "BuildVocabConsumer: Failed to get bv_consumer."; - return nullptr; - } - rc = consumer->Init(ds); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildVocab: Failed to init consumer. Error status: " << rc; - return nullptr; - } - runtime_context->AssignConsumer(std::move(consumer)); - - // Run tree here to starting building vocab - rc = bv_consumer->Start(); - if (rc.IsError()) { - MS_LOG(ERROR) << "BuildVocab: Failed to start consumer. Error status: " << rc; - return nullptr; - } - return vocab; -} -#endif - -std::shared_ptr Dataset::Batch(int32_t batch_size, bool drop_remainder) { - return std::make_shared(shared_from_this(), batch_size, drop_remainder); -} - -struct SchemaObj::Data { - int32_t num_rows_; - std::string dataset_type_; - std::string schema_file_; - nlohmann::json columns_; -}; - -SchemaObj::SchemaObj(const std::vector &schema_file) : data_(std::make_shared()) { - data_->schema_file_ = CharToString(schema_file); - data_->dataset_type_ = ""; - data_->num_rows_ = 0; -} - -// SchemaObj Init function -Status SchemaObj::Init() { - if (data_ != nullptr && !data_->schema_file_.empty()) { - std::string real_path; - RETURN_IF_NOT_OK(Path::RealPath(data_->schema_file_, real_path)); - Path schema_file(real_path); - CHECK_FAIL_RETURN_UNEXPECTED(schema_file.Exists(), - "The file " + data_->schema_file_ + " does not exist or permission denied!"); - - nlohmann::json js; - try { - std::ifstream in(real_path, std::ifstream::in); - in >> js; - CHECK_FAIL_RETURN_UNEXPECTED(js.find("columns") != js.end(), - "\"columns\" node is required in the schema json file."); - in.close(); - } catch (const std::exception &err) { - std::string err_msg = "Schema file failed to load: "; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return from_json(js); - } - return Status::OK(); -} - -// Function to add a column to schema with a mstype de_type and known shape -Status SchemaObj::add_column_char(const std::vector &name, mindspore::DataType de_type, - const std::vector &shape) { - DataType data_type = dataset::MSTypeToDEType(static_cast(de_type)); - return add_column_char(name, StringToChar(data_type.ToString()), shape); -} - -// Function to add a column to schema with a string de_type and known shape -Status SchemaObj::add_column_char(const std::vector &name, const std::vector &de_type, - const std::vector &shape) { - DataType data_type(CharToString(de_type)); - CHECK_FAIL_RETURN_UNEXPECTED(data_type != DataType::DE_UNKNOWN, "Type is unknown."); - - nlohmann::json new_column; - new_column["name"] = CharToString(name); - new_column["type"] = data_type.ToString(); - new_column["shape"] = shape; - new_column["rank"] = shape.size(); - - data_->columns_.push_back(new_column); - return Status::OK(); -} - -// Function to add a column to schema with a mstype de_type and without shape -Status SchemaObj::add_column_char(const std::vector &name, mindspore::DataType de_type) { - DataType data_type = dataset::MSTypeToDEType(static_cast(de_type)); - return add_column_char(name, StringToChar(data_type.ToString())); -} - -// Function to add a column to schema with a string de_type and without shape -Status SchemaObj::add_column_char(const std::vector &name, const std::vector &de_type) { - DataType data_type(CharToString(de_type)); - CHECK_FAIL_RETURN_UNEXPECTED(data_type != DataType::DE_UNKNOWN, "Type is unknown."); - - nlohmann::json new_column; - new_column["name"] = CharToString(name); - new_column["type"] = data_type.ToString(); - new_column["rank"] = 1; - - data_->columns_.push_back(new_column); - return Status::OK(); -} - -Status SchemaObj::schema_to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json json_file; - json_file["columns"] = data_->columns_; - std::string str_dataset_type_(data_->dataset_type_); - if (!str_dataset_type_.empty()) { - json_file["datasetType"] = str_dataset_type_; - } - - if (data_->num_rows_ > 0) { - json_file["numRows"] = data_->num_rows_; - } - *out_json = json_file; - return Status::OK(); -} - -std::vector SchemaObj::to_json_char() { - nlohmann::json json_file; - this->schema_to_json(&json_file); - return StringToChar(json_file.dump(2)); -} - -void SchemaObj::set_dataset_type(const std::string &dataset_type) { data_->dataset_type_ = dataset_type; } - -void SchemaObj::set_num_rows(int32_t num_rows) { data_->num_rows_ = num_rows; } - -int32_t SchemaObj::get_num_rows() const { return data_->num_rows_; } - -Status SchemaObj::parse_column(nlohmann::json columns) { - std::string name, de_type; - std::vector shape; - - data_->columns_.clear(); - if (columns.type() == nlohmann::json::value_t::array) { - // reference to python list - for (auto column : columns) { - auto key_name = column.find("name"); - if (key_name == column.end()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Column's name is missing"); - } - name = *key_name; - - auto key_type = column.find("type"); - if (key_type == column.end()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Column's type is missing"); - } - de_type = *key_type; - - shape.clear(); - auto key_shape = column.find("shape"); - if (key_shape != column.end()) { - shape.insert(shape.end(), (*key_shape).begin(), (*key_shape).end()); - } - RETURN_IF_NOT_OK(add_column(name, de_type, shape)); - } - } else if (columns.type() == nlohmann::json::value_t::object) { - for (const auto &it_child : columns.items()) { - name = it_child.key(); - auto key_type = it_child.value().find("type"); - if (key_type == it_child.value().end()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Column's type is missing"); - } - de_type = *key_type; - - shape.clear(); - auto key_shape = it_child.value().find("shape"); - if (key_shape != it_child.value().end()) { - shape.insert(shape.end(), (*key_shape).begin(), (*key_shape).end()); - } - - RETURN_IF_NOT_OK(add_column(name, de_type, shape)); - } - } else { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("columns must be dict or list, columns contain name, type, shape(optional)."); - } - return Status::OK(); -} - -Status SchemaObj::from_json(nlohmann::json json_obj) { - for (const auto &it_child : json_obj.items()) { - if (it_child.key() == "datasetType") { - std::string str_dataset_type_ = it_child.value(); - data_->dataset_type_ = str_dataset_type_; - } else if (it_child.key() == "numRows") { - data_->num_rows_ = it_child.value(); - } else if (it_child.key() == "columns") { - RETURN_IF_NOT_OK(parse_column(it_child.value())); - } else { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Unknown field " + it_child.key()); - } - } - if (data_->columns_.empty()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Columns are missing."); - } - if (data_->num_rows_ < 0) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("numRows must be greater than or equal to 0"); - } - - return Status::OK(); -} - -Status SchemaObj::FromJSONStringCharIF(const std::vector &json_string) { - try { - nlohmann::json js = nlohmann::json::parse(CharToString(json_string)); - CHECK_FAIL_RETURN_UNEXPECTED(js.find("columns") != js.end(), - "\"columns\" node is required in the schema json JSON."); - RETURN_IF_NOT_OK(from_json(js)); - } catch (const std::exception &err) { - std::string err_msg = "FromJSONString: JSON string failed to parse: "; - err_msg += err.what(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status SchemaObj::ParseColumnStringCharIF(const std::vector &json_string) { - try { - nlohmann::json js = nlohmann::json::parse(CharToString(json_string)); - RETURN_IF_NOT_OK(parse_column(js)); - } catch (const std::exception &err) { - std::string err_msg = "ParseColumnString: JSON string failed to parse: "; - err_msg += err.what(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// OTHER FUNCTIONS - -#ifndef ENABLE_ANDROID - -std::shared_ptr CreateDatasetCacheCharIF(session_id_type id, uint64_t mem_sz, bool spill, - const std::optional> &hostname, - const std::optional &port, - const std::optional &num_connections, - const std::optional &prefetch_sz) { - auto cache = std::make_shared(id, mem_sz, spill, hostname, port, num_connections, prefetch_sz); - return cache; -} - -AGNewsDataset::AGNewsDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), num_samples, shuffle, CharToString(usage), - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} -#endif - -AlbumDataset::AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(data_schema), - VectorCharToString(column_names), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -AlbumDataset::AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(data_schema), - VectorCharToString(column_names), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -AlbumDataset::AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(data_schema), - VectorCharToString(column_names), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -#ifndef ENABLE_ANDROID -AmazonReviewDataset::AmazonReviewDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Caltech256Dataset::Caltech256Dataset(const std::vector &dataset_dir, bool decode, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Caltech256Dataset::Caltech256Dataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Caltech256Dataset::Caltech256Dataset(const std::vector &dataset_dir, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CelebADataset::CelebADataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, bool decode, - const std::set> &extensions, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, decode, - SetCharToString(extensions), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CelebADataset::CelebADataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, bool decode, const std::set> &extensions, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, decode, - SetCharToString(extensions), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CelebADataset::CelebADataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, bool decode, - const std::set> &extensions, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, decode, - SetCharToString(extensions), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar10Dataset::Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar10Dataset::Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar10Dataset::Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar100Dataset::Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar100Dataset::Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Cifar100Dataset::Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CityscapesDataset::CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(quality_mode), - CharToString(task), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CityscapesDataset::CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(quality_mode), - CharToString(task), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CityscapesDataset::CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(quality_mode), - CharToString(task), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CLUEDataset::CLUEDataset(const std::vector> &dataset_files, const std::vector &task, - const std::vector &usage, int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache) { - auto ds = std::make_shared(VectorCharToString(dataset_files), CharToString(task), CharToString(usage), - num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CMUArcticDataset::CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CMUArcticDataset::CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CMUArcticDataset::CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CocoDataset::CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache, const bool &extra_metadata) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), CharToString(task), - decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -CocoDataset::CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, const Sampler *sampler, - const std::shared_ptr &cache, const bool &extra_metadata) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), CharToString(task), - decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -CocoDataset::CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache, - const bool &extra_metadata) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), CharToString(task), - decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -CoNLL2000Dataset::CoNLL2000Dataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -CSVDataset::CSVDataset(const std::vector> &dataset_files, char field_delim, - const std::vector> &column_defaults, - const std::vector> &column_names, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache) { - auto ds = - std::make_shared(VectorCharToString(dataset_files), field_delim, column_defaults, - VectorCharToString(column_names), num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -DBpediaDataset::DBpediaDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -DIV2KDataset::DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &downgrade, int32_t scale, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(downgrade), scale, - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -DIV2KDataset::DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &downgrade, int32_t scale, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(downgrade), scale, - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -DIV2KDataset::DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &downgrade, int32_t scale, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), CharToString(downgrade), scale, - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -EMnistDataset::EMnistDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -EMnistDataset::EMnistDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -EMnistDataset::EMnistDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -EnWik9Dataset::EnWik9Dataset(const std::vector &dataset_dir, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FakeImageDataset::FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, - int32_t base_seed, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(num_images, image_size, num_classes, base_seed, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FakeImageDataset::FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, - int32_t base_seed, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(num_images, image_size, num_classes, base_seed, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FakeImageDataset::FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, - int32_t base_seed, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(num_images, image_size, num_classes, base_seed, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FashionMnistDataset::FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FashionMnistDataset::FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FashionMnistDataset::FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FlickrDataset::FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FlickrDataset::FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - bool decode, const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -FlickrDataset::FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(annotation_file), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Food101Dataset::Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Food101Dataset::Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Food101Dataset::Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -GTZANDataset::GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -GTZANDataset::GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -GTZANDataset::GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ImageFolderDataset::ImageFolderDataset(const std::vector &dataset_dir, bool decode, - const std::shared_ptr &sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache) { - // This arg exists in ImageFolderOp, but not externalized (in Python API). The default value is false. - bool recursive = false; - - // Create logical representation of ImageFolderDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, recursive, - SetCharToString(extensions), MapCharToString(class_indexing), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ImageFolderDataset::ImageFolderDataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache) { - // This arg exists in ImageFolderOp, but not externalized (in Python API). The default value is false. - bool recursive = false; - - // Create logical representation of ImageFolderDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, recursive, - SetCharToString(extensions), MapCharToString(class_indexing), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ImageFolderDataset::ImageFolderDataset(const std::vector &dataset_dir, bool decode, - const std::reference_wrapper &sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache) { - // This arg exists in ImageFolderOp, but not externalized (in Python API). The default value is false. - bool recursive = false; - - // Create logical representation of ImageFolderDataset. - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, recursive, - SetCharToString(extensions), MapCharToString(class_indexing), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -IMDBDataset::IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - // Create logical representation of IMDBDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -IMDBDataset::IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - // Create logical representation of IMDBDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -IMDBDataset::IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - // Create logical representation of IMDBDataset. - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -IWSLT2016Dataset::IWSLT2016Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, - const std::vector &valid_set, const std::vector &test_set, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), - VectorCharToString(language_pair), CharToString(valid_set), - CharToString(test_set), num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -IWSLT2017Dataset::IWSLT2017Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), VectorCharToString(language_pair), - num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KITTIDataset::KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KITTIDataset::KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KITTIDataset::KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KMnistDataset::KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KMnistDataset::KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -KMnistDataset::KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LFWDataset::LFWDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::vector &image_set, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - // Create logical representation of LFWDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - CharToString(image_set), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LFWDataset::LFWDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::vector &image_set, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - // Create logical representation of LFWDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - CharToString(image_set), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LFWDataset::LFWDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::vector &image_set, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - // Create logical representation of LFWDataset. - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - CharToString(image_set), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LibriTTSDataset::LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LibriTTSDataset::LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LibriTTSDataset::LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LJSpeechDataset::LJSpeechDataset(const std::vector &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LJSpeechDataset::LJSpeechDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LJSpeechDataset::LJSpeechDataset(const std::vector &dataset_dir, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LSUNDataset::LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - // Create logical representation of LSUNDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), VectorCharToString(classes), - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LSUNDataset::LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - // Create logical representation of LSUNDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), VectorCharToString(classes), - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -LSUNDataset::LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - // Create logical representation of LSUNDataset. - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), VectorCharToString(classes), - decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ManifestDataset::ManifestDataset(const std::vector &dataset_file, const std::vector &usage, - const std::shared_ptr &sampler, - const std::map, int32_t> &class_indexing, bool decode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_file), CharToString(usage), sampler_obj, - MapCharToString(class_indexing), decode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ManifestDataset::ManifestDataset(const std::vector &dataset_file, const std::vector &usage, - const Sampler *sampler, const std::map, int32_t> &class_indexing, - bool decode, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_file), CharToString(usage), sampler_obj, - MapCharToString(class_indexing), decode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -ManifestDataset::ManifestDataset(const std::vector &dataset_file, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::map, int32_t> &class_indexing, bool decode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_file), CharToString(usage), sampler_obj, - MapCharToString(class_indexing), decode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector &dataset_file, - const std::vector> &columns_list, - const std::shared_ptr &sampler, const nlohmann::json *padded_sample, - int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - auto ds = std::make_shared(CharToString(dataset_file), VectorCharToString(columns_list), sampler_obj, - sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector &dataset_file, - const std::vector> &columns_list, const Sampler *sampler, - const nlohmann::json *padded_sample, int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - auto ds = std::make_shared(CharToString(dataset_file), VectorCharToString(columns_list), sampler_obj, - sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector &dataset_file, - const std::vector> &columns_list, - const std::reference_wrapper &sampler, const nlohmann::json *padded_sample, - int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - - auto ds = std::make_shared(CharToString(dataset_file), VectorCharToString(columns_list), sampler_obj, - sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, - const std::shared_ptr &sampler, const nlohmann::json *padded_sample, - int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - - auto ds = std::make_shared(VectorCharToString(dataset_files), VectorCharToString(columns_list), - sampler_obj, sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, const Sampler *sampler, - const nlohmann::json *padded_sample, int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - - auto ds = std::make_shared(VectorCharToString(dataset_files), VectorCharToString(columns_list), - sampler_obj, sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MindDataDataset::MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, - const std::reference_wrapper &sampler, const nlohmann::json *padded_sample, - int64_t num_padded, ShuffleMode shuffle_mode, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - nlohmann::json sample = nullptr; - if (padded_sample) { - sample = *padded_sample; - } - auto ds = std::make_shared(VectorCharToString(dataset_files), VectorCharToString(columns_list), - sampler_obj, sample, num_padded, shuffle_mode, cache); - ir_node_ = std::static_pointer_cast(ds); -} -#endif - -MnistDataset::MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MnistDataset::MnistDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -MnistDataset::MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -#ifndef ENABLE_ANDROID -Multi30kDataset::Multi30kDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), VectorCharToString(language_pair), - num_samples, shuffle, num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -OmniglotDataset::OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - // Create logical representation of OmniglotDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), background, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -OmniglotDataset::OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - // Create logical representation of OmniglotDataset. - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), background, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -OmniglotDataset::OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - // Create logical representation of OmniglotDataset. - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), background, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -PennTreebankDataset::PennTreebankDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -PhotoTourDataset::PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -PhotoTourDataset::PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -PhotoTourDataset::PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, - const std::vector &usage, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(name), CharToString(usage), - sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Places365Dataset::Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, - const bool small, const bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), small, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Places365Dataset::Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, - const bool small, const bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), small, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -Places365Dataset::Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, - const bool small, const bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), small, decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -QMnistDataset::QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), compat, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -QMnistDataset::QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), compat, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -QMnistDataset::QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), compat, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SemeionDataset::SemeionDataset(const std::vector &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SemeionDataset::SemeionDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SemeionDataset::SemeionDataset(const std::vector &dataset_dir, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SQuADDataset::SQuADDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SST2Dataset::SST2Dataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, num_shards, - shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -TedliumDataset::TedliumDataset(const std::vector &dataset_dir, const std::vector &release, - const std::vector &usage, const std::vector &extensions, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(release), CharToString(usage), - CharToString(extensions), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -TedliumDataset::TedliumDataset(const std::vector &dataset_dir, const std::vector &release, - const std::vector &usage, const std::vector &extensions, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(release), CharToString(usage), - CharToString(extensions), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -TedliumDataset::TedliumDataset(const std::vector &dataset_dir, const std::vector &release, - const std::vector &usage, const std::vector &extensions, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(release), CharToString(usage), - CharToString(extensions), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -STL10Dataset::STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -STL10Dataset::STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -STL10Dataset::STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SUN397Dataset::SUN397Dataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SUN397Dataset::SUN397Dataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SUN397Dataset::SUN397Dataset(const std::vector &dataset_dir, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -TextFileDataset::TextFileDataset(const std::vector> &dataset_files, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(VectorCharToString(dataset_files), num_samples, shuffle, num_shards, - shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -USPSDataset::USPSDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, num_shards, - shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -VOCDataset::VOCDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::map, int32_t> &class_indexing, - bool decode, const std::shared_ptr &sampler, const std::shared_ptr &cache, - bool extra_metadata) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - MapCharToString(class_indexing), decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -VOCDataset::VOCDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::map, int32_t> &class_indexing, - bool decode, const Sampler *sampler, const std::shared_ptr &cache, - bool extra_metadata) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - MapCharToString(class_indexing), decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -VOCDataset::VOCDataset(const std::vector &dataset_dir, const std::vector &task, - const std::vector &usage, const std::map, int32_t> &class_indexing, - bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache, bool extra_metadata) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(task), CharToString(usage), - MapCharToString(class_indexing), decode, sampler_obj, cache, extra_metadata); - ir_node_ = std::static_pointer_cast(ds); -} - -WikiTextDataset::WikiTextDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -RandomDataDataset::RandomDataDataset(const int32_t &total_rows, std::shared_ptr schema, - const std::vector> &columns_list, - const std::shared_ptr &cache) { - auto ds = std::make_shared(total_rows, std::move(schema), VectorCharToString(columns_list), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -RandomDataDataset::RandomDataDataset(const int32_t &total_rows, const std::vector &schema_path, - const std::vector> &columns_list, - const std::shared_ptr &cache) { - auto ds = - std::make_shared(total_rows, CharToString(schema_path), VectorCharToString(columns_list), cache); - ir_node_ = std::static_pointer_cast(ds); -} - -RenderedSST2Dataset::RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, - bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -RenderedSST2Dataset::RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, - bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -RenderedSST2Dataset::RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, - bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = - std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SBUDataset::SBUDataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SBUDataset::SBUDataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SBUDataset::SBUDataset(const std::vector &dataset_dir, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SogouNewsDataset::SogouNewsDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SpeechCommandsDataset::SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SpeechCommandsDataset::SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -SpeechCommandsDataset::SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -TFRecordDataset::TFRecordDataset(const std::vector> &dataset_files, const std::vector &schema, - const std::vector> &columns_list, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, bool shard_equal_rows, - const std::shared_ptr &cache, - const std::vector &compression_type) { - auto ds = std::make_shared(VectorCharToString(dataset_files), CharToString(schema), - VectorCharToString(columns_list), num_samples, shuffle, num_shards, shard_id, - shard_equal_rows, cache, CharToString(compression_type)); - ir_node_ = std::static_pointer_cast(ds); -} - -TFRecordDataset::TFRecordDataset(const std::vector> &dataset_files, - const std::shared_ptr &schema, - const std::vector> &columns_list, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, bool shard_equal_rows, - const std::shared_ptr &cache, - const std::vector &compression_type) { - auto ds = std::make_shared(VectorCharToString(dataset_files), schema, VectorCharToString(columns_list), - num_samples, shuffle, num_shards, shard_id, shard_equal_rows, cache, - CharToString(compression_type)); - ir_node_ = std::static_pointer_cast(ds); -} - -UDPOSDataset::UDPOSDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -WIDERFaceDataset::WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -WIDERFaceDataset::WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -WIDERFaceDataset::WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), decode, sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -YahooAnswersDataset::YahooAnswersDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -YelpReviewDataset::YelpReviewDataset(const std::vector &dataset_dir, const std::vector &usage, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) { - auto ds = std::make_shared(CharToString(dataset_dir), CharToString(usage), num_samples, shuffle, - num_shards, shard_id, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -YesNoDataset::YesNoDataset(const std::vector &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -YesNoDataset::YesNoDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler ? sampler->Parse() : nullptr; - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} - -YesNoDataset::YesNoDataset(const std::vector &dataset_dir, const std::reference_wrapper &sampler, - const std::shared_ptr &cache) { - auto sampler_obj = sampler.get().Parse(); - auto ds = std::make_shared(CharToString(dataset_dir), sampler_obj, cache); - ir_node_ = std::static_pointer_cast(ds); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/execute.cc b/mindspore-lite/minddata/dataset/api/execute.cc deleted file mode 100644 index c8cd9f76f..000000000 --- a/mindspore-lite/minddata/dataset/api/execute.cc +++ /dev/null @@ -1,747 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/include/dataset/execute.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/de_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/core/ascend_resource.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h" -#include "mindspore-lite/src/common/file_utils.h" - -namespace platform = mindspore::lite; - -namespace mindspore { -namespace dataset { -using json = nlohmann::json; - -struct Execute::ExtraInfo { - std::multimap> aipp_cfg_; - bool init_with_shared_ptr_ = true; // Initial execute object with shared_ptr as default -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - std::multimap op2para_map_ = {{vision::kDvppCropJpegOperation, "size"}, - {vision::kDvppDecodeResizeOperation, "size"}, - {vision::kDvppDecodeResizeCropOperation, "crop_size"}, - {vision::kDvppDecodeResizeCropOperation, "resize_size"}, - {vision::kDvppNormalizeOperation, "mean"}, - {vision::kDvppNormalizeOperation, "std"}, - {vision::kDvppResizeJpegOperation, "size"}}; -#endif -}; - -Status Execute::InitResource(MapTargetDevice device_type, uint32_t device_id) { -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) && !defined(ENABLE_ANDROID) - // set num threads of opencv for eager mode - int32_t thread_num = get_nprocs(); - if (thread_num == 0) { - std::string err_msg = "Invalid thread number, got 0."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - constexpr int32_t max_cv_threads_cnt = 8; - cv::setNumThreads(thread_num > max_cv_threads_cnt ? max_cv_threads_cnt : thread_num); -#endif - - if (device_type == MapTargetDevice::kAscend310) { - MS_LOG(INFO) << "InitResource for Ascend310"; -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - if (device_resource_ == nullptr) { - device_resource_ = std::make_shared(); - Status rc = device_resource_->InitResource(device_id); - if (!rc.IsOk()) { - device_resource_ = nullptr; - std::string err_msg = "Initialize Ascend310 resource fail"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - MS_LOG(INFO) << "Ascend310 context resource had been initialized."; - } -#endif - device_type_ = device_type; - } - return Status::OK(); -} - -Execute::Execute(const std::shared_ptr &op, MapTargetDevice device_type, uint32_t device_id) { - (void)ops_.emplace_back(op); - device_type_ = device_type; - info_ = std::make_shared(); - - // Ascend910B - if (op->Type() == MapTargetDevice::kAscend910B) { - // Update the device_type - device_type = MapTargetDevice::kAscend910B; - MS_LOG(INFO) << "Update device type: " << std::to_string(static_cast(device_type)); - } - - (void)InitResource(device_type, device_id); -} - -Execute::Execute(const std::shared_ptr &op, MapTargetDevice device_type, uint32_t device_id) { - // Initialize the op and other context - (void)transforms_.emplace_back(op); - - info_ = std::make_shared(); - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -Execute::Execute(const std::reference_wrapper &op, MapTargetDevice device_type, uint32_t device_id) { - // Initialize the transforms_ and other context - std::shared_ptr operation = op.get().Parse(); - (void)ops_.emplace_back(std::move(operation)); - - info_ = std::make_shared(); - info_->init_with_shared_ptr_ = false; - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -// Execute function for the example case: auto decode(new vision::Decode()); -Execute::Execute(TensorTransform *op, MapTargetDevice device_type, uint32_t device_id) { - // Initialize the transforms_ and other context - (void)ops_.emplace_back(op->Parse()); - - info_ = std::make_shared(); - info_->init_with_shared_ptr_ = false; - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -Execute::Execute(const std::vector> &ops, MapTargetDevice device_type, - uint32_t device_id) - : ops_(ops), device_type_(device_type) { - info_ = std::make_shared(); - (void)InitResource(device_type, device_id); -} - -Execute::Execute(const std::vector> &ops, MapTargetDevice device_type, - uint32_t device_id) { - // Initialize the transforms_ and other context - transforms_ = ops; - - info_ = std::make_shared(); - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -Execute::Execute(const std::vector> &ops, MapTargetDevice device_type, - uint32_t device_id) { - // Initialize the transforms_ and other context - if (device_type == MapTargetDevice::kCpu) { - (void)std::transform( - ops.begin(), ops.end(), std::back_inserter(ops_), - [](TensorTransform &operation) -> std::shared_ptr { return operation.Parse(); }); - } else { - for (auto &op : ops) { - (void)ops_.emplace_back(op.get().Parse(device_type)); - } - } - - info_ = std::make_shared(); - info_->init_with_shared_ptr_ = false; - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -// Execute function for the example vector case: auto decode(new vision::Decode()); -Execute::Execute(const std::vector &ops, MapTargetDevice device_type, uint32_t device_id) { - // Initialize the transforms_ and other context - (void)std::transform( - ops.begin(), ops.end(), std::back_inserter(ops_), - [](TensorTransform *operation) -> std::shared_ptr { return operation->Parse(); }); - - info_ = std::make_shared(); - info_->init_with_shared_ptr_ = false; - device_type_ = device_type; - (void)InitResource(device_type, device_id); -} - -Execute::~Execute() { - if (device_type_ == MapTargetDevice::kAscend310) { - if (device_resource_) { - auto rc = device_resource_->FinalizeResource(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Device resource release failed, error msg is " << rc; - } - } else { - MS_LOG(ERROR) << "Device resource is nullptr which is illegal under case Ascend310"; - } - } -} - -Status Execute::UpdateOperation(const std::shared_ptr &op) { - // clear the ops_ first - ops_.clear(); - (void)ops_.emplace_back(op); - return Status::OK(); -} - -Status Execute::BuildTransforms(std::vector> *transforms_rt) { - // Parse TensorTransform transforms_ into TensorOperation ops_ - if (info_->init_with_shared_ptr_) { - RETURN_IF_NOT_OK(ParseTransforms()); - info_->init_with_shared_ptr_ = false; - } - CHECK_FAIL_RETURN_UNEXPECTED(!ops_.empty(), "Input TensorOperation should be provided."); - - std::map env_list = { - {MapTargetDevice::kCpu, "kCpu"}, {MapTargetDevice::kGpu, "kGpu"}, {MapTargetDevice::kAscend310, "kAscend310"}}; - - // Validate and build runtime ops - for (size_t i = 0; i < ops_.size(); i++) { - if (ops_[i] == nullptr) { - std::string err_msg = "Input TensorOperation[" + std::to_string(i) + - "] is unsupported on your input device:" + env_list.at(device_type_); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - RETURN_IF_NOT_OK(ops_[i]->ValidateParams()); - (void)transforms_rt->emplace_back(ops_[i]->Build()); - } - - // if transforms_rt[0] is ComposeOp and all dvpp, extract all the ops from ComposeOp - if ((*transforms_rt)[0]->IsDvppOp() && (*transforms_rt)[0]->Name() == kComposeOp) { - if (dynamic_cast((*transforms_rt)[0].get())->IsMixedOps()) { - std::string err_msg = "Currently, it is not supported to mix DVPP transforms with CPU transforms in Compose."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - *transforms_rt = std::move(dynamic_cast((*transforms_rt)[0].get())->GetOps()); - MS_LOG(INFO) << "Extract the ComposeOp to " << std::to_string((*transforms_rt).size()) << " ops."; - } - - return Status::OK(); -} - -Status Execute::operator()(const mindspore::MSTensor &input, mindspore::MSTensor *output) { - // Validate input tensor - RETURN_UNEXPECTED_IF_NULL(output); - CHECK_FAIL_RETURN_UNEXPECTED(input.DataSize() > 0, "Input Tensor has no data."); - CHECK_FAIL_RETURN_UNEXPECTED(ValidateDevice(), "Device Type should be 'Ascend310' or 'CPU'."); - std::vector> transforms_rt; - CHECK_FAIL_RETURN_UNEXPECTED(BuildTransforms(&transforms_rt), "Building Transform ops failed!"); - - if (device_type_ == MapTargetDevice::kCpu) { - // Convert mindspore::Tensor to dataset::Tensor - std::shared_ptr de_tensor; - Status rc = dataset::Tensor::CreateFromMemory(dataset::TensorShape(input.Shape()), - MSTypeToDEType(static_cast(input.DataType())), - (const uchar *)(input.Data().get()), input.DataSize(), &de_tensor); - if (rc.IsError()) { - MS_LOG(ERROR) << rc; - return rc; - } - - // Apply transforms on tensor - for (auto &t : transforms_rt) { - TensorRow de_tensor_row; - TensorRow de_output_row; - de_tensor_row.push_back(de_tensor); - de_output_row.resize(1); - Status rc_ = t->Compute(de_tensor_row, &de_output_row); - if (rc_.IsError()) { - MS_LOG(ERROR) << rc_; - return rc_; - } - - // For next transform - de_tensor = std::move(de_output_row[0]); - } - - // Convert dataset::Tensor to mindspore::Tensor - if (!de_tensor->HasData()) { - std::stringstream ss; - ss << "Transformation returned an empty tensor with shape " << de_tensor->shape(); - RETURN_STATUS_UNEXPECTED(ss.str()); - } - *output = mindspore::MSTensor(std::make_shared(de_tensor)); - } else if (device_type_ == - MapTargetDevice::kAscend310) { // Ascend310 case, where we must set Ascend resource on each operations -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - CHECK_FAIL_RETURN_UNEXPECTED(device_resource_, "Device resource is nullptr which is illegal under case Ascend310."); - // Sink data from host into device - std::shared_ptr device_input; - RETURN_IF_NOT_OK(device_resource_->Sink(input, &device_input)); - - for (auto &t : transforms_rt) { - // Initialize AscendResource for each operations - std::shared_ptr device_output; - RETURN_IF_NOT_OK(t->SetAscendResource(device_resource_)); - - RETURN_IF_NOT_OK(t->Compute(device_input, &device_output)); - - // For next transform - device_input = std::move(device_output); - } - CHECK_FAIL_RETURN_UNEXPECTED(device_input->HasDeviceData(), "Apply transform failed, output tensor has no data."); - - *output = mindspore::MSTensor(std::make_shared(device_input, true)); -#endif - } else { - std::string err_msg = "Your input device is not supported. (Option: CPU or Ascend310)"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status Execute::operator()(const std::vector &input_tensor_list, std::vector *output_tensor_list) { - // Validate input tensor - RETURN_UNEXPECTED_IF_NULL(output_tensor_list); - CHECK_FAIL_RETURN_UNEXPECTED(!input_tensor_list.empty(), "Input Tensor is not valid."); - output_tensor_list->clear(); - for (auto &tensor : input_tensor_list) { - CHECK_FAIL_RETURN_UNEXPECTED(tensor.DataSize() > 0, "Input Tensor has no data."); - } - CHECK_FAIL_RETURN_UNEXPECTED(ValidateDevice(), "Device Type should be 'Ascend310' or 'CPU'."); - std::vector> transforms_rt; - CHECK_FAIL_RETURN_UNEXPECTED(BuildTransforms(&transforms_rt), "Building Transform ops failed!"); - - if (device_type_ == MapTargetDevice::kCpu) { // Case CPU - TensorRow de_tensor_list; - for (auto &tensor : input_tensor_list) { - std::shared_ptr de_tensor; - Status rc = dataset::Tensor::CreateFromMemory( - dataset::TensorShape(tensor.Shape()), MSTypeToDEType(static_cast(tensor.DataType())), - (const uchar *)(tensor.Data().get()), tensor.DataSize(), &de_tensor); - if (rc.IsError()) { - MS_LOG(ERROR) << rc; - RETURN_IF_NOT_OK(rc); - } - (void)de_tensor_list.emplace_back(std::move(de_tensor)); - } - // Apply transforms on tensor - for (auto &t : transforms_rt) { - TensorRow de_output_list; - RETURN_IF_NOT_OK(t->Compute(de_tensor_list, &de_output_list)); - // For next transform - de_tensor_list = std::move(de_output_list); - } - int32_t idx = 0; - for (auto &tensor : de_tensor_list) { - if (!tensor->HasData()) { - std::stringstream ss; - ss << "Transformation returned an empty tensor at location " << idx << ". "; - ss << "The shape of the tensor is " << tensor->shape(); - MS_LOG(WARNING) << ss.str(); - } - auto ms_tensor = mindspore::MSTensor(std::make_shared(tensor)); - (void)output_tensor_list->emplace_back(ms_tensor); - ++idx; - } - CHECK_FAIL_RETURN_UNEXPECTED(!output_tensor_list->empty(), "Output Tensor is not valid."); - } else if (device_type_ == - MapTargetDevice::kAscend310) { // Ascend310 case, where we must set Ascend resource on each operations - CHECK_FAIL_RETURN_UNEXPECTED(device_resource_, "Device resource is nullptr which is illegal under case Ascend310."); - for (auto &input_tensor : input_tensor_list) { - // Sink each data from host into device - std::shared_ptr device_input; - RETURN_IF_NOT_OK(device_resource_->Sink(input_tensor, &device_input)); - - for (auto &t : transforms_rt) { - std::shared_ptr device_output; - RETURN_IF_NOT_OK(t->SetAscendResource(device_resource_)); - - RETURN_IF_NOT_OK(t->Compute(device_input, &device_output)); - - // For next transform - device_input = std::move(device_output); - } - CHECK_FAIL_RETURN_UNEXPECTED(device_input->HasDeviceData(), "Apply transform failed, output tensor has no data"); - // Due to the limitation of Ascend310 memory, we have to pop every data onto host memory - // So the speed of this batch method is slower than solo mode - std::shared_ptr host_output; - RETURN_IF_NOT_OK(device_resource_->Pop(device_input, &host_output)); - - auto ms_tensor = mindspore::MSTensor(std::make_shared(host_output)); - (void)output_tensor_list->emplace_back(ms_tensor); - // Release the data on the device because we have copied one piece onto host - RETURN_IF_NOT_OK(device_resource_->DeviceDataRelease()); - } - CHECK_FAIL_RETURN_UNEXPECTED(!output_tensor_list->empty(), "Output Tensor vector is empty."); - } else { - std::string err_msg = "Your input device is not supported. (Option: CPU or Ascend310)"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status PyExecute::operator()(const std::vector> &input_tensor_list, - std::vector> *out) { - // Validate input tensor - CHECK_FAIL_RETURN_UNEXPECTED(!input_tensor_list.empty(), "Input Tensor is not valid."); - RETURN_UNEXPECTED_IF_NULL(out); - out->clear(); - for (auto &tensor : input_tensor_list) { - CHECK_FAIL_RETURN_UNEXPECTED(tensor->Size() > 0, "Input Tensor has no data."); - } - CHECK_FAIL_RETURN_UNEXPECTED(ValidateDevice(), "Device Type should be 'CPU'."); - - std::vector> transforms_rt; - CHECK_FAIL_RETURN_UNEXPECTED(BuildTransforms(&transforms_rt), "Building Transform ops failed!"); - - if (!transforms_rt[0]->IsDvppOp()) { - TensorRow de_tensor_list(input_tensor_list); - - // Apply transforms on tensor - for (auto &t : transforms_rt) { - TensorRow de_output_list; - RETURN_IF_NOT_OK(t->Compute(de_tensor_list, &de_output_list)); - // For next transform - de_tensor_list = std::move(de_output_list); - } - *out = std::move(de_tensor_list.getRow()); - CHECK_FAIL_RETURN_UNEXPECTED(!out->empty(), "Output Tensor is not valid."); - } else { - std::string err_msg = "Your input device is not supported. (Option: CPU or Ascend910B)"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -std::vector AippSizeFilter(const std::vector &resize_para, const std::vector &crop_para) { - std::vector aipp_size; - - // Special condition where (no Crop and no Resize) or (no Crop and resize with fixed ratio) will lead to dynamic input - if ((resize_para.empty() || resize_para.size() == 1) && crop_para.empty()) { - aipp_size = {0, 0}; - MS_LOG(WARNING) << "Dynamic input shape is not supported, incomplete aipp config file will be generated. Please " - "checkout your TensorTransform input, both src_image_size_h and src_image_size will be 0."; - return aipp_size; - } - - if (resize_para.empty()) { // If only Crop operation exists - aipp_size = crop_para; - } else if (crop_para.empty()) { // If only Resize operation with 2 parameters exists - aipp_size = resize_para; - } else { // If both of them exist - if (resize_para.size() == 1) { - aipp_size = crop_para; - } else { - aipp_size = - *min_element(resize_para.begin(), resize_para.end()) < *min_element(crop_para.begin(), crop_para.end()) - ? resize_para - : crop_para; - } - } - - aipp_size[0] = DVPP_ALIGN_UP(aipp_size[0], VPC_HEIGHT_ALIGN); // H - aipp_size[1] = DVPP_ALIGN_UP(aipp_size[1], VPC_WIDTH_ALIGN); // W - return aipp_size; -} - -std::vector AippMeanFilter(const std::vector &normalize_para) { - std::vector aipp_mean; - if (normalize_para.size() == 6) { // If Normalize operation exist - std::transform(normalize_para.begin(), normalize_para.begin() + 3, std::back_inserter(aipp_mean), - [](uint32_t i) { return static_cast(i / 10000); }); - } else { - aipp_mean = {0, 0, 0}; - } - return aipp_mean; -} - -std::vector AippStdFilter(const std::vector &normalize_para) { - std::vector aipp_std; - if (normalize_para.size() == 6) { // If Normalize operation exist - auto zeros = std::find(std::begin(normalize_para), std::end(normalize_para), 0); - if (zeros == std::end(normalize_para)) { - if (std::any_of(normalize_para.begin() + 3, normalize_para.end(), [](uint32_t i) { return i == 0; })) { - MS_LOG(ERROR) << "value in normalize para got 0."; - return {}; - } - std::transform(normalize_para.begin() + 3, normalize_para.end(), std::back_inserter(aipp_std), - [](uint32_t i) { return 10000 / static_cast(i); }); - } else { // If 0 occurs in std vector - MS_LOG(WARNING) << "Detect 0 in std vector, please verify your input."; - aipp_std = {1.0, 1.0, 1.0}; - } - } else { - aipp_std = {1.0, 1.0, 1.0}; - } - return aipp_std; -} - -Status AippInfoCollection(std::map *aipp_options, const std::vector &aipp_size, - const std::vector &aipp_mean, const std::vector &aipp_std) { - RETURN_UNEXPECTED_IF_NULL(aipp_options); - // Several aipp config parameters - aipp_options->insert(std::make_pair("related_input_rank", "0")); - aipp_options->insert(std::make_pair("src_image_size_w", std::to_string(aipp_size[1]))); - aipp_options->insert(std::make_pair("src_image_size_h", std::to_string(aipp_size[0]))); - aipp_options->insert(std::make_pair("crop", "false")); - aipp_options->insert(std::make_pair("input_format", "YUV420SP_U8")); - aipp_options->insert(std::make_pair("aipp_mode", "static")); - aipp_options->insert(std::make_pair("csc_switch", "true")); - aipp_options->insert(std::make_pair("rbuv_swap_switch", "false")); - // Y = AX + b, this part is A - std::vector color_space_matrix = {256, 0, 359, 256, -88, -183, 256, 454, 0}; - int count = 0; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - std::string key_word = "matrix_r" + std::to_string(i) + "c" + std::to_string(j); - aipp_options->insert(std::make_pair(key_word, std::to_string(color_space_matrix[count]))); - ++count; - } - } - // This part is b - std::vector color_space_bias = {0, 128, 128}; - for (int i = 0; i < 3; i++) { - std::string key_word = "input_bias_" + std::to_string(i); - aipp_options->insert(std::make_pair(key_word, std::to_string(color_space_bias[i]))); - } - // Y = (X - mean - min) * [std^(-1)], this part is mean - for (int i = 0; i < aipp_mean.size(); i++) { - std::string key_word = "mean_chn_" + std::to_string(i); - aipp_options->insert(std::make_pair(key_word, std::to_string(aipp_mean[i]))); - } - // This part is min - for (int i = 0; i < aipp_mean.size(); i++) { - std::string key_word = "min_chn_" + std::to_string(i); - aipp_options->insert(std::make_pair(key_word, "0.0")); - } - // This part is std^(-1) - for (int i = 0; i < aipp_std.size(); i++) { - std::string key_word = "var_reci_chn_" + std::to_string(i); - aipp_options->insert(std::make_pair(key_word, std::to_string(aipp_std[i]))); - } - return Status::OK(); -} - -std::string Execute::AippCfgGenerator() { - std::string config_location = "./aipp.cfg"; - if (info_ == nullptr) { - MS_LOG(ERROR) << "info_ is null"; - return ""; - } -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - if (info_->init_with_shared_ptr_) { - auto rc = ParseTransforms(); - RETURN_SECOND_IF_ERROR(rc, ""); - info_->init_with_shared_ptr_ = false; - } - std::vector paras; // Record the parameters value of each Ascend operations - for (int32_t i = 0; i < ops_.size(); i++) { - // Validate operation ir - json ir_info; - if (ops_[i] == nullptr) { - MS_LOG(ERROR) << "Input TensorOperation[" + std::to_string(i) + "] is null."; - return ""; - } - - // Define map between operation name and parameter name - auto rc = ops_[i]->to_json(&ir_info); - if (rc.IsError()) { - MS_LOG(ERROR) << "IR information serialize to json failed, error msg is " << rc; - return ""; - } - - // Collect the information of operations - for (auto pos = info_->op2para_map_.equal_range(ops_[i]->Name()); pos.first != pos.second; ++pos.first) { - auto paras_key_word = pos.first->second; - paras = ir_info[paras_key_word].get>(); - info_->aipp_cfg_.insert(std::make_pair(ops_[i]->Name(), paras)); - } - } - - std::ofstream outfile; - outfile.open(config_location, std::ofstream::out); - - if (!outfile.is_open()) { - MS_LOG(ERROR) << "Fail to open Aipp config file, please verify your system config(including authority)." - << "We will return empty string which represent the location of Aipp config file in this case."; - return ""; - } - - if (device_type_ == MapTargetDevice::kAscend310) { - // Process resize parameters and crop parameters to find out the final size of input data - std::vector resize_paras; - std::vector crop_paras; - - // Find resize parameters - std::map>::iterator iter; - if (info_->aipp_cfg_.find(vision::kDvppResizeJpegOperation) != info_->aipp_cfg_.end()) { - iter = info_->aipp_cfg_.find(vision::kDvppResizeJpegOperation); - resize_paras = iter->second; - } else if (info_->aipp_cfg_.find(vision::kDvppDecodeResizeOperation) != info_->aipp_cfg_.end()) { - iter = info_->aipp_cfg_.find(vision::kDvppDecodeResizeOperation); - resize_paras = iter->second; - } - - // Find crop parameters - if (info_->aipp_cfg_.find(vision::kDvppCropJpegOperation) != info_->aipp_cfg_.end()) { - iter = info_->aipp_cfg_.find(vision::kDvppCropJpegOperation); - crop_paras = iter->second; - } else if (info_->aipp_cfg_.find(vision::kDvppDecodeResizeCropOperation) != info_->aipp_cfg_.end()) { - iter = info_->aipp_cfg_.find(vision::kDvppDecodeResizeCropOperation); - crop_paras = iter->second; - } - if (crop_paras.size() == 1) { - (void)crop_paras.emplace_back(crop_paras[0]); - } - - std::vector aipp_size = AippSizeFilter(resize_paras, crop_paras); - - // Process Normalization parameters to find out the final Normalization parameters for Aipp module - std::vector normalize_paras; - if (info_->aipp_cfg_.find(vision::kDvppNormalizeOperation) != info_->aipp_cfg_.end()) { - for (auto pos = info_->aipp_cfg_.equal_range(vision::kDvppNormalizeOperation); pos.first != pos.second; - ++pos.first) { - auto mean_or_std = pos.first->second; - normalize_paras.insert(normalize_paras.end(), mean_or_std.begin(), mean_or_std.end()); - } - } - - std::vector aipp_mean = AippMeanFilter(normalize_paras); - std::vector aipp_std = AippStdFilter(normalize_paras); - - std::map aipp_options; - auto rc = AippInfoCollection(&aipp_options, aipp_size, aipp_mean, aipp_std); - if (rc.IsError()) { - MS_LOG(ERROR) << "Aipp information initialization failed, error msg is " << rc; - outfile.close(); - return ""; - } - - std::string tab_char(4, ' '); - outfile << "aipp_op {" << std::endl; - for (auto &option : aipp_options) { - outfile << tab_char << option.first << " : " << option.second << std::endl; - } - outfile << "}"; - outfile.close(); - } else { // For case GPU or CPU - outfile << "aipp_op {" << std::endl << "}"; - outfile.close(); - MS_LOG(WARNING) << "Your runtime environment is not Ascend310, this config file will lead to undefined behavior on " - "computing result. Please check that."; - } - - platform::ChangeFileMode(config_location, S_IRUSR | S_IWUSR); -#endif - return config_location; -} - -bool IsEmptyPtr(const std::shared_ptr &api_ptr) { return api_ptr == nullptr; } - -Status Execute::ParseTransforms() { - auto iter = std::find_if(transforms_.begin(), transforms_.end(), IsEmptyPtr); - if (iter != transforms_.end()) { - std::string err_msg = "Your input TensorTransforms contain at least one nullptr, please check your input."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - if (device_type_ == MapTargetDevice::kCpu) { - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(ops_), - [](const std::shared_ptr &operation) -> std::shared_ptr { - return operation->Parse(); - }); - } else if (device_type_ == MapTargetDevice::kAscend310) { - for (auto &transform_ : transforms_) { - (void)ops_.emplace_back(transform_->Parse(device_type_)); - } - } else if (device_type_ == MapTargetDevice::kAscend910B) { - for (auto &transform_ : transforms_) { - (void)ops_.emplace_back(transform_->Parse(device_type_)); - } - } else { - std::string err_msg = "Your input device is not supported. (Option: CPU or Ascend310)"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} - -Status Execute::ValidateDevice() { - if (device_type_ != MapTargetDevice::kCpu && device_type_ != MapTargetDevice::kAscend310 && - device_type_ != MapTargetDevice::kGpu && device_type_ != MapTargetDevice::kAscend910B) { - std::string err_msg = "Your input device is not supported. (Option: CPU, GPU, Ascend310 or Ascend910B)."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status Execute::DeviceMemoryRelease() { - CHECK_FAIL_RETURN_UNEXPECTED(device_resource_, "Device resource is nullptr which is illegal under case Ascend310."); - Status rc = device_resource_->DeviceDataRelease(); - if (rc.IsError()) { - std::string err_msg = "Error in device data release"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status Execute::Run(const std::vector> &data_graph, - const std::vector &inputs, std::vector *outputs) { - RETURN_UNEXPECTED_IF_NULL(outputs); - std::vector transform_inputs = inputs; - std::vector transform_outputs; - if (!data_graph.empty()) { - for (const auto &exes : data_graph) { - CHECK_FAIL_RETURN_UNEXPECTED(exes != nullptr, "Given execute object is null."); - Status ret = exes->operator()(transform_inputs, &transform_outputs); - if (ret != kSuccess) { - MS_LOG(ERROR) << "Run preprocess failed:" << ret.GetErrDescription(); - return ret; - } - MS_LOG(DEBUG) << "transform_outputs[0].Shape: " << transform_outputs[0].Shape(); - transform_inputs = transform_outputs; - } - *outputs = std::move(transform_outputs); - } else { - std::string msg = "The set of Executors can not be empty."; - MS_LOG(ERROR) << msg; - RETURN_STATUS_UNEXPECTED(msg); - } - return Status::OK(); -} - -// In the current stage, there is a cyclic dependency between libmindspore.so and c_dataengine.so, -// we make a C function here and dlopen by libminspore.so to avoid linking explicitly, -// will be fix after decouling libminspore.so into multi submodules -extern "C" { -// ExecuteRun_C has C-linkage specified, but returns user-defined type 'mindspore::Status' which is incompatible with C -void ExecuteRun_C(const std::vector> &data_graph, - const std::vector &inputs, std::vector *outputs, - Status *s) { - Status ret = Execute::Run(data_graph, inputs, outputs); - if (s == nullptr) { - return; - } - *s = Status(ret); -} -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/iterator.cc b/mindspore-lite/minddata/dataset/api/iterator.cc deleted file mode 100644 index ed099ea2f..000000000 --- a/mindspore-lite/minddata/dataset/api/iterator.cc +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/include/dataset/iterator.h" - -#include "mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/runtime_context.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -namespace mindspore { -namespace dataset { - -Iterator::Iterator() : consumer_(nullptr) {} -Iterator::~Iterator() { Stop(); } - -// Get the next row from the data pipeline. -Status Iterator::GetNextRowCharIF(MSTensorMapChar *row) { - RETURN_UNEXPECTED_IF_NULL(row); - // Clean data buffer - row->clear(); - std::unordered_map> md_map; - CHECK_FAIL_RETURN_UNEXPECTED(consumer_ != nullptr, "consumer_ is null, pls launch iterator first."); - Status rc = consumer_->GetNextAsMap(&md_map); - if (rc.IsError()) { - MS_LOG(ERROR) << "GetNextRow: Failed to get next row. Error status: " << rc; - row->clear(); - return rc; - } - for (auto &de_tensor : md_map) { - std::vector col_name(de_tensor.first.begin(), de_tensor.first.end()); - row->insert(std::make_pair(col_name, mindspore::MSTensor(std::make_shared(de_tensor.second)))); - } - - return Status::OK(); -} - -// Get the next row from the data pipeline. -Status Iterator::GetNextRow(MSTensorVec *row) { - RETURN_UNEXPECTED_IF_NULL(row); - // Clean data row - row->clear(); - // create a dataset tensor row and fetch. Then we convert the output to MSTensor - std::vector> md_row; - CHECK_FAIL_RETURN_UNEXPECTED(consumer_ != nullptr, "consumer_ is null, pls launch iterator first."); - Status rc = consumer_->GetNextAsVector(&md_row); - if (rc.IsError()) { - row->clear(); - return rc; - } - std::transform(md_row.begin(), md_row.end(), std::back_inserter(*row), - [](auto t) { return mindspore::MSTensor(std::make_shared(t)); }); - return Status::OK(); -} - -// Shut down the data pipeline. -void Iterator::Stop() { - if (runtime_context_ != nullptr) { - Status rc = runtime_context_->Terminate(); - if (rc.IsError()) { - MS_LOG(ERROR) << rc.ToString(); - } - } -} - -// Function to build and launch the execution tree. -Status Iterator::BuildAndLaunchTree(const std::shared_ptr &ds, int32_t num_epochs) { - RETURN_UNEXPECTED_IF_NULL(ds); - runtime_context_ = std::make_unique(); - CHECK_FAIL_RETURN_UNEXPECTED(runtime_context_ != nullptr, "Create runtime_context_ failed."); - RETURN_IF_NOT_OK(runtime_context_->Init()); - auto consumer = std::make_unique(num_epochs); - CHECK_FAIL_RETURN_UNEXPECTED(consumer != nullptr, "Create consumer failed."); - consumer_ = consumer.get(); - RETURN_IF_NOT_OK(consumer->Init(ds->IRNode())); - runtime_context_->AssignConsumer(std::move(consumer)); - return Status::OK(); -} - -PullIterator::PullIterator() : pull_consumer_(nullptr) {} - -PullIterator::~PullIterator() = default; - -// Get the next row from the data pipeline. -Status PullIterator::GetRows(int32_t num_rows, std::vector *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - CHECK_FAIL_RETURN_UNEXPECTED(pull_consumer_ != nullptr, "Consumer is nullptr. Please launch iterator first."); - row->clear(); - for (int i = 0; i < num_rows; i++) { - std::vector> md_row; - Status rc = pull_consumer_->GetNextAsVector(&md_row); - - if (rc.IsError()) { - row->clear(); - MS_LOG(ERROR) << "GetNextRow: Failed to get next row. Error status: " << rc; - return rc; - } - - MSTensorVec ms_row = {}; - for (const auto &de_tensor : md_row) { - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), "Apply transform failed, output tensor has no data"); - ms_row.push_back(mindspore::MSTensor(std::make_shared(de_tensor))); - } - row->push_back(ms_row); - } - return Status::OK(); -} - -Status PullIterator::GetNextRow(MSTensorVec *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - CHECK_FAIL_RETURN_UNEXPECTED(pull_consumer_ != nullptr, "Consumer is nullptr."); - row->clear(); - std::vector> md_row; - Status rc = pull_consumer_->GetNextAsVector(&md_row); - if (rc.IsError()) { - row->clear(); - MS_LOG(ERROR) << "GetNextRow: Failed to get next row. Error status: " << rc; - return rc; - } - - for (const auto &de_tensor : md_row) { - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), "Apply transform failed, output tensor has no data"); - row->push_back(mindspore::MSTensor(std::make_shared(de_tensor))); - } - return Status::OK(); -} - -// Function to build and launch the execution tree. This function kicks off a different type of consumer -// for the tree, the reason why this is the case is due to the fact that PullBasedIterator does not need -// to instantiate threads for each op. As such, the call to the consumer will by pass the execution tree. -Status PullIterator::BuildAndLaunchTree(const std::shared_ptr &ds, int32_t num_epochs) { - RETURN_UNEXPECTED_IF_NULL(ds); - if (pull_consumer_ == nullptr) { - pull_consumer_ = std::make_unique(); - } - CHECK_FAIL_RETURN_UNEXPECTED(pull_consumer_ != nullptr, "pull_consumer_ is nullptr"); - RETURN_IF_NOT_OK(pull_consumer_->Init(ds->IRNode())); - return Status::OK(); -} - -Iterator::_Iterator::_Iterator(Iterator *lt) : ind_{0}, lt_{lt}, cur_row_{nullptr} { - if (lt_) { - cur_row_ = new MSTensorMap(); - if (cur_row_ == nullptr) { - return; - } - Status rc = lt_->GetNextRow(cur_row_); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error getting next row. Message: " << rc; - delete cur_row_; - cur_row_ = nullptr; - } - } -} -Iterator::_Iterator &Iterator::_Iterator::operator++() { - if (lt_) { - ++ind_; - Status rc = lt_->GetNextRow(cur_row_); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error getting next row. Message: " << rc; - cur_row_ = nullptr; - } - } - if (cur_row_ && cur_row_->empty()) { - delete cur_row_; - cur_row_ = nullptr; - } - return *this; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/python/python_mp.h b/mindspore-lite/minddata/dataset/api/python/python_mp.h deleted file mode 100644 index fe60695fa..000000000 --- a/mindspore-lite/minddata/dataset/api/python/python_mp.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_API_PYTHON_MP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_API_PYTHON_MP_H_ - -#include -#include -#include -#include -#include - -#ifdef ENABLE_PYTHON -#include "pybind11/pybind11.h" -#include "pybind11/stl.h" -namespace py = pybind11; -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class PythonMultiprocessingRuntime { - public: - virtual void launch(int32_t id) = 0; - virtual void terminate() = 0; - virtual bool is_mp_enabled() = 0; - virtual void add_new_workers(int32_t num_new_workers) = 0; - virtual void remove_workers(int32_t num_removed_workers) = 0; - virtual std::vector get_pids() = 0; - virtual bool is_running() = 0; - virtual ~PythonMultiprocessingRuntime() {} - virtual void set_thread_to_worker(int32_t worker_id) = 0; - virtual Status get_thread_to_worker(int32_t *const worker_id) const = 0; - virtual void reset() = 0; -}; - -#ifdef ENABLE_PYTHON -class PyPythonMultiprocessingRuntime : public PythonMultiprocessingRuntime { - public: - // inherit constructors - using PythonMultiprocessingRuntime::PythonMultiprocessingRuntime; - // Trampoline (need one for each virtual function) - // PYBIND11_OVERLOAD_PURE(void, /* Return type */ - // PythonMultiprocessingRuntime, /* Parent class */ - // launch /* Name of function in C++ (must match Python name) */ - - void launch(int32_t id) override { PYBIND11_OVERLOAD_PURE(void, PythonMultiprocessingRuntime, launch, id); } - void terminate() override { PYBIND11_OVERLOAD_PURE(void, PythonMultiprocessingRuntime, terminate); } - bool is_mp_enabled() override { PYBIND11_OVERLOAD_PURE(bool, PythonMultiprocessingRuntime, is_mp_enabled); } - void add_new_workers(int32_t num_workers) override { - PYBIND11_OVERLOAD_PURE(void, PythonMultiprocessingRuntime, add_new_workers, num_workers); - } - void remove_workers(int32_t num_workers) override { - PYBIND11_OVERLOAD_PURE(void, PythonMultiprocessingRuntime, remove_workers, num_workers); - } - std::vector get_pids() override { - PYBIND11_OVERLOAD_PURE(std::vector, PythonMultiprocessingRuntime, get_pids); - } - - void set_thread_to_worker(int32_t worker_id) override { - std::lock_guard guard(lock_); - threads_to_workers_[std::this_thread::get_id()] = worker_id; - } - - Status get_thread_to_worker(int32_t *const worker_id) const override { - auto itr = threads_to_workers_.find(std::this_thread::get_id()); - CHECK_FAIL_RETURN_UNEXPECTED(itr != threads_to_workers_.end(), "[Internal] This thread is not a worker!"); - *worker_id = itr->second; - return Status::OK(); - } - - void reset() override { threads_to_workers_.clear(); } - - bool is_running() override { PYBIND11_OVERLOAD_PURE(bool, PythonMultiprocessingRuntime, is_running); } - - private: - std::map threads_to_workers_{}; - std::mutex lock_; // used when writing into threads_to_workers_ -}; -#endif -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_API_PYTHON_MP_H_ diff --git a/mindspore-lite/minddata/dataset/api/samplers.cc b/mindspore-lite/minddata/dataset/api/samplers.cc deleted file mode 100644 index 2a0f56760..000000000 --- a/mindspore-lite/minddata/dataset/api/samplers.cc +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/include/dataset/samplers.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h" - -namespace mindspore { -namespace dataset { -Status Sampler::BuildChildren(std::shared_ptr *const sampler) const { - RETURN_UNEXPECTED_IF_NULL(sampler); - for (const auto &child : children_) { - std::shared_ptr sampler_obj = child->Parse(); - RETURN_IF_NOT_OK((*sampler)->AddChildSampler(sampler_obj)); - } - return Status::OK(); -} - -// DistributedSampler -DistributedSampler::DistributedSampler(int64_t num_shards, int64_t shard_id, dataset::ShuffleMode shuffle_mode, - int64_t num_samples, uint32_t seed, int64_t offset, bool even_dist) - : num_shards_(num_shards), - shard_id_(shard_id), - shuffle_mode_(shuffle_mode), - num_samples_(num_samples), - seed_(seed), - offset_(offset), - even_dist_(even_dist) {} - -std::shared_ptr DistributedSampler::Parse() const { - std::shared_ptr output = std::make_shared( - num_shards_, shard_id_, shuffle_mode_, num_samples_, seed_, offset_, even_dist_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// PKSampler -PKSampler::PKSampler(int64_t num_val, bool shuffle, int64_t num_samples) - : num_val_(num_val), shuffle_(shuffle), num_samples_(num_samples) {} - -std::shared_ptr PKSampler::Parse() const { - std::shared_ptr output = std::make_shared(num_val_, shuffle_, num_samples_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// RandomSampler -RandomSampler::RandomSampler(bool replacement, int64_t num_samples, dataset::ShuffleMode shuffle_mode) - : replacement_(replacement), num_samples_(num_samples), shuffle_mode_(shuffle_mode) {} - -std::shared_ptr RandomSampler::Parse() const { - std::shared_ptr output = - std::make_shared(replacement_, num_samples_, true, shuffle_mode_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// SequentialSampler -SequentialSampler::SequentialSampler(int64_t start_index, int64_t num_samples) - : start_index_(start_index), num_samples_(num_samples) {} - -std::shared_ptr SequentialSampler::Parse() const { - std::shared_ptr output = std::make_shared(start_index_, num_samples_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// SubsetSampler -SubsetSampler::SubsetSampler(const std::vector &indices, int64_t num_samples) - : indices_(indices), num_samples_(num_samples) {} - -std::shared_ptr SubsetSampler::Parse() const { - std::shared_ptr output = std::make_shared(indices_, num_samples_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// SubsetRandomSampler -SubsetRandomSampler::SubsetRandomSampler(const std::vector &indices, int64_t num_samples) - : SubsetSampler(indices, num_samples) {} - -std::shared_ptr SubsetRandomSampler::Parse() const { - std::shared_ptr output = std::make_shared(indices_, num_samples_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} - -// WeightedRandomSampler -WeightedRandomSampler::WeightedRandomSampler(const std::vector &weights, int64_t num_samples, bool replacement) - : weights_(weights), num_samples_(num_samples), replacement_(replacement) {} - -std::shared_ptr WeightedRandomSampler::Parse() const { - std::shared_ptr output = std::make_shared(weights_, num_samples_, replacement_); - Status s = BuildChildren(&output); - if (s.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in Parse. Message: " << s; - } - return output; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/transforms.cc b/mindspore-lite/minddata/dataset/api/transforms.cc deleted file mode 100644 index d3bee47a2..000000000 --- a/mindspore-lite/minddata/dataset/api/transforms.cc +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h" -#include "ir/dtype/type_id.h" - -namespace mindspore { -namespace dataset { -// Transform operations for data. -namespace transforms { -// API CLASS FOR DATA TRANSFORM OPERATIONS -// (In alphabetical order) - -// Constructor to Compose. -struct Compose::Data { - std::vector> transforms_; -}; - -Compose::Compose(const std::vector &transforms) : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform *const op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); -} - -Compose::Compose(const std::vector> &transforms) : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); -} - -Compose::Compose(const std::vector> &transforms) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); -} - -std::shared_ptr Compose::Parse() { return std::make_shared(data_->transforms_); } - -// Constructor to Concatenate -struct Concatenate::Data { - explicit Data(int8_t axis, const MSTensor &prepend, const MSTensor &append) - : axis_(axis), prepend_(prepend), append_(append) {} - int8_t axis_; - MSTensor prepend_; - MSTensor append_; -}; - -Concatenate::Concatenate(int8_t axis, const MSTensor &prepend, const MSTensor &append) - : data_(std::make_shared(axis, prepend, append)) {} - -std::shared_ptr Concatenate::Parse() { -#ifndef ENABLE_ANDROID - std::shared_ptr out_prepend, out_append; - Status rc = Tensor::CreateFromMSTensor(data_->prepend_, &out_prepend); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error creating prepend constant tensor. " << rc; - return nullptr; - } - rc = Tensor::CreateFromMSTensor(data_->append_, &out_append); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error creating append constant tensor. " << rc; - return nullptr; - } - return std::make_shared(data_->axis_, out_prepend, out_append); -#else - MS_LOG(ERROR) << "Concatenate op is not supported for Android."; - return nullptr; -#endif // not ENABLE_ANDROID -} - -// Constructor to Duplicate -Duplicate::Duplicate() = default; - -std::shared_ptr Duplicate::Parse() { return std::make_shared(); } - -// Constructor to Fill -struct Fill::Data { - explicit Data(const MSTensor &fill_value) : fill_value_(fill_value) {} - MSTensor fill_value_; -}; - -Fill::Fill(const MSTensor &fill_value) : data_(std::make_shared(fill_value)) {} - -std::shared_ptr Fill::Parse() { -#ifndef ENABLE_ANDROID - std::shared_ptr out_fill_value; - Status rc = Tensor::CreateFromMSTensor(data_->fill_value_, &out_fill_value); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error creating fill value tensor. " << rc; - return nullptr; - } - return std::make_shared(out_fill_value); -#else - MS_LOG(ERROR) << "Fill op is not supported for Android."; - return nullptr; -#endif // not ENABLE_ANDROID -} - -// Constructor to Mask -struct Mask::Data { - explicit Data(RelationalOp op, const MSTensor &constant, mindspore::DataType ms_type) - : op_(op), constant_(constant), ms_type_(ms_type) {} - RelationalOp op_; - MSTensor constant_; - mindspore::DataType ms_type_; -}; - -Mask::Mask(RelationalOp op, const MSTensor &constant, mindspore::DataType ms_type) - : data_(std::make_shared(op, constant, ms_type)) {} - -std::shared_ptr Mask::Parse() { -#ifndef ENABLE_ANDROID - std::shared_ptr out_constant; - Status rc = Tensor::CreateFromMSTensor(data_->constant_, &out_constant); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error creating constant tensor. " << rc; - return nullptr; - } - - DataType de_type = dataset::MSTypeToDEType(static_cast(data_->ms_type_)); - return std::make_shared(data_->op_, out_constant, de_type); -#else - MS_LOG(ERROR) << "Mask op is not supported for Android."; - return nullptr; -#endif // not ENABLE_ANDROID -} - -// Constructor to OneHot -struct OneHot::Data { - explicit Data(int32_t num_classes, double smoothing_rate) - : num_classes_(num_classes), smoothing_rate_(smoothing_rate) {} - int32_t num_classes_; - double smoothing_rate_; -}; - -OneHot::OneHot(int32_t num_classes, double smoothing_rate) - : data_(std::make_shared(num_classes, smoothing_rate)) {} - -std::shared_ptr OneHot::Parse() { - return std::make_shared(data_->num_classes_, data_->smoothing_rate_); -} - -// Constructor to PadEnd -struct PadEnd::Data { - explicit Data(const std::vector &pad_shape, const MSTensor &pad_value) - : pad_shape_(pad_shape), pad_value_(pad_value) {} - std::vector pad_shape_; - MSTensor pad_value_; -}; - -PadEnd::PadEnd(const std::vector &pad_shape, const MSTensor &pad_value) - : data_(std::make_shared(pad_shape, pad_value)) {} - -std::shared_ptr PadEnd::Parse() { -#ifndef ENABLE_ANDROID - std::shared_ptr pad_value; - Status rc = Tensor::CreateFromMSTensor(data_->pad_value_, &pad_value); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error creating pad_value constant tensor. " << rc; - return nullptr; - } - return std::make_shared(TensorShape(data_->pad_shape_), pad_value); -#else - MS_LOG(ERROR) << "PadEnd op is not supported for Android."; - return nullptr; -#endif // not ENABLE_ANDROID -} - -// Constructor to RandomApply. -struct RandomApply::Data { - std::vector> transforms_; - double prob_; -}; - -RandomApply::RandomApply(const std::vector &transforms, double prob) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform *const op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); - data_->prob_ = prob; -} - -RandomApply::RandomApply(const std::vector> &transforms, double prob) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); - data_->prob_ = prob; -} - -RandomApply::RandomApply(const std::vector> &transforms, double prob) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); - data_->prob_ = prob; -} - -std::shared_ptr RandomApply::Parse() { - return std::make_shared(data_->transforms_, data_->prob_); -} - -// Constructor to RandomChoice. -struct RandomChoice::Data { - std::vector> transforms_; -}; - -RandomChoice::RandomChoice(const std::vector &transforms) : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform *const op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); -} - -RandomChoice::RandomChoice(const std::vector> &transforms) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); -} - -RandomChoice::RandomChoice(const std::vector> &transforms) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); -} - -std::shared_ptr RandomChoice::Parse() { - return std::make_shared(data_->transforms_); -} - -// Constructor to Slice -struct Slice::Data { - explicit Data(const std::vector &slice_input) : slice_input_(slice_input) {} - std::vector slice_input_; -}; - -Slice::Slice(const std::vector &slice_input) : data_(std::make_shared(slice_input)) {} - -std::shared_ptr Slice::Parse() { -#ifndef ENABLE_ANDROID - return std::make_shared(data_->slice_input_); -#else - MS_LOG(ERROR) << "Slice op is not supported for Android."; - return nullptr; -#endif // not ENABLE_ANDROID -} - -// Constructor to TypeCast -struct TypeCast::Data { - dataset::DataType data_type_; -}; - -TypeCast::TypeCast(mindspore::DataType data_type) : data_(std::make_shared()) { - data_->data_type_ = dataset::MSTypeToDEType(static_cast(data_type)); -} - -std::shared_ptr TypeCast::Parse() { return std::make_shared(data_->data_type_); } - -// Constructor to Unique -Unique::Unique() = default; - -#ifndef ENABLE_ANDROID -std::shared_ptr Unique::Parse() { return std::make_shared(); } -#else -std::shared_ptr Unique::Parse() { - MS_LOG(ERROR) << "Unique op is not supported for Android."; - return nullptr; -} -#endif -} // namespace transforms -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/api/vision.cc b/mindspore-lite/minddata/dataset/api/vision.cc deleted file mode 100644 index 7bd5df1ca..000000000 --- a/mindspore-lite/minddata/dataset/api/vision.cc +++ /dev/null @@ -1,1553 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/include/dataset/vision.h" -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) -#include "mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h" -#ifdef ENABLE_FFMPEG -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -#if !defined(ENABLE_ANDROID) -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h" -#endif - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#endif -#if !defined(ENABLE_ANDROID) && defined(ENABLE_FFMPEG) -#include "mindspore-lite/minddata/dataset/kernels/image/video_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" - -// Typecast between mindspore::DataType and dataset::DataType -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "ir/dtype/type_id.h" - -namespace mindspore { -namespace dataset { -// Transform operations for computer vision. -namespace vision { -// CONSTRUCTORS FOR API CLASSES TO CREATE VISION TENSOR TRANSFORM OPERATIONS -// (In alphabetical order) - -// Affine Transform Operation. -struct Affine::Data { - Data(float_t degrees, const std::vector &translation, float scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value) - : degrees_(degrees), - translation_(translation), - scale_(scale), - shear_(shear), - interpolation_(interpolation), - fill_value_(fill_value) {} - float degrees_; - std::vector translation_; - float scale_; - std::vector shear_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; - -Affine::Affine(float_t degrees, const std::vector &translation, float scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value) - : data_(std::make_shared(degrees, translation, scale, shear, interpolation, fill_value)) {} - -std::shared_ptr Affine::Parse() { - return std::make_shared(data_->degrees_, data_->translation_, data_->scale_, data_->shear_, - data_->interpolation_, data_->fill_value_); -} - -#ifndef ENABLE_ANDROID -// AdjustBrightness Transform Operation. -struct AdjustBrightness::Data { - explicit Data(float brightness_factor) : brightness_factor_(brightness_factor) {} - float brightness_factor_; -}; - -AdjustBrightness::AdjustBrightness(float brightness_factor) : data_(std::make_shared(brightness_factor)) {} - -std::shared_ptr AdjustBrightness::Parse() { - return std::make_shared(data_->brightness_factor_); -} - -// AdjustContrast Transform Operation. -struct AdjustContrast::Data { - explicit Data(float contrast_factor) : contrast_factor_(contrast_factor) {} - float contrast_factor_; -}; - -AdjustContrast::AdjustContrast(float contrast_factor) : data_(std::make_shared(contrast_factor)) {} - -std::shared_ptr AdjustContrast::Parse() { - return std::make_shared(data_->contrast_factor_); -} - -// AdjustGamma Transform Operation. -struct AdjustGamma::Data { - Data(float gamma, float gain) : gamma_(gamma), gain_(gain) {} - float gamma_; - float gain_; -}; - -AdjustGamma::AdjustGamma(float gamma, float gain) : data_(std::make_shared(gamma, gain)) {} - -std::shared_ptr AdjustGamma::Parse() { - return std::make_shared(data_->gamma_, data_->gain_); -} - -// AdjustHue Transform Operation. -struct AdjustHue::Data { - explicit Data(float hue_factor) : hue_factor_(hue_factor) {} - float hue_factor_; -}; - -AdjustHue::AdjustHue(float hue_factor) : data_(std::make_shared(hue_factor)) {} - -std::shared_ptr AdjustHue::Parse() { return std::make_shared(data_->hue_factor_); } - -// AdjustSaturation Transform Operation. -struct AdjustSaturation::Data { - explicit Data(float saturation_factor) : saturation_factor_(saturation_factor) {} - float saturation_factor_; -}; - -AdjustSaturation::AdjustSaturation(float saturation_factor) : data_(std::make_shared(saturation_factor)) {} - -std::shared_ptr AdjustSaturation::Parse() { - return std::make_shared(data_->saturation_factor_); -} - -// AdjustSharpness Transform Operation. -struct AdjustSharpness::Data { - explicit Data(float sharpness_factor) : sharpness_factor_(sharpness_factor) {} - float sharpness_factor_; -}; - -AdjustSharpness::AdjustSharpness(float sharpness_factor) : data_(std::make_shared(sharpness_factor)) {} - -std::shared_ptr AdjustSharpness::Parse() { - return std::make_shared(data_->sharpness_factor_); -} - -// AutoAugment Transform Operation. -struct AutoAugment::Data { - Data(AutoAugmentPolicy policy, InterpolationMode interpolation, const std::vector &fill_value) - : policy_(policy), interpolation_(interpolation), fill_value_(fill_value) {} - AutoAugmentPolicy policy_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; - -AutoAugment::AutoAugment(AutoAugmentPolicy policy, InterpolationMode interpolation, - const std::vector &fill_value) - : data_(std::make_shared(policy, interpolation, fill_value)) {} - -std::shared_ptr AutoAugment::Parse() { - return std::make_shared(data_->policy_, data_->interpolation_, data_->fill_value_); -} - -// AutoContrast Transform Operation. -struct AutoContrast::Data { - Data(float cutoff, const std::vector &ignore) : cutoff_(cutoff), ignore_(ignore) {} - float cutoff_; - std::vector ignore_; -}; - -AutoContrast::AutoContrast(float cutoff, const std::vector &ignore) - : data_(std::make_shared(cutoff, ignore)) {} - -std::shared_ptr AutoContrast::Parse() { - return std::make_shared(data_->cutoff_, data_->ignore_); -} - -// BoundingBoxAugment Transform Operation. -struct BoundingBoxAugment::Data { - std::shared_ptr transform_; - float ratio_; -}; - -BoundingBoxAugment::BoundingBoxAugment(TensorTransform *transform, float ratio) : data_(std::make_shared()) { - data_->transform_ = transform ? transform->Parse() : nullptr; - data_->ratio_ = ratio; -} - -BoundingBoxAugment::BoundingBoxAugment(const std::shared_ptr &transform, float ratio) - : data_(std::make_shared()) { - data_->transform_ = transform ? transform->Parse() : nullptr; - data_->ratio_ = ratio; -} - -BoundingBoxAugment::BoundingBoxAugment(const std::reference_wrapper &transform, float ratio) - : data_(std::make_shared()) { - data_->transform_ = transform.get().Parse(); - data_->ratio_ = ratio; -} - -std::shared_ptr BoundingBoxAugment::Parse() { - return std::make_shared(data_->transform_, data_->ratio_); -} -#endif // not ENABLE_ANDROID - -// CenterCrop Transform Operation. -struct CenterCrop::Data { - explicit Data(const std::vector &size) : size_(size) {} - std::vector size_; -}; - -CenterCrop::CenterCrop(const std::vector &size) : data_(std::make_shared(size)) {} - -std::shared_ptr CenterCrop::Parse() { return std::make_shared(data_->size_); } - -std::shared_ptr CenterCrop::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) - std::vector usize_; - usize_.reserve(data_->size_.size()); - std::transform(data_->size_.begin(), data_->size_.end(), std::back_inserter(usize_), - [](int32_t i) { return (uint32_t)i; }); - return std::make_shared(usize_); -#endif - } else if (env == MapTargetDevice::kCpu) { - return std::make_shared(data_->size_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kCpu and kAscend310."; - return nullptr; -} - -#ifndef ENABLE_ANDROID -// ConvertColor Transform Operation. -struct ConvertColor::Data { - explicit Data(ConvertMode convert_mode) : convert_mode_(convert_mode) {} - ConvertMode convert_mode_; -}; - -ConvertColor::ConvertColor(ConvertMode convert_mode) : data_(std::make_shared(convert_mode)) {} - -std::shared_ptr ConvertColor::Parse() { - return std::make_shared(data_->convert_mode_); -} -#endif // not ENABLE_ANDROID - -// Crop Transform Operation. -struct Crop::Data { - Data(const std::vector &coordinates, const std::vector &size) - : coordinates_(coordinates), size_(size) {} - std::vector coordinates_; - std::vector size_; -}; - -Crop::Crop(const std::vector &coordinates, const std::vector &size) - : data_(std::make_shared(coordinates, size)) {} - -std::shared_ptr Crop::Parse() { - return std::make_shared(data_->coordinates_, data_->size_); -} - -#ifndef ENABLE_ANDROID -// CutMixBatch Transform Operation. -struct CutMixBatch::Data { - Data(ImageBatchFormat image_batch_format, float alpha, float prob) - : image_batch_format_(image_batch_format), alpha_(alpha), prob_(prob) {} - float alpha_; - float prob_; - ImageBatchFormat image_batch_format_; -}; - -CutMixBatch::CutMixBatch(ImageBatchFormat image_batch_format, float alpha, float prob) - : data_(std::make_shared(image_batch_format, alpha, prob)) {} - -std::shared_ptr CutMixBatch::Parse() { - return std::make_shared(data_->image_batch_format_, data_->alpha_, data_->prob_); -} - -// CutOutOp. -struct CutOut::Data { - Data(int32_t length, int32_t num_patches, bool is_hwc) - : length_(length), num_patches_(num_patches), is_hwc_(is_hwc) {} - int32_t length_; - int32_t num_patches_; - bool is_hwc_; -}; - -CutOut::CutOut(int32_t length, int32_t num_patches, bool is_hwc) - : data_(std::make_shared(length, num_patches, is_hwc)) {} - -std::shared_ptr CutOut::Parse() { - return std::make_shared(data_->length_, data_->num_patches_, data_->is_hwc_); -} -#endif // not ENABLE_ANDROID - -// Decode Transform Operation. -struct Decode::Data { - explicit Data(bool rgb) : rgb_(rgb) {} - bool rgb_; -}; - -Decode::Decode(bool rgb) : data_(std::make_shared(rgb)) {} - -std::shared_ptr Decode::Parse() { return std::make_shared(data_->rgb_); } - -std::shared_ptr Decode::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) - return std::make_shared(); -#endif - } else if (env == MapTargetDevice::kCpu) { - return std::make_shared(data_->rgb_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kCpu and kAscend310."; - return nullptr; -} - -#if !defined(ENABLE_ANDROID) && defined(ENABLE_FFMPEG) -// DecodeVideo Transform Operation. -DecodeVideo::DecodeVideo() {} - -std::shared_ptr DecodeVideo::Parse() { return std::make_shared(); } -#endif // not ENABLE_ANDROID - -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) -// DvppDecodeVideo Transform Operation. -struct DvppDecodeVideo::Data { - Data(const std::vector &size, VdecStreamFormat type, VdecOutputFormat out_format, const std::string &output) - : size_(size), format_(out_format), en_type_(type), output_(output) {} - - std::vector size_; - VdecOutputFormat format_; - VdecStreamFormat en_type_; - std::string output_; -}; - -DvppDecodeVideo::DvppDecodeVideo(const std::vector &size, VdecStreamFormat type, VdecOutputFormat out_format, - const std::vector &output) - : data_(std::make_shared(size, type, out_format, CharToString(output))) {} - -std::shared_ptr DvppDecodeVideo::Parse() { - return std::make_shared(data_->size_, data_->en_type_, data_->format_, data_->output_); -} - -std::shared_ptr DvppDecodeVideo::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { - return std::make_shared(data_->size_, data_->en_type_, data_->format_, data_->output_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only kAscend310 is supported."; - return nullptr; -} - -// DvppDecodeResize Transform Operation. -struct DvppDecodeResizeJpeg::Data { - explicit Data(const std::vector &resize) : resize_(resize) {} - std::vector resize_; -}; - -DvppDecodeResizeJpeg::DvppDecodeResizeJpeg(const std::vector &resize) - : data_(std::make_shared(resize)) {} - -std::shared_ptr DvppDecodeResizeJpeg::Parse() { - return std::make_shared(data_->resize_); -} - -std::shared_ptr DvppDecodeResizeJpeg::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { - return std::make_shared(data_->resize_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kAscend310."; - return nullptr; -} - -// DvppDecodeResizeCrop Transform Operation. -struct DvppDecodeResizeCropJpeg::Data { - Data(const std::vector &crop, const std::vector &resize) : crop_(crop), resize_(resize) {} - std::vector crop_; - std::vector resize_; -}; - -DvppDecodeResizeCropJpeg::DvppDecodeResizeCropJpeg(const std::vector &crop, - const std::vector &resize) - : data_(std::make_shared(crop, resize)) {} - -std::shared_ptr DvppDecodeResizeCropJpeg::Parse() { - return std::make_shared(data_->crop_, data_->resize_); -} - -std::shared_ptr DvppDecodeResizeCropJpeg::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - return std::make_shared(data_->crop_, data_->resize_); -#endif - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kAscend310."; - return nullptr; -} - -// DvppDecodePng Transform Operation. -DvppDecodePng::DvppDecodePng() {} - -std::shared_ptr DvppDecodePng::Parse() { return std::make_shared(); } - -std::shared_ptr DvppDecodePng::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { - return std::make_shared(); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kAscend310."; - return nullptr; -} -#endif -#ifndef ENABLE_ANDROID - -// EncodeJpeg Function. -Status EncodeJpeg(const mindspore::MSTensor &image, mindspore::MSTensor *output, int quality) { - RETURN_UNEXPECTED_IF_NULL(output); - std::shared_ptr input; - RETURN_IF_NOT_OK(Tensor::CreateFromMSTensor(image, &input)); - std::shared_ptr de_tensor; - RETURN_IF_NOT_OK(mindspore::dataset::EncodeJpeg(input, &de_tensor, quality)); - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), - "EncodeJpeg: get an empty tensor with shape " + de_tensor->shape().ToString()); - *output = mindspore::MSTensor(std::make_shared(de_tensor)); - return Status::OK(); -} - -// EncodePng Function. -Status EncodePng(const mindspore::MSTensor &image, mindspore::MSTensor *output, int compression_level) { - RETURN_UNEXPECTED_IF_NULL(output); - std::shared_ptr input; - RETURN_IF_NOT_OK(Tensor::CreateFromMSTensor(image, &input)); - TensorPtr de_tensor; - RETURN_IF_NOT_OK(mindspore::dataset::EncodePng(input, &de_tensor, compression_level)); - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), - "EncodePng: get an empty tensor with shape " + de_tensor->shape().ToString()); - *output = mindspore::MSTensor(std::make_shared(de_tensor)); - return Status::OK(); -} - -// Equalize Transform Operation. -Equalize::Equalize() = default; - -std::shared_ptr Equalize::Parse() { return std::make_shared(); } - -// Erase Operation. -struct Erase::Data { - Data(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value, bool inplace) - : top_(top), left_(left), height_(height), width_(width), value_(value), inplace_(inplace) {} - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - std::vector value_; - bool inplace_; -}; - -Erase::Erase(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value, bool inplace) - : data_(std::make_shared(top, left, height, width, value, inplace)) {} - -std::shared_ptr Erase::Parse() { - return std::make_shared(data_->top_, data_->left_, data_->height_, data_->width_, data_->value_, - data_->inplace_); -} -#endif // not ENABLE_ANDROID - -// GaussianBlur Transform Operation. -struct GaussianBlur::Data { - Data(const std::vector &kernel_size, const std::vector &sigma) - : kernel_size_(kernel_size), sigma_(sigma) {} - std::vector kernel_size_; - std::vector sigma_; -}; - -GaussianBlur::GaussianBlur(const std::vector &kernel_size, const std::vector &sigma) - : data_(std::make_shared(kernel_size, sigma)) {} - -std::shared_ptr GaussianBlur::Parse() { - return std::make_shared(data_->kernel_size_, data_->sigma_); -} - -#ifndef ENABLE_ANDROID -// GetImageNumChannels Function. -Status GetImageNumChannels(const mindspore::MSTensor &image, dsize_t *channels) { - RETURN_UNEXPECTED_IF_NULL(channels); - std::shared_ptr input; - Status rc = Tensor::CreateFromMSTensor(image, &input); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("GetImageNumChannels: failed to create image tensor."); - } - return ImageNumChannels(input, channels); -} - -// GetImageSize Function. -Status GetImageSize(const mindspore::MSTensor &image, std::vector *size) { - RETURN_UNEXPECTED_IF_NULL(size); - std::shared_ptr input; - Status rc = Tensor::CreateFromMSTensor(image, &input); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("GetImageSize: failed to create image tensor."); - } - return ImageSize(input, size); -} - -// HorizontalFlip Transform Operation. -HorizontalFlip::HorizontalFlip() = default; - -std::shared_ptr HorizontalFlip::Parse() { return std::make_shared(); } -#endif // not ENABLE_ANDROID - -// HwcToChw Transform Operation. -HWC2CHW::HWC2CHW() = default; - -std::shared_ptr HWC2CHW::Parse() { return std::make_shared(); } - -#ifndef ENABLE_ANDROID -// Invert Transform Operation. -Invert::Invert() = default; - -std::shared_ptr Invert::Parse() { return std::make_shared(); } - -// MixUpBatch Transform Operation. -struct MixUpBatch::Data { - explicit Data(float alpha) : alpha_(alpha) {} - float alpha_; -}; - -MixUpBatch::MixUpBatch(float alpha) : data_(std::make_shared(alpha)) {} - -std::shared_ptr MixUpBatch::Parse() { return std::make_shared(data_->alpha_); } -#endif // not ENABLE_ANDROID - -// Normalize Transform Operation. -struct Normalize::Data { - Data(const std::vector &mean, const std::vector &std, bool is_hwc) - : mean_(mean), std_(std), is_hwc_(is_hwc) {} - std::vector mean_; - std::vector std_; - bool is_hwc_; -}; - -Normalize::Normalize(const std::vector &mean, const std::vector &std, bool is_hwc) - : data_(std::make_shared(mean, std, is_hwc)) {} - -std::shared_ptr Normalize::Parse() { - return std::make_shared(data_->mean_, data_->std_, data_->is_hwc_); -} - -std::shared_ptr Normalize::Parse(const MapTargetDevice &env) { -#ifdef ENABLE_ANDROID - if (data_->is_hwc_ == false) { - MS_LOG(ERROR) << "Normalize op on Lite does not support 'is_hwc' = false."; - return nullptr; - } -#endif - if (env == MapTargetDevice::kAscend310) { -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) - return std::make_shared(data_->mean_, data_->std_); -#endif - } else if (env == MapTargetDevice::kCpu) { - return std::make_shared(data_->mean_, data_->std_, data_->is_hwc_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kCpu and kAscend310."; - return nullptr; -} - -#ifndef ENABLE_ANDROID -// NormalizePad Transform Operation. -struct NormalizePad::Data { - Data(const std::vector &mean, const std::vector &std, const std::string &dtype, bool is_hwc) - : mean_(mean), std_(std), dtype_(dtype), is_hwc_(is_hwc) {} - std::vector mean_; - std::vector std_; - std::string dtype_; - bool is_hwc_; -}; - -NormalizePad::NormalizePad(const std::vector &mean, const std::vector &std, - const std::vector &dtype, bool is_hwc) - : data_(std::make_shared(mean, std, CharToString(dtype), is_hwc)) {} - -std::shared_ptr NormalizePad::Parse() { - return std::make_shared(data_->mean_, data_->std_, data_->dtype_, data_->is_hwc_); -} -#endif // not ENABLE_ANDROID - -// Pad Transform Operation. -struct Pad::Data { - Data(const std::vector &padding, const std::vector &fill_value, BorderType padding_mode) - : padding_(padding), fill_value_(fill_value), padding_mode_(padding_mode) {} - std::vector padding_; - std::vector fill_value_; - BorderType padding_mode_; -}; - -Pad::Pad(const std::vector &padding, const std::vector &fill_value, BorderType padding_mode) - : data_(std::make_shared(padding, fill_value, padding_mode)) {} - -std::shared_ptr Pad::Parse() { -#if !defined(ENABLE_ANDROID) - return std::make_shared(data_->padding_, data_->fill_value_, data_->padding_mode_); -#else - MS_LOG(ERROR) << "Unsupported Pad."; - return nullptr; -#endif -} - -#ifndef ENABLE_ANDROID -// PadToSize Transform Operation. -struct PadToSize::Data { - Data(const std::vector &size, const std::vector &offset, const std::vector &fill_value, - BorderType padding_mode) - : size_(size), offset_(offset), fill_value_(fill_value), padding_mode_(padding_mode) {} - std::vector size_; - std::vector offset_; - std::vector fill_value_; - BorderType padding_mode_; -}; - -PadToSize::PadToSize(const std::vector &size, const std::vector &offset, - const std::vector &fill_value, BorderType padding_mode) - : data_(std::make_shared(size, offset, fill_value, padding_mode)) {} - -std::shared_ptr PadToSize::Parse() { - return std::make_shared(data_->size_, data_->offset_, data_->fill_value_, data_->padding_mode_); -} - -// Perspective Transform Operation. -struct Perspective::Data { - Data(const std::vector> &start_points, const std::vector> &end_points, - InterpolationMode interpolation) - : start_points_(start_points), end_points_(end_points), interpolation_(interpolation) {} - std::vector> start_points_; - std::vector> end_points_; - InterpolationMode interpolation_; -}; - -Perspective::Perspective(const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation) - : data_(std::make_shared(start_points, end_points, interpolation)) {} - -std::shared_ptr Perspective::Parse() { - return std::make_shared(data_->start_points_, data_->end_points_, data_->interpolation_); -} - -// Posterize Transform Operation -struct Posterize::Data { - explicit Data(uint8_t bits) : bits_(bits) {} - uint8_t bits_; -}; - -Posterize::Posterize(uint8_t bits) : data_(std::make_shared(bits)) {} - -std::shared_ptr Posterize::Parse() { return std::make_shared(data_->bits_); } - -// RandAugment Transform Operation -struct RandAugment::Data { - Data(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value) - : num_ops_(num_ops), - magnitude_(magnitude), - num_magnitude_bins_(num_magnitude_bins), - interpolation_(interpolation), - fill_value_(fill_value) {} - int32_t num_ops_; - int32_t magnitude_; - int32_t num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; - -RandAugment::RandAugment(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, - InterpolationMode interpolation, const std::vector &fill_value) - : data_(std::make_shared(num_ops, magnitude, num_magnitude_bins, interpolation, fill_value)) {} - -std::shared_ptr RandAugment::Parse() { - return std::make_shared(data_->num_ops_, data_->magnitude_, data_->num_magnitude_bins_, - data_->interpolation_, data_->fill_value_); -} - -// RandomAdjustSharpness Transform Operation. -struct RandomAdjustSharpness::Data { - Data(float degree, float prob) : degree_(degree), probability_(prob) {} - float degree_; - float probability_; -}; - -RandomAdjustSharpness::RandomAdjustSharpness(float degree, float prob) : data_(std::make_shared(degree, prob)) {} - -std::shared_ptr RandomAdjustSharpness::Parse() { - return std::make_shared(data_->degree_, data_->probability_); -} -#endif // not ENABLE_ANDROID - -// RandomAffine Transform Operation. -struct RandomAffine::Data { - Data(const std::vector °rees, const std::vector &translate_range, - const std::vector &scale_range, const std::vector &shear_ranges, - InterpolationMode interpolation, const std::vector &fill_value) - : degrees_(degrees), - translate_range_(translate_range), - scale_range_(scale_range), - shear_ranges_(shear_ranges), - interpolation_(interpolation), - fill_value_(fill_value) {} - std::vector degrees_; // min_degree, max_degree - std::vector translate_range_; // maximum x translation percentage, maximum y translation percentage - std::vector scale_range_; // min_scale, max_scale - std::vector shear_ranges_; // min_x_shear, max_x_shear, min_y_shear, max_y_shear - InterpolationMode interpolation_; - std::vector fill_value_; -}; - -RandomAffine::RandomAffine(const std::vector °rees, const std::vector &translate_range, - const std::vector &scale_range, const std::vector &shear_ranges, - InterpolationMode interpolation, const std::vector &fill_value) - : data_(std::make_shared(degrees, translate_range, scale_range, shear_ranges, interpolation, fill_value)) {} - -std::shared_ptr RandomAffine::Parse() { - return std::make_shared(data_->degrees_, data_->translate_range_, data_->scale_range_, - data_->shear_ranges_, data_->interpolation_, data_->fill_value_); -} - -#ifndef ENABLE_ANDROID -// RandomAutoContrast Transform Operation. -struct RandomAutoContrast::Data { - Data(float cutoff, const std::vector &ignore, float prob) - : cutoff_(cutoff), ignore_(ignore), probability_(prob) {} - float cutoff_; - std::vector ignore_; - float probability_; -}; - -RandomAutoContrast::RandomAutoContrast(float cutoff, const std::vector &ignore, float prob) - : data_(std::make_shared(cutoff, ignore, prob)) {} - -std::shared_ptr RandomAutoContrast::Parse() { - return std::make_shared(data_->cutoff_, data_->ignore_, data_->probability_); -} - -// RandomColor Transform Operation. -struct RandomColor::Data { - Data(float t_lb, float t_ub) : t_lb_(t_lb), t_ub_(t_ub) {} - float t_lb_; - float t_ub_; -}; - -RandomColor::RandomColor(float t_lb, float t_ub) : data_(std::make_shared(t_lb, t_ub)) {} - -std::shared_ptr RandomColor::Parse() { - return std::make_shared(data_->t_lb_, data_->t_ub_); -} - -// RandomColorAdjust Transform Operation. -struct RandomColorAdjust::Data { - Data(const std::vector &brightness, const std::vector &contrast, const std::vector &saturation, - const std::vector &hue) - : brightness_(brightness), contrast_(contrast), saturation_(saturation), hue_(hue) {} - std::vector brightness_; - std::vector contrast_; - std::vector saturation_; - std::vector hue_; -}; - -RandomColorAdjust::RandomColorAdjust(const std::vector &brightness, const std::vector &contrast, - const std::vector &saturation, const std::vector &hue) - : data_(std::make_shared(brightness, contrast, saturation, hue)) {} - -std::shared_ptr RandomColorAdjust::Parse() { - return std::make_shared(data_->brightness_, data_->contrast_, data_->saturation_, - data_->hue_); -} - -// RandomCrop Transform Operation. -struct RandomCrop::Data { - Data(const std::vector &size, const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, BorderType padding_mode) - : size_(size), - padding_(padding), - pad_if_needed_(pad_if_needed), - fill_value_(fill_value), - padding_mode_(padding_mode) {} - std::vector size_; - std::vector padding_; - bool pad_if_needed_; - std::vector fill_value_; - BorderType padding_mode_; -}; - -RandomCrop::RandomCrop(const std::vector &size, const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, BorderType padding_mode) - : data_(std::make_shared(size, padding, pad_if_needed, fill_value, padding_mode)) {} - -std::shared_ptr RandomCrop::Parse() { - return std::make_shared(data_->size_, data_->padding_, data_->pad_if_needed_, data_->fill_value_, - data_->padding_mode_); -} - -// RandomCropDecodeResize Transform Operation. -struct RandomCropDecodeResize::Data { - Data(const std::vector &size, const std::vector &scale, const std::vector &ratio, - InterpolationMode interpolation, int32_t max_attempts) - : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} - std::vector size_; - std::vector scale_; - std::vector ratio_; - InterpolationMode interpolation_; - int32_t max_attempts_; -}; - -RandomCropDecodeResize::RandomCropDecodeResize(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, - int32_t max_attempts) - : data_(std::make_shared(size, scale, ratio, interpolation, max_attempts)) {} - -std::shared_ptr RandomCropDecodeResize::Parse() { - return std::make_shared(data_->size_, data_->scale_, data_->ratio_, - data_->interpolation_, data_->max_attempts_); -} - -// RandomCropWithBBox Transform Operation. -struct RandomCropWithBBox::Data { - Data(const std::vector &size, const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, BorderType padding_mode) - : size_(size), - padding_(padding), - pad_if_needed_(pad_if_needed), - fill_value_(fill_value), - padding_mode_(padding_mode) {} - std::vector size_; - std::vector padding_; - bool pad_if_needed_; - std::vector fill_value_; - BorderType padding_mode_; -}; - -RandomCropWithBBox::RandomCropWithBBox(const std::vector &size, const std::vector &padding, - bool pad_if_needed, const std::vector &fill_value, - BorderType padding_mode) - : data_(std::make_shared(size, padding, pad_if_needed, fill_value, padding_mode)) {} - -std::shared_ptr RandomCropWithBBox::Parse() { - return std::make_shared(data_->size_, data_->padding_, data_->pad_if_needed_, - data_->fill_value_, data_->padding_mode_); -} - -// RandomEqualize Transform Operation. -struct RandomEqualize::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomEqualize::RandomEqualize(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomEqualize::Parse() { - return std::make_shared(data_->probability_); -} - -// RandomHorizontalFlip. -struct RandomHorizontalFlip::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomHorizontalFlip::RandomHorizontalFlip(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomHorizontalFlip::Parse() { - return std::make_shared(data_->probability_); -} - -// RandomHorizontalFlipWithBBox -struct RandomHorizontalFlipWithBBox::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomHorizontalFlipWithBBox::RandomHorizontalFlipWithBBox(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomHorizontalFlipWithBBox::Parse() { - return std::make_shared(data_->probability_); -} - -// RandomInvert Operation. -struct RandomInvert::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomInvert::RandomInvert(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomInvert::Parse() { - return std::make_shared(data_->probability_); -} - -// RandomLighting Transform Operation. -struct RandomLighting::Data { - explicit Data(float alpha) : alpha_(alpha) {} - float alpha_; -}; - -RandomLighting::RandomLighting(float alpha) : data_(std::make_shared(alpha)) {} - -std::shared_ptr RandomLighting::Parse() { - return std::make_shared(data_->alpha_); -} - -// RandomPosterize Transform Operation. -struct RandomPosterize::Data { - explicit Data(const std::vector &bit_range) : bit_range_(bit_range) {} - std::vector bit_range_; -}; - -RandomPosterize::RandomPosterize(const std::vector &bit_range) : data_(std::make_shared(bit_range)) {} - -std::shared_ptr RandomPosterize::Parse() { - return std::make_shared(data_->bit_range_); -} - -// RandomResize Transform Operation. -struct RandomResize::Data { - explicit Data(const std::vector &size) : size_(size) {} - std::vector size_; -}; - -RandomResize::RandomResize(const std::vector &size) : data_(std::make_shared(size)) {} - -std::shared_ptr RandomResize::Parse() { return std::make_shared(data_->size_); } - -// RandomResizeWithBBox Transform Operation. -struct RandomResizeWithBBox::Data { - explicit Data(const std::vector &size) : size_(size) {} - std::vector size_; -}; - -RandomResizeWithBBox::RandomResizeWithBBox(const std::vector &size) : data_(std::make_shared(size)) {} - -std::shared_ptr RandomResizeWithBBox::Parse() { - return std::make_shared(data_->size_); -} - -// RandomResizedCrop Transform Operation. -struct RandomResizedCrop::Data { - Data(const std::vector &size, const std::vector &scale, const std::vector &ratio, - InterpolationMode interpolation, int32_t max_attempts) - : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} - std::vector size_; - std::vector scale_; - std::vector ratio_; - InterpolationMode interpolation_; - int32_t max_attempts_; -}; - -RandomResizedCrop::RandomResizedCrop(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, - int32_t max_attempts) - : data_(std::make_shared(size, scale, ratio, interpolation, max_attempts)) {} - -std::shared_ptr RandomResizedCrop::Parse() { - return std::make_shared(data_->size_, data_->scale_, data_->ratio_, data_->interpolation_, - data_->max_attempts_); -} - -// RandomResizedCrop Transform Operation. -struct RandomResizedCropWithBBox::Data { - Data(const std::vector &size, const std::vector &scale, const std::vector &ratio, - InterpolationMode interpolation, int32_t max_attempts) - : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} - std::vector size_; - std::vector scale_; - std::vector ratio_; - InterpolationMode interpolation_; - int32_t max_attempts_; -}; - -RandomResizedCropWithBBox::RandomResizedCropWithBBox(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, - int32_t max_attempts) - : data_(std::make_shared(size, scale, ratio, interpolation, max_attempts)) {} - -std::shared_ptr RandomResizedCropWithBBox::Parse() { - return std::make_shared(data_->size_, data_->scale_, data_->ratio_, - data_->interpolation_, data_->max_attempts_); -} - -// RandomRotation Transform Operation. -struct RandomRotation::Data { - Data(const std::vector °rees, InterpolationMode resample, bool expand, const std::vector ¢er, - const std::vector &fill_value) - : degrees_(degrees), interpolation_mode_(resample), center_(center), expand_(expand), fill_value_(fill_value) {} - std::vector degrees_; - InterpolationMode interpolation_mode_; - std::vector center_; - bool expand_; - std::vector fill_value_; -}; - -RandomRotation::RandomRotation(const std::vector °rees, InterpolationMode resample, bool expand, - const std::vector ¢er, const std::vector &fill_value) - : data_(std::make_shared(degrees, resample, expand, center, fill_value)) {} - -std::shared_ptr RandomRotation::Parse() { - return std::make_shared(data_->degrees_, data_->interpolation_mode_, data_->expand_, - data_->center_, data_->fill_value_); -} - -// RandomSelectSubpolicy Transform Operation. -struct RandomSelectSubpolicy::Data { - std::vector, double>>> policy_; -}; - -RandomSelectSubpolicy::RandomSelectSubpolicy( - const std::vector>> &policy) - : data_(std::make_shared()) { - for (uint32_t i = 0; i < policy.size(); i++) { - std::vector, double>> subpolicy; - - for (uint32_t j = 0; j < policy[i].size(); j++) { - TensorTransform *op = policy[i][j].first; - std::shared_ptr operation = (op ? op->Parse() : nullptr); - double prob = policy[i][j].second; - subpolicy.emplace_back(std::move(std::make_pair(operation, prob))); - } - data_->policy_.emplace_back(subpolicy); - } -} - -RandomSelectSubpolicy::RandomSelectSubpolicy( - const std::vector, double>>> &policy) - : data_(std::make_shared()) { - for (uint32_t i = 0; i < policy.size(); i++) { - std::vector, double>> subpolicy; - - for (uint32_t j = 0; j < policy[i].size(); j++) { - std::shared_ptr op = policy[i][j].first; - std::shared_ptr operation = (op ? op->Parse() : nullptr); - double prob = policy[i][j].second; - subpolicy.emplace_back(std::move(std::make_pair(operation, prob))); - } - data_->policy_.emplace_back(subpolicy); - } -} - -RandomSelectSubpolicy::RandomSelectSubpolicy( - const std::vector, double>>> &policy) - : data_(std::make_shared()) { - for (int32_t i = 0; i < policy.size(); i++) { - std::vector, double>> subpolicy; - - for (int32_t j = 0; j < policy[i].size(); j++) { - TensorTransform &op = policy[i][j].first; - std::shared_ptr operation = op.Parse(); - double prob = policy[i][j].second; - subpolicy.emplace_back(std::move(std::make_pair(operation, prob))); - } - data_->policy_.emplace_back(subpolicy); - } -} - -std::shared_ptr RandomSelectSubpolicy::Parse() { - return std::make_shared(data_->policy_); -} - -// RandomSharpness Transform Operation. -struct RandomSharpness::Data { - explicit Data(const std::vector °rees) : degrees_(degrees) {} - std::vector degrees_; -}; - -RandomSharpness::RandomSharpness(const std::vector °rees) : data_(std::make_shared(degrees)) {} - -std::shared_ptr RandomSharpness::Parse() { - return std::make_shared(data_->degrees_); -} - -// RandomSolarize Transform Operation. -struct RandomSolarize::Data { - explicit Data(const std::vector &threshold) : threshold_(threshold) {} - std::vector threshold_; -}; - -RandomSolarize::RandomSolarize(const std::vector &threshold) : data_(std::make_shared(threshold)) {} - -std::shared_ptr RandomSolarize::Parse() { - return std::make_shared(data_->threshold_); -} - -// RandomVerticalFlip Transform Operation. -struct RandomVerticalFlip::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomVerticalFlip::RandomVerticalFlip(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomVerticalFlip::Parse() { - return std::make_shared(data_->probability_); -} - -// RandomVerticalFlipWithBBox Transform Operation. -struct RandomVerticalFlipWithBBox::Data { - explicit Data(float prob) : probability_(prob) {} - float probability_; -}; - -RandomVerticalFlipWithBBox::RandomVerticalFlipWithBBox(float prob) : data_(std::make_shared(prob)) {} - -std::shared_ptr RandomVerticalFlipWithBBox::Parse() { - return std::make_shared(data_->probability_); -} - -// ReadFile Function. -Status ReadFile(const std::string &filename, mindspore::MSTensor *output) { - RETURN_UNEXPECTED_IF_NULL(output); - - std::shared_ptr de_tensor; - RETURN_IF_NOT_OK(mindspore::dataset::ReadFile(filename, &de_tensor)); - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), - "ReadFile: Get an empty tensor with shape " + de_tensor->shape().ToString()); - *output = mindspore::MSTensor(std::make_shared(de_tensor)); - - return Status::OK(); -} - -// ReadImage Function. -Status ReadImage(const std::string &filename, mindspore::MSTensor *output, ImageReadMode mode) { - RETURN_UNEXPECTED_IF_NULL(output); - - std::shared_ptr de_tensor; - RETURN_IF_NOT_OK(mindspore::dataset::ReadImage(filename, &de_tensor, mode)); - CHECK_FAIL_RETURN_UNEXPECTED(de_tensor->HasData(), - "ReadImage: get an empty tensor with shape " + de_tensor->shape().ToString()); - *output = mindspore::MSTensor(std::make_shared(de_tensor)); - return Status::OK(); -} - -#ifdef ENABLE_FFMPEG -// ReadVideo Function. -Status ReadVideo(const std::string &filename, mindspore::MSTensor *video_output, mindspore::MSTensor *audio_output, - std::map *metadata_output, float start_pts, float end_pts, - const std::string &pts_unit) { - RETURN_UNEXPECTED_IF_NULL(video_output); - RETURN_UNEXPECTED_IF_NULL(audio_output); - RETURN_UNEXPECTED_IF_NULL(metadata_output); - - std::shared_ptr de_video_output; - std::shared_ptr de_audio_output; - RETURN_IF_NOT_OK(mindspore::dataset::ReadVideo(filename, &de_video_output, &de_audio_output, metadata_output, - start_pts, end_pts, pts_unit)); - *video_output = mindspore::MSTensor(std::make_shared(de_video_output)); - *audio_output = mindspore::MSTensor(std::make_shared(de_audio_output)); - return Status::OK(); -} - -// ReadVideoTimestamps. -Status ReadVideoTimestamps(const std::string &filename, std::tuple, float> *output, - const std::string &pts_unit) { - RETURN_UNEXPECTED_IF_NULL(output); - std::vector pts_int64_vector; - float video_fps; - float time_base; - - RETURN_IF_NOT_OK( - mindspore::dataset::ReadVideoTimestamps(filename, &pts_int64_vector, &video_fps, &time_base, pts_unit)); - - std::vector pts_float_vector; - - if (pts_unit == "pts") { - for (int64_t pts_int64 : pts_int64_vector) { - pts_float_vector.push_back(static_cast(pts_int64)); - } - } - if (pts_unit == "sec") { - for (int64_t pts_int64 : pts_int64_vector) { - pts_float_vector.push_back(static_cast(pts_int64 * time_base)); - } - } - *output = std::make_tuple(pts_float_vector, video_fps); - return Status::OK(); -} -#endif -#endif // not ENABLE_ANDROID - -// Rescale Transform Operation. -struct Rescale::Data { - Data(float rescale, float shift) : rescale_(rescale), shift_(shift) {} - float rescale_; - float shift_; -}; - -Rescale::Rescale(float rescale, float shift) : data_(std::make_shared(rescale, shift)) {} - -std::shared_ptr Rescale::Parse() { -#if !defined(ENABLE_ANDROID) - return std::make_shared(data_->rescale_, data_->shift_); -#else - MS_LOG(ERROR) << "Unsupported Rescale."; - return nullptr; -#endif -} - -// Resize Transform Operation. -struct Resize::Data { - Data(const std::vector &size, InterpolationMode interpolation) - : size_(size), interpolation_(interpolation) {} - std::vector size_; - InterpolationMode interpolation_; -}; - -Resize::Resize(const std::vector &size, InterpolationMode interpolation) - : data_(std::make_shared(size, interpolation)) {} - -std::shared_ptr Resize::Parse() { - return std::make_shared(data_->size_, data_->interpolation_); -} - -std::shared_ptr Resize::Parse(const MapTargetDevice &env) { - if (env == MapTargetDevice::kAscend310) { -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) || defined(ENABLE_DVPP) - std::vector usize_; - usize_.reserve(data_->size_.size()); - std::transform(data_->size_.begin(), data_->size_.end(), std::back_inserter(usize_), - [](int32_t i) { return (uint32_t)i; }); - return std::make_shared(usize_); -#endif - } else if (env == MapTargetDevice::kCpu) { - return std::make_shared(data_->size_, data_->interpolation_); - } - MS_LOG(ERROR) << "Unsupported MapTargetDevice, only supported kCpu and kAscend310."; - return nullptr; -} - -#ifndef ENABLE_ANDROID -// ResizedCrop Transform Operation. -struct ResizedCrop::Data { - Data(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &size, - InterpolationMode interpolation) - : top_(top), left_(left), height_(height), width_(width), size_(size), interpolation_(interpolation) {} - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - std::vector size_; - InterpolationMode interpolation_; -}; - -ResizedCrop::ResizedCrop(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &size, - InterpolationMode interpolation) - : data_(std::make_shared(top, left, height, width, size, interpolation)) {} - -std::shared_ptr ResizedCrop::Parse() { - return std::make_shared(data_->top_, data_->left_, data_->height_, data_->width_, data_->size_, - data_->interpolation_); -} -#endif // not ENABLE_ANDROID - -// ResizePreserveAR Transform Operation. -struct ResizePreserveAR::Data { - Data(int32_t height, int32_t width, int32_t img_orientation) - : height_(height), width_(width), img_orientation_(img_orientation) {} - int32_t height_; - int32_t width_; - int32_t img_orientation_; -}; - -ResizePreserveAR::ResizePreserveAR(int32_t height, int32_t width, int32_t img_orientation) - : data_(std::make_shared(height, width, img_orientation)) {} - -std::shared_ptr ResizePreserveAR::Parse() { - return std::make_shared(data_->height_, data_->width_, data_->img_orientation_); -} - -#ifndef ENABLE_ANDROID -// ResizeWithBBox Transform Operation. -struct ResizeWithBBox::Data { - Data(const std::vector &size, InterpolationMode interpolation) - : size_(size), interpolation_(interpolation) {} - std::vector size_; - InterpolationMode interpolation_; -}; - -ResizeWithBBox::ResizeWithBBox(const std::vector &size, InterpolationMode interpolation) - : data_(std::make_shared(size, interpolation)) {} - -std::shared_ptr ResizeWithBBox::Parse() { - return std::make_shared(data_->size_, data_->interpolation_); -} -#endif // not ENABLE_ANDROID - -// RGB2BGR Transform Operation. -std::shared_ptr RGB2BGR::Parse() { return std::make_shared(); } - -// RGB2GRAY Transform Operation. -std::shared_ptr RGB2GRAY::Parse() { return std::make_shared(); } - -// Rotate Transform Operation. -struct Rotate::Data { - Data(const float °rees, InterpolationMode resample, bool expand, const std::vector ¢er, - const std::vector &fill_value) - : degrees_(degrees), interpolation_mode_(resample), center_(center), expand_(expand), fill_value_(fill_value) {} - explicit Data(const FixRotationAngle &angle_id) : angle_id_(angle_id), lite_impl_(true) {} - FixRotationAngle angle_id_{FixRotationAngle::k0Degree}; - bool lite_impl_{false}; - float degrees_{0}; - InterpolationMode interpolation_mode_{InterpolationMode::kNearestNeighbour}; - std::vector center_{{}}; - bool expand_{false}; - std::vector fill_value_{0, 0, 0}; -}; - -Rotate::Rotate(FixRotationAngle angle_id) : data_(std::make_shared(angle_id)) {} - -Rotate::Rotate(float degrees, InterpolationMode resample, bool expand, const std::vector ¢er, - const std::vector &fill_value) - : data_(std::make_shared(degrees, resample, expand, center, fill_value)) {} - -std::shared_ptr Rotate::Parse() { -#ifndef ENABLE_ANDROID - if (!data_->lite_impl_) { - return std::make_shared(data_->degrees_, data_->interpolation_mode_, data_->expand_, - data_->center_, data_->fill_value_); - } -#else - if (data_->lite_impl_) { - return std::make_shared(data_->angle_id_); - } -#endif // not ENABLE_ANDROID - std::string platform = data_->lite_impl_ ? "Cloud" : "Android"; - MS_LOG(ERROR) << "This Rotate API is not supported for " + platform + ", use another Rotate API."; - return nullptr; -} - -#ifndef ENABLE_ANDROID -// RgbaToBgr Transform Operation. -RGBA2BGR::RGBA2BGR() = default; - -std::shared_ptr RGBA2BGR::Parse() { return std::make_shared(); } - -// RgbaToRgb Transform Operation. -RGBA2RGB::RGBA2RGB() = default; - -std::shared_ptr RGBA2RGB::Parse() { return std::make_shared(); } - -// SlicePatches Transform Operation. -struct SlicePatches::Data { - Data(int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) - : num_height_(num_height), num_width_(num_width), slice_mode_(slice_mode), fill_value_(fill_value) {} - int32_t num_height_; - int32_t num_width_; - SliceMode slice_mode_; - uint8_t fill_value_; -}; - -SlicePatches::SlicePatches(int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) - : data_(std::make_shared(num_height, num_width, slice_mode, fill_value)) {} - -std::shared_ptr SlicePatches::Parse() { - return std::make_shared(data_->num_height_, data_->num_width_, data_->slice_mode_, - data_->fill_value_); -} - -// Solarize Transform Operation. -struct Solarize::Data { - explicit Data(const std::vector &threshold) : threshold_(threshold) {} - std::vector threshold_; -}; - -Solarize::Solarize(const std::vector &threshold) : data_(std::make_shared(threshold)) {} - -std::shared_ptr Solarize::Parse() { return std::make_shared(data_->threshold_); } -#endif // not ENABLE_ANDROID - -// SwapRedBlue Transform Operation. -SwapRedBlue::SwapRedBlue() = default; -std::shared_ptr SwapRedBlue::Parse() { -#if !defined(ENABLE_ANDROID) - return std::make_shared(); -#else - MS_LOG(ERROR) << "Unsupported SwapRedBlue."; - return nullptr; -#endif -} - -#ifndef ENABLE_ANDROID -// ToTensor Transform Operation. -struct ToTensor::Data { - explicit Data(const std::string &output_type) : output_type_(DataType(output_type)) {} - explicit Data(const DataType::Type &output_type) : output_type_(output_type) {} - explicit Data(const mindspore::DataType &output_type) - : output_type_(dataset::MSTypeToDEType(static_cast(output_type))) {} - DataType output_type_{}; -}; - -ToTensor::ToTensor() : data_(std::make_shared(DataType::Type::DE_FLOAT32)) {} -ToTensor::ToTensor(std::string output_type) : data_(std::make_shared(output_type)) {} -ToTensor::ToTensor(mindspore::DataType output_type) : data_(std::make_shared(output_type)) {} - -std::shared_ptr ToTensor::Parse() { return std::make_shared(data_->output_type_); } - -// TrivialAugmentWide Transform Operation. -struct TrivialAugmentWide::Data { - Data(int32_t num_magnitude_bins, InterpolationMode interpolation, const std::vector &fill_value) - : num_magnitude_bins_(num_magnitude_bins), interpolation_(interpolation), fill_value_(fill_value) {} - int32_t num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; - -TrivialAugmentWide::TrivialAugmentWide(int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value) - : data_(std::make_shared(num_magnitude_bins, interpolation, fill_value)) {} - -std::shared_ptr TrivialAugmentWide::Parse() { - return std::make_shared(data_->num_magnitude_bins_, data_->interpolation_, - data_->fill_value_); -} - -// UniformAug Transform Operation. -struct UniformAugment::Data { - std::vector> transforms_; - int32_t num_ops_; -}; - -UniformAugment::UniformAugment(const std::vector &transforms, int32_t num_ops) - : data_(std::make_shared()) { - (void)std::transform( - transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform *const op) -> std::shared_ptr { return op ? op->Parse() : nullptr; }); - data_->num_ops_ = num_ops; -} - -UniformAugment::UniformAugment(const std::vector> &transforms, int32_t num_ops) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op ? op->Parse() : nullptr; - }); - data_->num_ops_ = num_ops; -} - -UniformAugment::UniformAugment(const std::vector> &transforms, int32_t num_ops) - : data_(std::make_shared()) { - (void)std::transform(transforms.begin(), transforms.end(), std::back_inserter(data_->transforms_), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); - data_->num_ops_ = num_ops; -} - -std::shared_ptr UniformAugment::Parse() { - return std::make_shared(data_->transforms_, data_->num_ops_); -} - -// VerticalFlip Transform Operation. -VerticalFlip::VerticalFlip() = default; - -std::shared_ptr VerticalFlip::Parse() { return std::make_shared(); } - -// WriteFile Function. -Status WriteFile(const std::string &filename, const mindspore::MSTensor &data) { - std::shared_ptr de_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMSTensor(data, &de_tensor)); - RETURN_IF_NOT_OK(mindspore::dataset::WriteFile(filename, de_tensor)); - return Status::OK(); -} - -// WriteJpeg Function. -Status WriteJpeg(const std::string &filename, const mindspore::MSTensor &image, int quality) { - std::shared_ptr image_de_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMSTensor(image, &image_de_tensor)); - RETURN_IF_NOT_OK(mindspore::dataset::WriteJpeg(filename, image_de_tensor, quality)); - return Status::OK(); -} - -// WritePNG Function. -Status WritePng(const std::string &filename, const mindspore::MSTensor &image, int compression_level) { - std::shared_ptr image_de_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMSTensor(image, &image_de_tensor)); - RETURN_IF_NOT_OK(mindspore::dataset::WritePng(filename, image_de_tensor, compression_level)); - return Status::OK(); -} -#endif // not ENABLE_ANDROID -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/callback/callback_manager.cc b/mindspore-lite/minddata/dataset/callback/callback_manager.cc deleted file mode 100644 index 36af5c119..000000000 --- a/mindspore-lite/minddata/dataset/callback/callback_manager.cc +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/callback/callback_manager.h" -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -namespace mindspore { -namespace dataset { - -void CallbackManager::AddCallbacks(std::vector> callbacks) { - callbacks_.insert(callbacks_.end(), callbacks.begin(), callbacks.end()); - for (size_t ind = 0; ind < callbacks_.size(); ind++) { - callbacks.push_back(callbacks[ind]); - if (callbacks[ind]->IsBeginNeeded()) { - begin_indices_.push_back(ind); - } - if (callbacks[ind]->IsEndNeeded()) { - end_indices_.push_back(ind); - } - if (callbacks[ind]->IsEpochBeginNeeded()) { - epoch_begin_indices_.push_back(ind); - } - if (callbacks[ind]->IsEpochEndNeeded()) { - epoch_end_indices_.push_back(ind); - } - if (callbacks[ind]->IsNStepBeginNeeded()) { - step_begin_indices_.push_back(ind); - } - if (callbacks[ind]->IsNStepEndNeeded()) { - step_end_indices_.push_back(ind); - } - } -} - -Status CallbackManager::Init(DatasetOp *op) { - RETURN_UNEXPECTED_IF_NULL(op); - op_ = op; - // turn the flag on if callback is set - enabled_ = !callbacks_.empty(); - - // error check for each of the callbacks - for (auto &cb : callbacks_) { - CHECK_FAIL_RETURN_UNEXPECTED(cb->step_size() > 0, "callback step_size needs to be greater than 0."); - } - - return Status::OK(); -} - -Status CallbackManager::Begin(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - - // Now do the actual callback - for (size_t ind : begin_indices_) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSBegin(cb_param)); - } - return Status::OK(); -} - -Status CallbackManager::EpochBegin(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - - // only wait if there are callbacks to call - if (epoch_begin_indices_.size() > 0) { - RETURN_IF_NOT_OK(op_->WaitForWorkers()); - } - - // Now do the actual callback - for (size_t ind : epoch_begin_indices_) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSEpochBegin(cb_param)); - } - - // wakeup all the workers threads and collector thread - RETURN_IF_NOT_OK(op_->PostForWorkers()); - - return Status::OK(); -} - -Status CallbackManager::StepBegin(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - - // Now do the actual callback - for (size_t ind : step_begin_indices_) { - if ((cb_param.cur_epoch_step_num_ - 1) % callbacks_[ind]->step_size() == 0) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSNStepBegin(cb_param)); - } - } - return Status::OK(); -} - -Status CallbackManager::End(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - // return Status::OK() if no end is needed - RETURN_OK_IF_TRUE(end_indices_.empty()); - - // Now do the actual callback - for (size_t ind : end_indices_) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSEnd(cb_param)); - } - return Status::OK(); -} - -Status CallbackManager::EpochEnd(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - - // Now do the actual callback - for (size_t ind : epoch_end_indices_) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSEpochEnd(cb_param)); - } - return Status::OK(); -} - -Status CallbackManager::StepEnd(const CallbackParam &cb_param) { - RETURN_OK_IF_TRUE(!enabled_); - RETURN_UNEXPECTED_IF_NULL(op_); - - // Now do the actual callback - for (size_t ind : step_end_indices_) { - if ((cb_param.cur_epoch_step_num_ - 1) % callbacks_[ind]->step_size() == 0) { - RETURN_IF_NOT_OK(callbacks_[ind]->DSNStepEnd(cb_param)); - } - } - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/callback/callback_manager.h b/mindspore-lite/minddata/dataset/callback/callback_manager.h deleted file mode 100644 index d37ce5320..000000000 --- a/mindspore-lite/minddata/dataset/callback/callback_manager.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_MANAGER_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_MANAGER_H - -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// forward declare to avoid cyclic include of dataset_op.h -class DatasetOp; - -/// This class manages all the callbacks that are associated with a single DatasetOp. For now, only MapOp supports this. -class CallbackManager { - public: - /// \brief CallbackManager default constructor. Init needs to be called before using the created instance. - CallbackManager() : enabled_(false) {} - - ~CallbackManager() = default; - - /// \brief - /// \param [in] callbacks list of callbacks to perform - void AddCallbacks(std::vector> callbacks); - - /// \brief set callbacks to empty - void ClearCallbacks() { callbacks_.clear(); } - - /// \brief DatasetOp needs to call Init if it wishes to use callback, Init will set enabled_ to true - /// \param[in] op, this pointer is used for Callback Manager to Pause Worker threads - /// \return Status - Status Init(DatasetOp *op); - - /// \brief callback function called at the start of the first row - /// \return Status - Status Begin(const CallbackParam &); - - /// \brief callback function called at the start of each epoch - /// \return Status - Status EpochBegin(const CallbackParam &); - - /// \brief callback function called at the start of each row - /// \return Status - Status StepBegin(const CallbackParam &); - - /// \brief callback function called after the last row is processed - /// \return Status - Status End(const CallbackParam &); - - /// \brief callback function called at the end of each epoch - /// \return Status - Status EpochEnd(const CallbackParam &); - - /// \brief callback function called at the the end of each row - /// \return Status - Status StepEnd(const CallbackParam &); - - private: - bool enabled_; // flag to enable callback, if false, all functions would return immediately - DatasetOp *op_ = nullptr; // back pointer to DatasetOp, raw pointer to avoid circular ownership - std::vector> callbacks_; // list of callbacks the DatasetOp needs to call - std::vector begin_indices_; - std::vector end_indices_; - std::vector epoch_begin_indices_; - std::vector epoch_end_indices_; - std::vector step_begin_indices_; - std::vector step_end_indices_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_MANAGER_H diff --git a/mindspore-lite/minddata/dataset/callback/callback_param.h b/mindspore-lite/minddata/dataset/callback/callback_param.h deleted file mode 100644 index 70f465690..000000000 --- a/mindspore-lite/minddata/dataset/callback/callback_param.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_PARAM_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_PARAM_H - -#include - -namespace mindspore { -namespace dataset { - -/// Callback Param is the object a DatasetOp uses to pass run-time information to user defined function. -/// This is a prototype for now, more fields will be added -class CallbackParam { - public: - CallbackParam(int64_t epoch_num, int64_t cur_epoch_step, int64_t total_step_num) - : cur_epoch_num_(epoch_num), cur_epoch_step_num_(cur_epoch_step), cur_step_num_(total_step_num) {} - - ~CallbackParam() = default; - - // these are constant public fields for easy access and consistency with python cb_param - // the names and orders are consistent with batchInfo - const int64_t cur_epoch_num_; // current epoch - const int64_t cur_epoch_step_num_; // step number of the current epoch - const int64_t cur_step_num_; // step number since the first row -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CALLBACK_PARAM_H diff --git a/mindspore-lite/minddata/dataset/callback/ds_callback.h b/mindspore-lite/minddata/dataset/callback/ds_callback.h deleted file mode 100644 index d2beca100..000000000 --- a/mindspore-lite/minddata/dataset/callback/ds_callback.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_DS_CALLBACK_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_DS_CALLBACK_H - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/callback_param.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -class DSCallback { - public: - /// \brief constructor of DSCallback, this is the base class for all front end specific callbacks - /// \param step_size number of steps to call DSNStepBegin() - explicit DSCallback(int32_t step_size = 1) : step_size_(step_size) {} - - /// \brief Destructor - virtual ~DSCallback() = default; - - /// \brief actual callback function for begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSBegin(const CallbackParam &cb_param) = 0; - - /// \brief actual callback function for epoch_begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSEpochBegin(const CallbackParam &cb_param) = 0; - - /// \brief actual callback function for step_begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSNStepBegin(const CallbackParam &cb_param) = 0; - - /// \brief actual callback function for end, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSEnd(const CallbackParam &cb_param) = 0; - - /// \brief actual callback function epoch_end begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSEpochEnd(const CallbackParam &cb_param) = 0; - - /// \brief actual callback function for step_end, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - virtual Status DSNStepEnd(const CallbackParam &cb_param) = 0; - - /// \brief predicate function, whether begin callback is needed - /// \return bool - virtual bool IsBeginNeeded() = 0; - - /// \brief predicate function, whether epoch_begin callback is needed - /// \return bool - virtual bool IsEpochBeginNeeded() = 0; - - /// \brief predicate function, whether step_begin callback is needed - /// \return bool - virtual bool IsNStepBeginNeeded() = 0; - - /// \brief predicate function, whether end callback is needed - /// \return bool - virtual bool IsEndNeeded() = 0; - - /// \brief predicate function, whether epoch_end callback is needed - /// \return bool - virtual bool IsEpochEndNeeded() = 0; - - /// \brief predicate function, whether step_end callback is needed - /// \return bool - virtual bool IsNStepEndNeeded() = 0; - - /// \brief getter - /// \return step_size - int32_t step_size() const { return step_size_; } - - protected: - int32_t step_size_; // step begin/end will be called every step_size_ -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_DS_CALLBACK_H diff --git a/mindspore-lite/minddata/dataset/callback/py_ds_callback.cc b/mindspore-lite/minddata/dataset/callback/py_ds_callback.cc deleted file mode 100644 index cf3fe8215..000000000 --- a/mindspore-lite/minddata/dataset/callback/py_ds_callback.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/callback/py_ds_callback.h" -#include "mindspore-lite/minddata/dataset/callback/callback_manager.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -Status PyDSCallback::DSBegin(const CallbackParam &cb_param) { - return PyDSCallback::ExecutePyfunc(begin_func_, cb_param); -} -Status PyDSCallback::DSEpochBegin(const CallbackParam &cb_param) { - return PyDSCallback::ExecutePyfunc(epoch_begin_func_, cb_param); -} -Status PyDSCallback::DSNStepBegin(const CallbackParam &cb_param) { - return PyDSCallback::ExecutePyfunc(step_begin_func_, cb_param); -} -Status PyDSCallback::DSEnd(const CallbackParam &cb_param) { return PyDSCallback::ExecutePyfunc(end_func_, cb_param); } - -Status PyDSCallback::DSEpochEnd(const CallbackParam &cb_param) { - return PyDSCallback::ExecutePyfunc(epoch_end_func_, cb_param); -} -Status PyDSCallback::DSNStepEnd(const CallbackParam &cb_param) { - return PyDSCallback::ExecutePyfunc(step_end_func_, cb_param); -} - -bool PyDSCallback::IsBeginNeeded() { return begin_needed_; } -bool PyDSCallback::IsEpochBeginNeeded() { return epoch_begin_needed_; } -bool PyDSCallback::IsNStepBeginNeeded() { return step_begin_needed_; } -bool PyDSCallback::IsNStepEndNeeded() { return step_end_needed_; } -bool PyDSCallback::IsEpochEndNeeded() { return epoch_end_needed_; } -bool PyDSCallback::IsEndNeeded() { return end_needed_; } - -Status PyDSCallback::ExecutePyfunc(py::function f, const CallbackParam &cb_param) { - { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - return Status(StatusCode::kMDPythonInterpreterFailure, "Python Interpreter is finalized"); - } - try { - f(cb_param); - } catch (const py::error_already_set &e) { - return Status(StatusCode::kMDPyFuncException, e.what()); - } - } - return Status::OK(); -} -void PyDSCallback::SetBegin(const py::function &f) { - begin_func_ = f; - begin_needed_ = true; -} -void PyDSCallback::SetEnd(const py::function &f) { - end_func_ = f; - end_needed_ = true; -} -void PyDSCallback::SetEpochBegin(const py::function &f) { - epoch_begin_func_ = f; - epoch_begin_needed_ = true; -} -void PyDSCallback::SetEpochEnd(const py::function &f) { - epoch_end_func_ = f; - epoch_end_needed_ = true; -} -void PyDSCallback::SetStepBegin(const py::function &f) { - step_begin_func_ = f; - step_begin_needed_ = true; -} -void PyDSCallback::SetStepEnd(const py::function &f) { - step_end_func_ = f; - step_end_needed_ = true; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/callback/py_ds_callback.h b/mindspore-lite/minddata/dataset/callback/py_ds_callback.h deleted file mode 100644 index c65a23f5e..000000000 --- a/mindspore-lite/minddata/dataset/callback/py_ds_callback.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_PY_DS_CALLBACK_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_PY_DS_CALLBACK_H - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "pybind11/pybind11.h" - -namespace mindspore { -namespace dataset { - -namespace py = pybind11; - -class PyDSCallback : public DSCallback { - public: - /// \brief constructor for PyDSCallback. This callback is for python front end - explicit PyDSCallback(int32_t step_size = 1) - : DSCallback(step_size), - begin_needed_(false), - epoch_begin_needed_(false), - step_begin_needed_(false), - end_needed_(false), - epoch_end_needed_(false), - step_end_needed_(false) {} - - ~PyDSCallback() override = default; - - void SetBegin(const py::function &f); - void SetEnd(const py::function &f); - void SetEpochBegin(const py::function &f); - void SetEpochEnd(const py::function &f); - void SetStepBegin(const py::function &f); - void SetStepEnd(const py::function &f); - - /// \brief actual callback function for begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSBegin(const CallbackParam &cb_param) override; - - /// \brief actual callback function for epoch_begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSEpochBegin(const CallbackParam &cb_param) override; - - /// \brief actual callback function for step_begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSNStepBegin(const CallbackParam &cb_param) override; - - /// \brief actual callback function for end, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSEnd(const CallbackParam &cb_param) override; - - /// \brief actual callback function epoch_end begin, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSEpochEnd(const CallbackParam &cb_param) override; - - /// \brief actual callback function for step_end, needs to be overridden in the derived class - /// \param cb_param, callback parameter passed in from DatasetOp when calling the callback - /// \return Status - Status DSNStepEnd(const CallbackParam &cb_param) override; - - /// \brief predicate function, whether begin callback is needed - /// \return bool - bool IsBeginNeeded() override; - - /// \brief predicate function, whether epoch_begin callback is needed - /// \return bool - bool IsEpochBeginNeeded() override; - - /// \brief predicate function, whether step_begin callback is needed - /// \return bool - bool IsNStepBeginNeeded() override; - - /// \brief predicate function, whether end callback is needed - /// \return bool - bool IsEndNeeded() override; - - /// \brief predicate function, whether epoch_end callback is needed - /// \return bool - bool IsEpochEndNeeded() override; - - /// \brief predicate function, whether step_end callback is needed - /// \return bool - bool IsNStepEndNeeded() override; - - /// \brief helper function to acquire GIL then execute a pyfunc - /// \param f the python function - /// \param cb_param - /// \return Status - static Status ExecutePyfunc(py::function f, const CallbackParam &cb_param); - - private: - py::function begin_func_; - py::function epoch_begin_func_; - py::function step_begin_func_; - py::function end_func_; - py::function epoch_end_func_; - py::function step_end_func_; - - bool begin_needed_; - bool epoch_begin_needed_; - bool step_begin_needed_; - bool end_needed_; - bool epoch_end_needed_; - bool step_end_needed_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_PY_DS_CALLBACK_H diff --git a/mindspore-lite/minddata/dataset/core/ascend_resource.cc b/mindspore-lite/minddata/dataset/core/ascend_resource.cc deleted file mode 100644 index 37bc19738..000000000 --- a/mindspore-lite/minddata/dataset/core/ascend_resource.cc +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/core/ascend_resource.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status AscendResource::InitResource(uint32_t device_id) { - ResourceInfo resource; - resource.deviceIds.insert(device_id); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string err_msg = "Error in Init D-chip:" + std::to_string(ret); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int cur_device_id = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(cur_device_id); - processor_ = std::shared_ptr(AclAdapter::GetInstance().CreateAclProcess(context, false, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(processor_.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string err_msg = "Error in Init resource:" + std::to_string(ret); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - MS_LOG(INFO) << "Ascend resource all initialized!"; - return Status::OK(); -} - -Status AscendResource::FinalizeResource() { - AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - return Status::OK(); -} - -Status AscendResource::Sink(const mindspore::MSTensor &host_input, std::shared_ptr *device_input) { - std::shared_ptr de_input; - Status rc = dataset::Tensor::CreateFromMemory(dataset::TensorShape(host_input.Shape()), - MSTypeToDEType(static_cast(host_input.DataType())), - (const uchar *)(host_input.Data().get()), &de_input); - RETURN_IF_NOT_OK(rc); - if (!IsNonEmptyJPEG(de_input)) { - std::string err_msg = "Dvpp operators can only support processing JPEG image"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - APP_ERROR ret = AclAdapter::GetInstance().H2D_Sink(processor_.get(), de_input, device_input); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string err_msg = "Error in data sink process:" + std::to_string(ret); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - MS_LOG(INFO) << "Process data sink successfully"; - return Status::OK(); -} - -Status AscendResource::Pop(const std::shared_ptr &device_output, std::shared_ptr *host_output) { - APP_ERROR ret = AclAdapter::GetInstance().D2H_Pop(processor_.get(), device_output, host_output); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string err_msg = "Error in data pop processing:" + std::to_string(ret); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status AscendResource::DeviceDataRelease() { - APP_ERROR ret = AclAdapter::GetInstance().DeviceMemoryRelease(processor_.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string err_msg = "Error in device data release:" + std::to_string(ret); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AscendResource::GetInstance() { return processor_; } - -void *AscendResource::GetContext() { return AclAdapter::GetInstance().GetContextFromAclProcess(processor_.get()); } - -void *AscendResource::GetStream() { return AclAdapter::GetInstance().GetStreamFromAclProcess(processor_.get()); } - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/ascend_resource.h b/mindspore-lite/minddata/dataset/core/ascend_resource.h deleted file mode 100644 index e705ad5dc..000000000 --- a/mindspore-lite/minddata/dataset/core/ascend_resource.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_ASCEND_RESOURCE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_ASCEND_RESOURCE_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -namespace mindspore { -namespace dataset { - -class AscendResource : public DeviceResource { - public: - AscendResource() = default; - ~AscendResource() = default; - - Status InitResource(uint32_t device_id) override; - - Status FinalizeResource() override; - - Status Sink(const mindspore::MSTensor &host_input, std::shared_ptr *device_input) override; - - Status Pop(const std::shared_ptr &device_output, std::shared_ptr *host_output) override; - - std::shared_ptr GetInstance() override; - - Status DeviceDataRelease() override; - - void *GetContext() override; - - void *GetStream() override; - - private: - std::shared_ptr processor_; -}; - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_ASCEND_RESOURCE_H_ diff --git a/mindspore-lite/minddata/dataset/core/client.cc b/mindspore-lite/minddata/dataset/core/client.cc deleted file mode 100644 index 2485ae036..000000000 --- a/mindspore-lite/minddata/dataset/core/client.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/client.h" -#include "mindspore-lite/minddata/dataset/util/services.h" - -namespace mindspore { -namespace dataset { -// This is a one-time global initializer which includes the call to instantiate singletons. -// It is external api call and not a member of the GlobalContext directly. -Status GlobalInit() { - // Bring up all the services (logger, task, bufferpool) - return (Services::CreateInstance()); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/client.h b/mindspore-lite/minddata/dataset/core/client.h deleted file mode 100644 index 1ba0ea703..000000000 --- a/mindspore-lite/minddata/dataset/core/client.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CLIENT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CLIENT_H_ - -// client.h -// Include file for DE client functions - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h" -#endif - -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h" -#endif - -#include "mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/project_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/take_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// This is a one-time global initializer that needs to be called at the -// start of any minddata applications. -extern Status GlobalInit(); -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CLIENT_H_ diff --git a/mindspore-lite/minddata/dataset/core/config_manager.cc b/mindspore-lite/minddata/dataset/core/config_manager.cc deleted file mode 100644 index 831405f29..000000000 --- a/mindspore-lite/minddata/dataset/core/config_manager.cc +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -#include -#include -#include - -#include "include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "nlohmann/json.hpp" -#include "util/path.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -ConfigManager::ConfigManager() - : num_parallel_workers_(kCfgParallelWorkers), - worker_connector_size_(kCfgWorkerConnectorSize), - op_connector_size_(kCfgOpConnectorSize), - sending_batches_(kCfgSendingBatch), - rank_id_(kCfgDefaultRankId), - seed_(kCfgDefaultSeed), - monitor_sampling_interval_(kCfgMonitorSamplingInterval), - callback_timout_(kCfgCallbackTimeout), - cache_host_(kCfgDefaultCacheHost), - cache_port_(kCfgDefaultCachePort), - num_connections_(kDftNumConnections), - numa_enable_(false), - cache_prefetch_size_(kDftCachePrefetchSize), - auto_num_workers_(kDftAutoNumWorkers), - num_cpu_threads_(std::thread::hardware_concurrency()), - auto_num_workers_num_shards_(1), - auto_worker_config_(0), - enable_shared_mem_(true), - auto_offload_(false), - enable_autotune_(false), - save_autoconfig_(false), - autotune_interval_(kCfgAutoTuneInterval), - enable_watchdog_(true), - multiprocessing_timeout_interval_(kCfgMultiprocessingTimeoutInterval), - iterator_mode_({{"do_copy", true}, {"parallel_convert", false}}), - start_method_("fork") { - autotune_json_filepath_ = kEmptyString; - num_cpu_threads_ = num_cpu_threads_ > 0 ? num_cpu_threads_ : std::numeric_limits::max(); - num_parallel_workers_ = num_parallel_workers_ < num_cpu_threads_ ? num_parallel_workers_ : num_cpu_threads_; - std::string env_cache_host = common::GetEnv("MS_CACHE_HOST"); - std::string env_cache_port = common::GetEnv("MS_CACHE_PORT"); - if (!env_cache_host.empty()) { - cache_host_ = env_cache_host; - } - if (!env_cache_port.empty()) { - char *end = nullptr; - cache_port_ = static_cast(strtol(env_cache_port.c_str(), &end, kDecimal)); - if (*end != '\0') { - MS_LOG(WARNING) << "Cache port from env variable MS_CACHE_PORT is invalid\n"; - cache_port_ = 0; // cause the port range validation to generate an error during the validation checks - } - } -} - -// A print method typically used for debugging -void ConfigManager::Print(std::ostream &out) const { - // Don't show the test/internal ones. Only display the main ones here. - // fyi, boolalpha tells the output stream to write "true" and "false" for bools - out << "\nClient config settings :" - << "\nParallelOp workers : " << num_parallel_workers_ - << "\nParallelOp worker connector size : " << worker_connector_size_ - << "\nSize of each Connector : " << op_connector_size_ << std::endl; -} - -// Private helper function that takes a nlohmann json format and populates the settings -Status ConfigManager::FromJson(const nlohmann::json &j) { - RETURN_IF_NOT_OK(set_num_parallel_workers(j.value("numParallelWorkers", num_parallel_workers_))); - set_worker_connector_size(j.value("workerConnectorSize", worker_connector_size_)); - set_op_connector_size(j.value("opConnectorSize", op_connector_size_)); - set_seed(j.value("seed", seed_)); - set_monitor_sampling_interval(j.value("monitorSamplingInterval", monitor_sampling_interval_)); - set_fast_recovery(j.value("fast_recovery", fast_recovery_)); - set_error_samples_mode(j.value("error_samples_mode", error_samples_mode_)); - set_cache_host(j.value("cacheHost", cache_host_)); - set_cache_port(j.value("cachePort", cache_port_)); - set_num_connections(j.value("numConnections", num_connections_)); - set_cache_prefetch_size(j.value("cachePrefetchSize", cache_prefetch_size_)); - set_debug_mode(j.value("debug_mode_flag", debug_mode_flag_)); - set_multiprocessing_start_method(j.value("start_method", start_method_)); - return Status::OK(); -} - -// Loads a json file with the default settings and populates all the settings -Status ConfigManager::LoadFile(const std::string &settingsFile) { - Status rc; - if (!Path(settingsFile).Exists()) { - RETURN_STATUS_UNEXPECTED("Invalid file: settings file:" + settingsFile + - " is not exist, check input path of config 'load' API."); - } - // Some settings are mandatory, others are not (with default). If a setting - // is optional it will set a default value if the config is missing from the file. - std::ifstream in(settingsFile, std::ios::in); - try { - nlohmann::json js; - in >> js; - rc = FromJson(js); - in.close(); - } catch (const nlohmann::json::type_error &e) { - in.close(); - std::ostringstream ss; - ss << "Client file failed to load:\n" << e.what(); - std::string err_msg = ss.str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } catch (const std::exception &err) { - in.close(); - RETURN_STATUS_UNEXPECTED("Client file failed to load."); - } - return rc; -} - -// Setter function -Status ConfigManager::set_num_parallel_workers(int32_t num_parallel_workers) { - if (num_parallel_workers > num_cpu_threads_ || num_parallel_workers < 1) { - std::string err_msg = "Invalid Parameter, num_parallel_workers exceeds the boundary between 1 and " + - std::to_string(num_cpu_threads_) + ", as got " + std::to_string(num_parallel_workers) + "."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - num_parallel_workers_ = num_parallel_workers; - return Status::OK(); -} - -// Setter function -void ConfigManager::set_worker_connector_size(int32_t connector_size) { worker_connector_size_ = connector_size; } - -// Setter function -void ConfigManager::set_op_connector_size(int32_t connector_size) { op_connector_size_ = connector_size; } - -void ConfigManager::set_sending_batches(int64_t sending_batches) { sending_batches_ = sending_batches; } - -uint32_t ConfigManager::seed() const { return seed_; } - -void ConfigManager::set_rank_id(int32_t rank_id) { - if (rank_id_ == kCfgDefaultRankId) { - rank_id_ = rank_id; - } -} - -void ConfigManager::set_numa_enable(bool numa_enable) { numa_enable_ = numa_enable; } - -void ConfigManager::set_seed(uint32_t seed) { seed_ = seed; } - -void ConfigManager::set_monitor_sampling_interval(uint32_t interval) { monitor_sampling_interval_ = interval; } - -void ConfigManager::set_callback_timeout(uint32_t timeout) { callback_timout_ = timeout; } - -void ConfigManager::set_cache_host(std::string cache_host) { cache_host_ = std::move(cache_host); } - -void ConfigManager::set_cache_port(int32_t cache_port) { cache_port_ = cache_port; } - -void ConfigManager::set_num_connections(int32_t num_connections) { num_connections_ = num_connections; } - -void ConfigManager::set_cache_prefetch_size(int32_t cache_prefetch_size) { cache_prefetch_size_ = cache_prefetch_size; } - -Status ConfigManager::set_enable_autotune(bool enable, bool save_autoconfig, const std::string &json_filepath) { - enable_autotune_ = enable; - save_autoconfig_ = save_autoconfig; - - // Check if not requested to save AutoTune config - if (!save_autoconfig_) { - // No need for further processing, like process json_filepath input - return Status::OK(); - } - - Path jsonpath(json_filepath); - - if (jsonpath.IsDirectory()) { - std::string err_msg = "Invalid json_filepath parameter. <" + json_filepath + "> is a directory, not filename."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::string parent_path = jsonpath.ParentPath(); - if (parent_path != "") { - if (!Path(parent_path).Exists()) { - std::string err_msg = "Invalid json_filepath parameter. Directory <" + parent_path + "> does not exist."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } else { - // Set parent_path to current working directory - parent_path = "."; - } - - std::string real_path; - if (Path::RealPath(parent_path, real_path).IsError()) { - std::string err_msg = "Invalid json_filepath parameter. Cannot get real json_filepath <" + real_path + ">."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (access(real_path.c_str(), W_OK) == -1) { - std::string err_msg = "Invalid json_filepath parameter. No access to write to <" + real_path + ">."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (jsonpath.Exists()) { - // Note: Allow file to be overwritten (like serialize) - std::string err_msg = "Invalid json_filepath parameter. File: <" + json_filepath + "> already exists." + - " File will be overwritten with the AutoTuned data pipeline configuration."; - MS_LOG(WARNING) << err_msg; - } - - // Save the final AutoTune configuration JSON filepath name - autotune_json_filepath_ = json_filepath; - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/config_manager.h b/mindspore-lite/minddata/dataset/core/config_manager.h deleted file mode 100644 index 7a1776867..000000000 --- a/mindspore-lite/minddata/dataset/core/config_manager.h +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CONFIG_MANAGER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CONFIG_MANAGER_H_ - -#include -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -// Config settings for the client-side -// example config file: -// { -// "numParallelWorkers": 3 -// } -// - -namespace mindspore { -namespace dataset { -const char kEmptyString[] = ""; -const char kJsonExtension[] = ".json"; - -// The ConfigManager is a class for managing default values. When a user is constructing any objects -// in the framework, often they may choose to omit some settings instead of overriding them. -// This class manages some of the default values, for cases when the user does not manually specify -// those values. -class ConfigManager { - public: - ConfigManager(); - - // destructor - ~ConfigManager() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - void Print(std::ostream &out) const; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param cS - reference to the ConfigManager to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ConfigManager &cS) { - cS.Print(out); - return out; - } - - // Another debug print helper. Converts the print info to a string for you. - // @return The string version of the debug print - std::string ToString() const { - std::stringstream ss; - ss << *this; - return ss.str(); - } - - // Loads a json file with the default settings and populates all the settings - // @param settingsFile - A json file with a set of default settings - // @return Status error code - Status LoadFile(const std::string &settingsFile); - - // getter function - // @return The number of workers setting - int32_t num_parallel_workers() const { return num_parallel_workers_; } - - // getter function - // @return The queue size of the operator's output connector - int32_t op_connector_size() const { return op_connector_size_; } - - // getter function - // @return The sending batches that will send to device - int64_t sending_batches() const { return sending_batches_; } - - // getter function - // @return The internal worker-to-master connector queue size - int32_t worker_connector_size() const { return worker_connector_size_; } - - int32_t num_cpu_threads() const { return num_cpu_threads_; } - - // getter function - // @return The hostname of cache server - std::string cache_host() const { return cache_host_; } - - // getter function - // @return The port of cache server - int32_t cache_port() const { return cache_port_; } - - /// getter function - /// \return Number of tcp/ip connection - int32_t num_connections() const { return num_connections_; } - - /// getter function - /// \return Prefetch size - int32_t cache_prefetch_size() const { return cache_prefetch_size_; } - - /// getter function - /// \return auto_num_workers_ - bool auto_num_workers() const { return auto_num_workers_; } - - // setter function - // @param num_parallel_workers - The setting to apply to the config - // @return Status error code - Status set_num_parallel_workers(int32_t num_parallel_workers); - - // setter function - // @param connector_size - The setting to apply to the config - void set_worker_connector_size(int32_t connector_size); - - // setter function - // @param connector_size - The setting to apply to the config - void set_op_connector_size(int32_t connector_size); - - // setter function - // @param sending_batches - The setting to apply to the config - void set_sending_batches(int64_t sending_batches); - - // setter function - // @param cache_host - The hostname of cache server - void set_cache_host(std::string cache_host); - - // setter function - // @param cache_port - The port of cache server - void set_cache_port(int32_t cache_port); - - /// setter function - /// \param num_connections - void set_num_connections(int32_t num_connections); - - /// setter function - /// \param cache_prefetch_size - void set_cache_prefetch_size(int32_t cache_prefetch_size); - - /// setter function - /// \param numa_switch - void set_numa_enable(bool numa_enable); - - /// getter function - /// Now we want to separate the numa link to _c_dataengine in the CMakeLists, - /// so we want user to choose whether to open numa switch. - /// @return Get the current numa switch state. - bool numa_enable() const { return numa_enable_; } - - // getter function - // This rank_id is for numa and device_queue, one process work with only one rank_id - // for standalone scenario, this rank_id may come from env 'CUDA_VISIBLE_DEVICES', - // but for distribute scenario, this rank_id come from _get_global_rank() in python - // @return Get the current device id, for one process, it's only with one rank_id. - int32_t rank_id() const { return rank_id_; } - - // setter function - // @param rank_id - Set the current device id - void set_rank_id(int32_t rank_id); - - uint32_t seed() const; - - // setter function - // @param seed - The default seed to use - void set_seed(uint32_t seed); - - // setter function - // @param interval - The setting to apply to the config - void set_monitor_sampling_interval(uint32_t interval); - - // getter function - // @return The interval of monitor sampling - uint32_t monitor_sampling_interval() const { return monitor_sampling_interval_; } - - // setter function - // @param auto_num_workers - whether assign threads to each op automatically - void set_auto_num_workers(bool auto_num_workers) { auto_num_workers_ = auto_num_workers; } - - // setter function - // this function will be called when a distributed sampler (RT and Obj) is created and will be used by AutoWorkerPass - // This is to get around the limitation of PreBuildSampler (which doesn't have a getter for sharding params) - // @param num_shards - void set_num_shards_for_auto_num_workers(int32_t num_shards) { auto_num_workers_num_shards_ = num_shards; } - - // getter function, will be called by AutoNumWorker, user discretion above AutoNumWorker is advised - // @param num_shards_ - int32_t get_num_shards_for_auto_num_workers() const { return auto_num_workers_num_shards_; } - - // setter function - // @param timeout - The setting to apply to the config - void set_callback_timeout(uint32_t timeout); - - // getter function - // @return The timeout DSWaitedCallback would wait for before raising an error - uint32_t callback_timeout() const { return callback_timout_; } - - // getter function - // E.g. 0 would corresponds to a 1:1:1 ratio of num_worker among leaf batch and map. - // please refer to AutoWorkerPass for detail on what each option is. - // @return The experimental config used by AutoNumWorker, each 1 refers to a different setup configuration - uint8_t get_auto_worker_config() const { return auto_worker_config_; } - - // setter function - // E.g. set the value of 0 would corresponds to a 1:1:1 ratio of num_worker among leaf batch and map. - // please refer to AutoWorkerPass for detail on what each option is. - // @return The experimental config used by AutoNumWorker, each 1 refers to a different setup configuration - void set_auto_worker_config_(uint8_t cfg) { auto_worker_config_ = cfg; } - - // setter function - // @param enable - To enable multiprocessing to use shared memory - void set_enable_shared_mem(bool enable) { enable_shared_mem_ = enable; } - - // getter function - // @return - Flag to indicate whether shared memory for multi-processing is enabled - bool enable_shared_mem() const { return enable_shared_mem_; } - - // setter function - // @param offload - To enable automatic offloading of dataset ops - void set_auto_offload(bool offload) { auto_offload_ = offload; } - - // getter function - // @return - Flag to indicate whether automatic offloading is enabled for the dataset - bool get_auto_offload() const { return auto_offload_; } - - // setter function - // @param enable - To enable autotune - // @param bool save_autoconfig - True if should save AutoTune data pipeline configuration - // @param json_filepath - JSON filepath where the final AutoTune data pipeline will be generated - // @return Status error code - Status set_enable_autotune(bool enable, bool save_autoconfig, const std::string &json_filepath); - - // getter function - // @return - Flag to indicate whether autotune is enabled - bool enable_autotune() const { return enable_autotune_; } - - // getter function - // @return - Flag to indicate whether to save AutoTune configuration - bool save_autoconfig() const { return save_autoconfig_; } - - // getter function - // @return - The final AutoTune configuration JSON filepath - std::string get_autotune_json_filepath() { return autotune_json_filepath_; } - - // getter function - // @return - autotune interval in steps - int64_t autotune_interval() const { return autotune_interval_; } - - // setter function - // @param interval - autotune interval in steps - void set_autotune_interval(int64_t interval) { autotune_interval_ = interval; } - - // setter function - // @param enable - To enable watchdog python thread - void set_enable_watchdog(bool enable) { enable_watchdog_ = enable; } - - // getter function - // @return - Flag to indicate whether watchdog python thread is enabled - bool enable_watchdog() const { return enable_watchdog_; } - - // getter function - // @return - multiprocessing timeout interval in seconds - uint32_t multiprocessing_timeout_interval() const { return multiprocessing_timeout_interval_; } - - // setter function - // @param interval - multiprocessing timeout interval in seconds - void set_multiprocessing_timeout_interval(uint32_t interval) { multiprocessing_timeout_interval_ = interval; } - - // setter function - // @param is_dynamic - Indicate whether the dataset is dynamic-shape - void set_dynamic_shape(bool is_dynamic) { dynamic_shape_ = is_dynamic; } - - // getter function - // @return - Flag to indicate whether the dataset is dynamic-shape - bool dynamic_shape() const { return dynamic_shape_; } - - // setter function - // @notes User must also set the seed to be able to get same augmentations - // @notes Fast recovery can cause slightly different random augmentations than original run - // (System default = true) - // @param fast_recovery - Set whether MD pipeline recovers fast in failover reset - void set_fast_recovery(const bool fast_recovery) { fast_recovery_ = fast_recovery; } - - // getter function - // @return - Flag to indicate whether md pipeline recovers fast in failover reset - bool fast_recovery() const { return fast_recovery_; } - - // setter function - // @param debug_mode_flag - Set whether debug mode is on. When enabled, the dataset pipeline runs synchronously and - // sequentially. - void set_debug_mode(const bool debug_mode_flag) { debug_mode_flag_ = debug_mode_flag; } - - // getter function - // @return - Flag to indicate whether the debug mode is on - bool get_debug_mode() const { return debug_mode_flag_; } - - // setter function - // @param error_samples_mode - Set the method in which erroneous samples should be processed - // (System default = ErrorSamplesMode::kReturn) - // @notes For replacement of erroneous samples, MD will select a deterministic but "random" sample. - void set_error_samples_mode(const ErrorSamplesMode error_samples_mode) { error_samples_mode_ = error_samples_mode; } - - // getter function - // @return - The method in which erroneous samples should be processed in a dataset pipeline - // @notes This method is used for external configuration API which returns integer type - int32_t get_error_samples_mode() const { return static_cast(error_samples_mode_); } - - // getter function - // @return - The method in which erroneous samples should be processed in a dataset pipeline - // @notes This method is used for internal processing, using enum type - ErrorSamplesMode error_samples_mode() const { return error_samples_mode_; } - - // setter function - // @param tensor_copy - Whether dataset iterator creates a Tensor from numpy.ndarray without copy. - // @param parallel_convert - Whether dataset iterator starts a thread to organize Tensors to output. - void set_iterator_mode(const bool tensor_copy, const bool parallel_convert) { - iterator_mode_["do_copy"] = tensor_copy; - iterator_mode_["parallel_convert"] = parallel_convert; - } - - // getter function - // @return - iterator mode map contains the value of `do_copy` and `parallel_convert`. - const std::map &get_iterator_mode() const { return iterator_mode_; } - - // @param start_method - Set the multiprocessing start method in ['fork', 'spawn'] - void set_multiprocessing_start_method(std::string start_method) { start_method_ = start_method; } - - // getter function - // @return - Indicate the multiprocessing start method - std::string get_multiprocessing_start_method() { return start_method_; } - - private: - // Private helper function that takes a nlohmann json format and populates the settings - // @param j - The json nlohmann json info - Status FromJson(const nlohmann::json &j); - - int32_t num_parallel_workers_; - int32_t worker_connector_size_; - int32_t op_connector_size_; - int64_t sending_batches_; - // This rank_id is for numa and device_queue, one process work with only one rank_id, - // for standalone scenario, this rank_id may come from env 'CUDA_VISIBLE_DEVICES', - // but for distribute scenario, this rank_id come from _get_global_rank() in python - int32_t rank_id_; - uint32_t seed_; - uint32_t monitor_sampling_interval_; - uint32_t callback_timout_; - std::string cache_host_; - int32_t cache_port_; - int32_t num_connections_; - bool numa_enable_; - int32_t cache_prefetch_size_; - bool auto_num_workers_; - int32_t num_cpu_threads_; - int32_t auto_num_workers_num_shards_; - uint8_t auto_worker_config_; - bool enable_shared_mem_; - bool auto_offload_; - bool enable_autotune_; - bool save_autoconfig_; // True if should save AutoTune configuration - int64_t autotune_interval_; - bool enable_watchdog_; // Watchdog python thread enabled flag - uint32_t multiprocessing_timeout_interval_; // Multiprocessing timeout interval in seconds - std::string autotune_json_filepath_; // Filepath name of the final AutoTune Configuration JSON file - bool dynamic_shape_{false}; - bool fast_recovery_{true}; // Used for failover scenario to recover quickly or produce same augmentations - bool debug_mode_flag_{false}; // Indicator for debug mode - ErrorSamplesMode error_samples_mode_{ErrorSamplesMode::kReturn}; // The method to process erroneous samples - std::map iterator_mode_; - std::string start_method_; // fork or spawn -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CONFIG_MANAGER_H_ diff --git a/mindspore-lite/minddata/dataset/core/cv_tensor.cc b/mindspore-lite/minddata/dataset/core/cv_tensor.cc deleted file mode 100644 index 7bf829ce4..000000000 --- a/mindspore-lite/minddata/dataset/core/cv_tensor.cc +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -CVTensor::CVTensor(std::shared_ptr tensor) : Tensor(std::move(*tensor)) { - (void)this->MatInit(GetMutableBuffer(), shape_, type_, &mat_); -} - -Status CVTensor::CreateEmpty(const TensorShape &shape, DataType type, CVTensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - *out = std::make_shared(shape, type); - RETURN_UNEXPECTED_IF_NULL(*out); - int64_t byte_size = (*out)->SizeInBytes(); - // Don't allocate if we have a tensor with no elements. - if (byte_size != 0) { - RETURN_IF_NOT_OK((*out)->AllocateBuffer(byte_size)); - } - - return (*out)->MatInit((*out)->GetMutableBuffer(), (*out)->shape_, (*out)->type_, &(*out)->mat_); -} - -Status CVTensor::CreateFromMat(const cv::Mat &mat, const dsize_t rank, CVTensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - TensorPtr out_tensor; - cv::Mat mat_local = mat; - // if the input Mat's memory is not continuous, copy it to one block of memory - if (!mat.isContinuous()) { - mat_local = mat.clone(); - } - TensorShape shape({}); - if (mat.dims == 2 && rank == 2) { - shape = TensorShape({mat.rows, mat.cols}); - } else if (mat.dims == 2 && rank == 3) { - shape = TensorShape({mat.rows, mat.cols, mat.channels()}); - } else { - // the info of tensor is: dims = 3, size = (C, H, W), channels = 1 - RETURN_STATUS_UNEXPECTED("CreateFromMat: tensor should be in shape of or ."); - } - DataType type = DataType::FromCVType(mat_local.type()); - RETURN_IF_NOT_OK(CreateFromMemory(shape, type, mat_local.data, &out_tensor)); - *out = AsCVTensor(out_tensor); - return Status::OK(); -} - -std::pair, int> CVTensor::IsValidImage(uchar *data, const TensorShape &shape, const DataType &type) { - constexpr int64_t array_size = 2; - constexpr int64_t rank_two = 2; - constexpr int64_t rank_three = 3; - constexpr int64_t index = 2; - std::array size = {1, 1}; - if (shape.Rank() <= rank_two || (shape.Rank() == rank_three && shape[index] <= CV_CN_MAX)) { - uint16_t ch = 1; - if (shape.Rank() == rank_three) { - ch = static_cast(shape[2]); - } - if (shape.Rank() > 0) { - size[0] = static_cast(shape[0]); - } - if (shape.Rank() > 1) { - size[1] = static_cast(shape[1]); - } - if (type.AsCVType() == kCVInvalidType) { - return std::make_pair(size, -1); - } - int cv_type = CV_MAKETYPE(type.AsCVType(), ch); - // update the n which in matrx(m*n) for bytes type - if (type == DataType::DE_BYTES) { - offset_t *offset = reinterpret_cast(data); - size[1] = *(offset + 1) - *offset - 1; - } - return std::make_pair(size, cv_type); - } - return std::make_pair(size, -1); -} - -std::shared_ptr CVTensor::AsCVTensor(std::shared_ptr t) { - if (t == nullptr) { - return nullptr; - } - std::shared_ptr cv_t = std::dynamic_pointer_cast(t); - if (cv_t != nullptr) { - return cv_t; - } else { - return std::make_shared(t); - } -} - -Status CVTensor::MatInit(uchar *data, const TensorShape &shape, const DataType &type, cv::Mat *mat) { - RETURN_UNEXPECTED_IF_NULL(data); - RETURN_UNEXPECTED_IF_NULL(mat); - const int kShapeAsDefault = 2; - std::pair, int> cv_shape_type = IsValidImage(data, shape, type); - if (cv_shape_type.second == -1) { - std::vector sizes = shape.AsVector(); - std::vector sizes32(sizes.begin(), sizes.end()); // convert long to int for usage with OpenCV - - uint8_t cv_type = type.AsCVType(); - if (cv_type == kCVInvalidType) { - RETURN_STATUS_UNEXPECTED("Error in creating CV mat. Invalid type."); - } - *mat = cv::Mat(static_cast(shape.Rank()), &sizes32[0], cv_type, data); - } else { - if (type == DataType::DE_BYTES) { - *mat = cv::Mat(kShapeAsDefault, &(cv_shape_type.first[0]), cv_shape_type.second, data + kOffsetSize * 2); - } else { - *mat = cv::Mat(kShapeAsDefault, &(cv_shape_type.first[0]), cv_shape_type.second, data); - } - } - if (mat == nullptr) { - RETURN_STATUS_UNEXPECTED("Error in creating CV mat from dataset Tensor."); - } - return Status::OK(); -} - -Status CVTensor::Reshape(const TensorShape &shape) { - RETURN_IF_NOT_OK(Tensor::Reshape(shape)); - RETURN_IF_NOT_OK(this->MatInit(GetMutableBuffer(), shape_, type_, &mat_)); - return Status::OK(); -} - -Status CVTensor::ExpandDim(const dsize_t &axis) { - RETURN_IF_NOT_OK(Tensor::ExpandDim(axis)); - RETURN_IF_NOT_OK(this->MatInit(GetMutableBuffer(), shape_, type_, &mat_)); - return Status::OK(); -} - -void CVTensor::Squeeze() { - Tensor::Squeeze(); - Status rc = this->MatInit(GetMutableBuffer(), shape_, type_, &mat_); - if (rc.IsError()) { - MS_LOG(ERROR) << "Squeeze failed, error details is " << rc; - } -} - -Status CVTensor::MatAtIndex(const std::vector &index, cv::Mat *mat) { - RETURN_UNEXPECTED_IF_NULL(mat); - uchar *start = nullptr; - TensorShape remaining({-1}); - RETURN_IF_NOT_OK(this->StartAddrOfIndex(index, &start, &remaining)); - RETURN_IF_NOT_OK(this->MatInit(start, remaining, type_, mat)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/cv_tensor.h b/mindspore-lite/minddata/dataset/core/cv_tensor.h deleted file mode 100644 index 1a84f4d67..000000000 --- a/mindspore-lite/minddata/dataset/core/cv_tensor.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CV_TENSOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CV_TENSOR_H_ - -#include -#include -#include - -#include - -#include "include/securec.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -namespace mindspore { -namespace dataset { -using CVTensorPtr = std::shared_ptr; -class CVTensor : public Tensor { - public: - // Inherit Tensor's constructors - using Tensor::Tensor; - - /// Create a CVTensor from a given tensor. This constructor should not be used directly, use Create* instead. - /// The input tensor will be invalidated (i.e., the shape and type will be - /// set to unknown and the data buffer will point to null. - /// \note there is no memory copying here, the buffer will be assigned to the constructed tensor. - /// \param tensor - explicit CVTensor(std::shared_ptr tensor); - - /// Create CV tensor with type and shape. Items of the tensor would be uninitialized. - /// \param shape [in] shape of the output tensor - /// \param type [in] type of the output tensor - /// \param out [out] Generated tensor - /// \return Status code - static Status CreateEmpty(const TensorShape &shape, DataType type, CVTensorPtr *out); - - /// Create CV tensor from cv::Mat - /// \note This constructor allocates a new space in the memory and copies the CV::Mat buffer into it. - /// \param mat [in] cv::Mat to be copied into the new tensor. - /// \param shape [in] the rank of output CVTensor. - /// \param out [out] Generated tensor - /// \return Status code - static Status CreateFromMat(const cv::Mat &mat, const dsize_t rank, CVTensorPtr *out); - - ~CVTensor() override = default; - - /// Static function to cast a given Tensor as CVTensor. If the input tensor is already of type CVTensor, - /// this function would be treated as a no-op. Fot other tensor types, a new CVTensor is created based on the data - /// provided. The Passed Tensor will be invalidated. - /// \note the input tensor will be invalidated. - /// \note there is no memory copying here, the buffer will be assigned to the constructed tensor. - /// \param tensor [in] - /// \return CVTensor - static std::shared_ptr AsCVTensor(std::shared_ptr tensor); - - /// Get a reference to the CV::Mat - /// \return a reference to the internal CV::Mat - cv::Mat &mat() { return mat_; } - - /// Get a copy of the CV::Mat - /// \return a copy of internal CV::Mat - cv::Mat matCopy() const { return mat_.clone(); } - - /// Static function to check if the passed information (shape and type) can be treated as a valid description - /// of an image in OpenCV. Moreover, it returns OpenCV shape and type - /// For example, if the shape is <512,512,3> and type is DE_UINT8, the output would be [512,512] and CV_8UC3. - /// In case of invalid shape or type, the function will return pair - /// \param shape [in] TensorShape - /// \param type [in] DataType - /// \return std::pair of OpenCV shape and type - static std::pair, int> IsValidImage(uchar *data, const TensorShape &shape, const DataType &type); - - Status Reshape(const TensorShape &shape) override; - - Status ExpandDim(const dsize_t &axis) override; - - void Squeeze() override; - - Status MatAtIndex(const std::vector &index, cv::Mat *mat); - - private: - /// Opencv Mat object wrapping the raw data of the tensor. - /// Modifying the content of the matrix, modifies the tensor. - cv::Mat mat_; - - /// Create cv::Mat from data, TensorShape and DataType - /// \param data [in] Pointer to the data in memory. - /// \param shape [in] Shape of the tensor. - /// \param type [in] Type of the tensor. - /// \param mat [out] cv::Mat initialized with the provided data. - /// \return Status code - Status MatInit(uchar *data, const TensorShape &shape, const DataType &type, cv::Mat *mat); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_CV_TENSOR_H_ diff --git a/mindspore-lite/minddata/dataset/core/data_type.cc b/mindspore-lite/minddata/dataset/core/data_type.cc deleted file mode 100644 index 48bf54e20..000000000 --- a/mindspore-lite/minddata/dataset/core/data_type.cc +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#ifdef ENABLE_CLOUD_FUSION_INFERENCE -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -#endif -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -uint8_t DataType::SizeInBytes() const { - if (type_ < DataType::NUM_OF_TYPES) { - return kTypeInfo[type_].sizeInBytes_; - } else { - return 0; - } -} - -#ifdef ENABLE_CLOUD_FUSION_INFERENCE -py::dtype DataType::AsNumpyType() const { - if (type_ < DataType::NUM_OF_TYPES) { - return py::dtype(kTypeInfo[type_].pybindType_); - } else { - return py::dtype("unknown"); - } -} -#endif - -#if !defined(ENABLE_ANDROID) || defined(ENABLE_CLOUD_FUSION_INFERENCE) -uint8_t DataType::AsCVType() const { - uint8_t res = kCVInvalidType; - if (type_ < DataType::NUM_OF_TYPES) { - res = kTypeInfo[type_].cvType_; - } - - if (res == kCVInvalidType) { - std::string type_name = "unknown"; - if (type_ < DataType::NUM_OF_TYPES) { - type_name = std::string(kTypeInfo[type_].name_); - } - std::string err_msg = "Cannot convert [" + type_name + "] to OpenCV type."; - err_msg += " Currently unsupported data type: [uint32, int64, uint64, string]"; - MS_LOG(ERROR) << err_msg; - } - - return res; -} - -DataType DataType::FromCVType(int cv_type) { - auto depth = static_cast(cv_type) & static_cast(CV_MAT_DEPTH_MASK); - switch (depth) { - case CV_8S: - return DataType(DataType::DE_INT8); - case CV_8U: - return DataType(DataType::DE_UINT8); - case CV_16S: - return DataType(DataType::DE_INT16); - case CV_16U: - return DataType(DataType::DE_UINT16); - case CV_32S: - return DataType(DataType::DE_INT32); - case CV_16F: - return DataType(DataType::DE_FLOAT16); - case CV_32F: - return DataType(DataType::DE_FLOAT32); - case CV_64F: - return DataType(DataType::DE_FLOAT64); - default: - std::string err_msg = "Cannot convert from OpenCV type, unknown CV type."; - err_msg += " Currently supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]"; - MS_LOG(ERROR) << err_msg; - return DataType(DataType::DE_UNKNOWN); - } -} -#endif - -DataType::DataType(const std::string &type_str) { - if (type_str == "bool") { - type_ = DE_BOOL; - } else if (type_str == "int8") { - type_ = DE_INT8; - } else if (type_str == "uint8") { - type_ = DE_UINT8; - } else if (type_str == "int16") { - type_ = DE_INT16; - } else if (type_str == "uint16") { - type_ = DE_UINT16; - } else if (type_str == "int32") { - type_ = DE_INT32; - } else if (type_str == "uint32") { - type_ = DE_UINT32; - } else if (type_str == "int64") { - type_ = DE_INT64; - } else if (type_str == "uint64") { - type_ = DE_UINT64; - } else if (type_str == "float16") { - type_ = DE_FLOAT16; - } else if (type_str == "float32") { - type_ = DE_FLOAT32; - } else if (type_str == "float64") { - type_ = DE_FLOAT64; - } else if (type_str == "string") { - type_ = DE_STRING; - } else if (type_str == "bytes") { - type_ = DE_BYTES; -#ifdef ENABLE_CLOUD_FUSION_INFERENCE - } else if (type_str == "python") { - type_ = DE_PYTHON; -#endif - } else { - type_ = DE_UNKNOWN; - } -} - -std::string DataType::ToString() const { - if (type_ < DataType::NUM_OF_TYPES) { - return kTypeInfo[type_].name_; - } else { - return "unknown"; - } -} - -#ifdef ENABLE_CLOUD_FUSION_INFERENCE -DataType DataType::FromNpArray(const py::array &arr) { - if (py::isinstance>(arr)) { - return DataType(DataType::DE_BOOL); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_INT8); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_UINT8); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_INT16); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_UINT16); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_INT32); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_UINT32); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_INT64); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_UINT64); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_FLOAT16); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_FLOAT32); - } else if (py::isinstance>(arr)) { - return DataType(DataType::DE_FLOAT64); - } else if (arr.dtype().kind() == 'U') { - return DataType(DataType::DE_STRING); - } else if (arr.dtype().kind() == 'S') { - return DataType(DataType::DE_BYTES); - } else { - if (arr.size() == 0) { - MS_LOG(ERROR) << "Please check input data, the data of numpy array is empty."; - } - std::string err_msg = "Cannot convert from numpy type. Unknown data type is returned!"; - err_msg += - " Currently supported data type: [int8, uint8, int16, uint16, int32, uint32, int64, uint64, float16, float32, " - "float64, string, bytes]"; - MS_LOG(ERROR) << err_msg; - return DataType(DataType::DE_UNKNOWN); - } -} - -std::string DataType::GetPybindFormat() const { - std::string res; - if (type_ < DataType::NUM_OF_TYPES) { - res = kTypeInfo[type_].pybindFormatDescriptor_; - } - - if (res.empty()) { - MS_LOG(ERROR) << "Cannot convert from data type to pybind format descriptor!"; - } - return res; -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/data_type.h b/mindspore-lite/minddata/dataset/core/data_type.h deleted file mode 100644 index 06621384a..000000000 --- a/mindspore-lite/minddata/dataset/core/data_type.h +++ /dev/null @@ -1,398 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DATA_TYPE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DATA_TYPE_H_ - -#if !defined(ENABLE_ANDROID) || defined(ENABLE_CLOUD_FUSION_INFERENCE) -#include -#endif - -#include -#include - -#ifdef ENABLE_CLOUD_FUSION_INFERENCE -#include "pybind11/numpy.h" -#include "pybind11/pybind11.h" -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -namespace py = pybind11; -#else -#include "base/bfloat16.h" -#include "base/float16.h" -#endif -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -// Class that represents basic data types in DataEngine. -class DataType { - public: - enum Type : uint8_t { - DE_UNKNOWN = 0, - DE_BOOL, - DE_INT8, - DE_UINT8, - DE_INT16, - DE_UINT16, - DE_INT32, - DE_UINT32, - DE_INT64, - DE_UINT64, - DE_FLOAT16, - DE_FLOAT32, - DE_FLOAT64, - DE_STRING, - DE_BYTES, - DE_PYTHON, - NUM_OF_TYPES - }; - - struct TypeInfo { - const char *name_; // name to be represent the type while printing - const uint8_t sizeInBytes_; // number of bytes needed for this type - const char *pybindType_; // Python matching type, used in get_output_types - const std::string pybindFormatDescriptor_; // pybind format used for numpy types - const uint8_t cvType_; // OpenCv matching type - }; - -#ifdef ENABLE_CLOUD_FUSION_INFERENCE - static inline const TypeInfo kTypeInfo[] = { - // name, sizeInBytes, pybindType, pybindFormatDescriptor, openCV - {"unknown", 0, "object", "", kCVInvalidType}, // DE_UNKNOWN - {"bool", 1, "bool", py::format_descriptor::format(), CV_8U}, // DE_BOOL - {"int8", 1, "int8", py::format_descriptor::format(), CV_8S}, // DE_INT8 - {"uint8", 1, "uint8", py::format_descriptor::format(), CV_8U}, // DE_UINT8 - {"int16", 2, "int16", py::format_descriptor::format(), CV_16S}, // DE_INT16 - {"uint16", 2, "uint16", py::format_descriptor::format(), CV_16U}, // DE_UINT16 - {"int32", 4, "int32", py::format_descriptor::format(), CV_32S}, // DE_INT32 - {"uint32", 4, "uint32", py::format_descriptor::format(), kCVInvalidType}, // DE_UINT32 - {"int64", 8, "int64", py::format_descriptor::format(), kCVInvalidType}, // DE_INT64 - {"uint64", 8, "uint64", py::format_descriptor::format(), kCVInvalidType}, // DE_UINT64 - {"float16", 2, "float16", "e", CV_16F}, // DE_FLOAT16 - {"float32", 4, "float32", py::format_descriptor::format(), CV_32F}, // DE_FLOAT32 - {"float64", 8, "double", py::format_descriptor::format(), CV_64F}, // DE_FLOAT64 - {"string", 0, "str", "U", kCVInvalidType}, // DE_STRING - {"bytes", 0, "bytes", "S", CV_8U}, // DE_BYTES - {"python", 0, "object", "O", kCVInvalidType} // DE_PYTHON - }; -#else -#if !defined(ENABLE_ANDROID) || defined(ENABLE_CLOUD_FUSION_INFERENCE) - static inline const TypeInfo kTypeInfo[] = { - // name, sizeInBytes, pybindTypem formatDescriptor, openCV - {"unknown", 0, "object", "", kCVInvalidType}, // DE_UNKNOWN - {"bool", 1, "bool", "", CV_8U}, // DE_BOOL - {"int8", 1, "int8", "", CV_8S}, // DE_INT8 - {"uint8", 1, "uint8", "", CV_8U}, // DE_UINT8 - {"int16", 2, "int16", "", CV_16S}, // DE_INT16 - {"uint16", 2, "uint16", "", CV_16U}, // DE_UINT16 - {"int32", 4, "int32", "", CV_32S}, // DE_INT32 - {"uint32", 4, "uint32", "", kCVInvalidType}, // DE_UINT32 - {"int64", 8, "int64", "", kCVInvalidType}, // DE_INT64 - {"uint64", 8, "uint64", "", kCVInvalidType}, // DE_UINT64 - {"float16", 2, "float16", "", CV_16F}, // DE_FLOAT16 - {"float32", 4, "float32", "", CV_32F}, // DE_FLOAT32 - {"float64", 8, "double", "", CV_64F}, // DE_FLOAT64 - {"string", 0, "str", "", kCVInvalidType}, // DE_STRING - {"bytes", 0, "bytes", "", CV_8U}, // DE_BYTES - {"python", 0, "object", "O", kCVInvalidType} // DE_PYTHON - }; -#else - // android and no python - static inline const TypeInfo kTypeInfo[] = { - // name, sizeInBytes, formatDescriptor - {"unknown", 0, "object", "", kCVInvalidType}, // DE_UNKNOWN - {"bool", 1, "bool", ""}, // DE_BOOL - {"int8", 1, "int8", ""}, // DE_INT8 - {"uint8", 1, "uint8", ""}, // DE_UINT8 - {"int16", 2, "int16", ""}, // DE_INT16 - {"uint16", 2, "uint16", ""}, // DE_UINT16 - {"int32", 4, "int32", ""}, // DE_INT32 - {"uint32", 4, "uint32", "", kCVInvalidType}, // DE_UINT32 - {"int64", 8, "int64", "", kCVInvalidType}, // DE_INT64 - {"uint64", 8, "uint64", "", kCVInvalidType}, // DE_UINT64 - {"float16", 2, "float16", ""}, // DE_FLOAT16 - {"float32", 4, "float32", ""}, // DE_FLOAT32 - {"float64", 8, "double", ""}, // DE_FLOAT64 - {"string", 0, "str", "", kCVInvalidType}, // DE_STRING - {"bytes", 0, "bytes", ""}, // DE_BYTES - {"python", 0, "object", "O", kCVInvalidType} // DE_PYTHON - }; -#endif -#endif - // No arg constructor to create an unknown shape - DataType() : type_(DE_UNKNOWN) {} - - // Create a type from a given string - /// \param type_str - explicit DataType(const std::string &type_str); - - // Default destructor - ~DataType() = default; - - // Create a type from a given enum - /// \param type - constexpr explicit DataType(const Type &type) : type_(std::move(type)) {} - - constexpr bool operator==(const DataType a) const { return type_ == a.type_; } - - constexpr bool operator==(const Type a) const { return type_ == a; } - - constexpr bool operator!=(const DataType a) const { return type_ != a.type_; } - - constexpr bool operator!=(const Type a) const { return type_ != a; } - - // Disable this usage `if(d)` where d is of type DataType - /// \return return nothing since we deiable this function. - operator bool() = delete; - - // To be used in Switch/case - /// \return data type internal. - operator Type() const { return type_; } - - // The number of bytes needed to store one value of this type - /// \return the number of bytes of the type. - uint8_t SizeInBytes() const; - -#if !defined(ENABLE_ANDROID) || defined(ENABLE_CLOUD_FUSION_INFERENCE) - // Convert from DataType to OpenCV type - /// \return - uint8_t AsCVType() const; - - // Convert from OpenCV type to DataType - /// \param cv_type - /// \return - static DataType FromCVType(int cv_type); -#endif - - // Returns a string representation of the type - /// \return - std::string ToString() const; - - // returns true if the template type is the same as the Tensor type_ - /// \tparam T - /// \return true or false - template - bool IsCompatible() const { - return type_ == FromCType(); - } - - // returns true if the template type is the same as the Tensor type_ - /// \tparam T - /// \return true or false - template - bool IsLooselyCompatible() const; - - // << Stream output operator overload - /// \notes This allows you to print the info using stream operators - /// \param out - reference to the output stream being overloaded - /// \param rO - reference to the DataType to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const DataType &so) { - out << so.ToString(); - return out; - } - - template - static DataType FromCType(); - -#ifdef ENABLE_CLOUD_FUSION_INFERENCE - // Convert from DataType to Pybind type - /// \return - py::dtype AsNumpyType() const; - - // Convert from NP type to DataType - /// \param type - /// \return - static DataType FromNpType(const py::dtype &type); - - // Convert from NP array to DataType - /// \param py array - /// \return - static DataType FromNpArray(const py::array &arr); -#endif - - // Get the buffer string format of the current type. Used in pybind buffer protocol. - /// \return - std::string GetPybindFormat() const; - - bool IsSignedInt() const { - return type_ == DataType::DE_INT8 || type_ == DataType::DE_INT16 || type_ == DataType::DE_INT32 || - type_ == DataType::DE_INT64; - } - - bool IsUnsignedInt() const { - return type_ == DataType::DE_UINT8 || type_ == DataType::DE_UINT16 || type_ == DataType::DE_UINT32 || - type_ == DataType::DE_UINT64; - } - - bool IsInt() const { return IsSignedInt() || IsUnsignedInt(); } - - bool IsFloat() const { - return type_ == DataType::DE_FLOAT16 || type_ == DataType::DE_FLOAT32 || type_ == DataType::DE_FLOAT64; - } - - bool IsBool() const { return type_ == DataType::DE_BOOL; } - - bool IsNumeric() const { return IsInt() || IsFloat() || IsBool(); } - - bool IsString() const { return type_ == DataType::DE_STRING || type_ == DataType::DE_BYTES; } - - bool IsPython() const { return type_ == DataType::DE_PYTHON; } - - Type value() const { return type_; } - - private: - Type type_; -}; - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_BOOL); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_FLOAT64); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_FLOAT32); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_FLOAT16); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_INT64); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_UINT64); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_INT32); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_UINT32); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_INT16); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_UINT16); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_INT8); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_UINT8); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_STRING); -} - -template <> -inline DataType DataType::FromCType() { - return DataType(DataType::DE_STRING); -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_BOOL; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_FLOAT64 || type_ == DataType::DE_FLOAT32; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_FLOAT32; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_FLOAT16; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_INT64 || type_ == DataType::DE_INT32 || type_ == DataType::DE_INT16 || - type_ == DataType::DE_INT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_UINT64 || type_ == DataType::DE_UINT32 || type_ == DataType::DE_UINT16 || - type_ == DataType::DE_UINT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_INT32 || type_ == DataType::DE_INT16 || type_ == DataType::DE_INT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_UINT32 || type_ == DataType::DE_UINT16 || type_ == DataType::DE_UINT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_INT16 || type_ == DataType::DE_INT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_UINT16 || type_ == DataType::DE_UINT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_INT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_UINT8; -} - -template <> -inline bool DataType::IsLooselyCompatible() const { - return type_ == DataType::DE_STRING || type_ == DataType::DE_BYTES; -} -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DATA_TYPE_H_ diff --git a/mindspore-lite/minddata/dataset/core/de_tensor.cc b/mindspore-lite/minddata/dataset/core/de_tensor.cc deleted file mode 100644 index cd22f2a3e..000000000 --- a/mindspore-lite/minddata/dataset/core/de_tensor.cc +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/core/de_tensor.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#ifndef ENABLE_ANDROID -#define EXCEPTION_IF_NULL(ptr) MS_EXCEPTION_IF_NULL(ptr) -#else -#define EXCEPTION_IF_NULL(ptr) MS_ASSERT((ptr) != nullptr) -#endif - -namespace mindspore { -namespace dataset { - -DETensor::DETensor(std::shared_ptr tensor_impl) - : tensor_impl_(tensor_impl), - name_("MindDataTensor"), - type_(static_cast(DETypeToMSType(tensor_impl_->type()))), - shape_(tensor_impl_->shape().AsVector()), - is_device_(false) {} - -#ifndef ENABLE_ANDROID -DETensor::DETensor(std::shared_ptr device_tensor_impl, bool is_device) - : device_tensor_impl_(device_tensor_impl), name_("MindDataDeviceTensor"), is_device_(is_device) { - // The sequence of shape_ is (width, widthStride, height, heightStride) in Dvpp module - // We need to add [1]widthStride and [3]heightStride, which are actual YUV image shape, into shape_ attribute - if (device_tensor_impl && device_tensor_impl->GetYuvStrideShape().size() > 0) { - uint8_t flag = 0; - for (auto &i : device_tensor_impl->GetYuvStrideShape()) { - if (flag % 2 == 1) { - int64_t j = static_cast(i); - shape_.emplace_back(j); - } - ++flag; - } - std::reverse(shape_.begin(), shape_.end()); - } - MS_LOG(INFO) << "This is a YUV420 format image, one pixel takes 1.5 bytes. Therefore, the shape of" - << " image is in (H, W) format. You can search for more information about YUV420 format"; -} -#endif - -const std::string &DETensor::Name() const { return name_; } - -enum mindspore::DataType DETensor::DataType() const { -#ifndef ENABLE_ANDROID - if (is_device_) { - EXCEPTION_IF_NULL(device_tensor_impl_); - return static_cast(DETypeToMSType(device_tensor_impl_->DeviceDataType())); - } -#endif - EXCEPTION_IF_NULL(tensor_impl_); - return static_cast(DETypeToMSType(tensor_impl_->type())); -} - -size_t DETensor::DataSize() const { -#ifndef ENABLE_ANDROID - if (is_device_) { - EXCEPTION_IF_NULL(device_tensor_impl_); - return device_tensor_impl_->DeviceDataSize(); - } -#endif - EXCEPTION_IF_NULL(tensor_impl_); - return static_cast(tensor_impl_->SizeInBytes()); -} - -const std::vector &DETensor::Shape() const { return shape_; } - -int64_t DETensor::ElementNum() const { - if (shape_.empty()) { - // element number of scalar is 1 - return 1; - } - return std::accumulate(shape_.begin(), shape_.end(), 1, std::multiplies()); -} - -std::shared_ptr DETensor::Data() const { -#ifndef ENABLE_ANDROID - if (is_device_) { - EXCEPTION_IF_NULL(device_tensor_impl_); - return std::shared_ptr(device_tensor_impl_->GetHostBuffer(), [](const void *) {}); - } -#endif - return std::shared_ptr(tensor_impl_->GetBuffer(), [](const void *) {}); -} - -void *DETensor::MutableData() { -#ifndef ENABLE_ANDROID - if (is_device_) { - EXCEPTION_IF_NULL(device_tensor_impl_); - return static_cast(device_tensor_impl_->GetDeviceMutableBuffer()); - } -#endif - EXCEPTION_IF_NULL(tensor_impl_); - return static_cast(tensor_impl_->GetMutableBuffer()); -} - -bool DETensor::IsDevice() const { return is_device_; } - -std::shared_ptr DETensor::Clone() const { -#ifndef ENABLE_ANDROID - if (is_device_) { - EXCEPTION_IF_NULL(device_tensor_impl_); - return std::make_shared(device_tensor_impl_, is_device_); - } -#endif - return std::make_shared(tensor_impl_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/de_tensor.h b/mindspore-lite/minddata/dataset/core/de_tensor.h deleted file mode 100644 index ad15f548f..000000000 --- a/mindspore-lite/minddata/dataset/core/de_tensor.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DETENSOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DETENSOR_H_ -#include -#include -#include -#include -#include "include/api/status.h" -#include "include/api/visible.h" -#include "src/common/api_tensor_impl.h" - -namespace mindspore { -namespace dataset { -class Tensor; -class DeviceTensor; - -class DETensor : public mindspore::MSTensor::Impl { - public: - DETensor() = default; - ~DETensor() = default; - explicit DETensor(std::shared_ptr tensor_impl); -#ifndef ENABLE_ANDROID - DETensor(std::shared_ptr device_tensor_impl, bool is_device); -#endif - const std::string &Name() const override; - - enum mindspore::DataType DataType() const override; - - size_t DataSize() const override; - - const std::vector &Shape() const override; - void SetShape(const std::vector &shape) override { shape_ = shape; }; - - int64_t ElementNum() const; - - std::shared_ptr Data() const override; - - void *MutableData() override; - - bool IsDevice() const override; - - std::shared_ptr Clone() const override; - - private: - std::shared_ptr tensor_impl_; -#ifndef ENABLE_ANDROID - std::shared_ptr device_tensor_impl_; -#endif - bool is_device_; - std::string name_; - enum mindspore::DataType type_; - std::vector shape_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DETENSOR_H_ diff --git a/mindspore-lite/minddata/dataset/core/device_resource.cc b/mindspore-lite/minddata/dataset/core/device_resource.cc deleted file mode 100644 index e77f847a8..000000000 --- a/mindspore-lite/minddata/dataset/core/device_resource.cc +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/core/device_resource.h" - -namespace mindspore { -namespace dataset { - -Status DeviceResource::InitResource(uint32_t) { - return Status(StatusCode::kMDUnexpectedError, - "Is this a valid device? If yes, please implement this InitResource() in the derived class."); -} - -Status DeviceResource::FinalizeResource() { - return Status(StatusCode::kMDUnexpectedError, - "Is this a valid device? If yes, please implement this FinalizeResource() in the derived class."); -} - -Status DeviceResource::Sink(const mindspore::MSTensor &host_input, std::shared_ptr *device_input) { - return Status(StatusCode::kMDUnexpectedError, - "Is this a valid device whose device memory is available? If yes, please implement this Sink() in the " - "derived class."); -} - -Status DeviceResource::Pop(const std::shared_ptr &device_output, std::shared_ptr *host_output) { - return Status(StatusCode::kMDUnexpectedError, - "Is this a valid device whose device memory is available? If yes, please implement this Pop() in the " - "derived class."); -} - -Status DeviceResource::DeviceDataRelease() { - return Status( - StatusCode::kMDUnexpectedError, - "Is this a valid device whose device memory is available? If yes, please implement this DeviceDataRelease() in the " - "derived class."); -} - -std::shared_ptr DeviceResource::GetInstance() { - MS_LOG(ERROR) << "Is this a device which contains a processor object? If yes, please implement this GetInstance() in " - "the derived class"; - return nullptr; -} - -void *DeviceResource::GetContext() { - MS_LOG(ERROR) - << "Is this a device which contains context resource? If yes, please implement GetContext() in the derived class"; - return nullptr; -} - -void *DeviceResource::GetStream() { - MS_LOG(ERROR) - << "Is this a device which contains stream resource? If yes, please implement GetContext() in the derived class"; - return nullptr; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/device_resource.h b/mindspore-lite/minddata/dataset/core/device_resource.h deleted file mode 100644 index 013bdc508..000000000 --- a/mindspore-lite/minddata/dataset/core/device_resource.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DEVICE_RESOURCE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DEVICE_RESOURCE_H_ - -#include -#include "include/api/context.h" -#include "include/api/status.h" -#include "include/api/visible.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -namespace mindspore { -namespace dataset { - -class DeviceResource { - public: - DeviceResource() = default; - - virtual ~DeviceResource() = default; - - virtual Status InitResource(uint32_t device_id); - - virtual Status FinalizeResource(); - - virtual Status Sink(const mindspore::MSTensor &host_input, std::shared_ptr *device_input); - - virtual Status Pop(const std::shared_ptr &device_output, std::shared_ptr *host_output); - - virtual std::shared_ptr GetInstance(); - - virtual Status DeviceDataRelease(); - - virtual void *GetContext(); - - virtual void *GetStream(); -}; - -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_DEVICE_RESOURCE_H diff --git a/mindspore-lite/minddata/dataset/core/device_tensor.cc b/mindspore-lite/minddata/dataset/core/device_tensor.cc deleted file mode 100644 index 11bf0c663..000000000 --- a/mindspore-lite/minddata/dataset/core/device_tensor.cc +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const int kYuvDefaultChannels = 4; - -DeviceTensor::DeviceTensor(const TensorShape &shape, const DataType &type) - : Tensor(shape, type), device_data_(nullptr), size_(0) { - device_data_type_ = type; - host_data_tensor_ = nullptr; -} - -Status DeviceTensor::CreateEmpty(const TensorShape &shape, const DataType &type, std::shared_ptr *out) { - CHECK_FAIL_RETURN_UNEXPECTED(shape.known(), "Invalid shape."); - CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, "Invalid data type."); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Invalid nullptr pointer."); - *out = std::make_shared(shape, type); - // if it's a string tensor and it has no elements, Just initialize the shape and type. - if (!type.IsNumeric() && shape.NumOfElements() == 0) { - return Status::OK(); - } - - CHECK_FAIL_RETURN_UNEXPECTED(type.IsNumeric(), "Number of elements is not 0. The type should be numeric."); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Allocate memory faiiled."); - - int64_t bytes = (*out)->SizeInBytes(); - // Don't allocate if we have a tensor with no elements. - if (bytes != 0) { - RETURN_IF_NOT_OK((*out)->AllocateBuffer(bytes)); - } - return Status::OK(); -} - -Status DeviceTensor::CreateFromDeviceMemory(const TensorShape &shape, const DataType &type, uint8_t *data_ptr, - const uint32_t &dataSize, const std::vector &attributes, - std::shared_ptr *out) { - CHECK_FAIL_RETURN_UNEXPECTED(shape.known(), "Invalid shape."); - CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, "Invalid data type."); - CHECK_FAIL_RETURN_UNEXPECTED(data_ptr != nullptr, "Data pointer is NULL"); - CHECK_FAIL_RETURN_UNEXPECTED(dataSize > 0, "Invalid data size"); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Out pointer is NULL"); - - *out = std::make_shared(shape, type); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Allocate memory failed."); - - // if it's a string tensor and it has no elements, Just initialize the shape and type. - if (!type.IsNumeric() && shape.NumOfElements() == 0) { - return Status::OK(); - } - - CHECK_FAIL_RETURN_UNEXPECTED(type.IsNumeric(), "Number of elements is not 0. The type should be numeric."); - - int64_t byte_size = (*out)->SizeInBytes(); - - // Don't allocate if we have a tensor with no elements. - if (byte_size != 0) { - RETURN_IF_NOT_OK((*out)->AllocateBuffer(byte_size)); - } - - CHECK_FAIL_RETURN_UNEXPECTED(attributes.size() >= kYuvDefaultChannels, - "Invalid attributes size, should be greater than 4."); - CHECK_FAIL_RETURN_UNEXPECTED( - (*out)->SetAttributes(data_ptr, dataSize, attributes[0], attributes[1], attributes[2], attributes[3]), - "Fail to set attributes for DeviceTensor"); - - return Status::OK(); -} - -const unsigned char *DeviceTensor::GetHostBuffer() { - if (AclAdapter::GetInstance().HasAclPlugin()) { - Status rc = DataPop_(&host_data_tensor_); - if (!rc.IsOk()) { - MS_LOG(ERROR) << "Pop device data onto host fail, a nullptr will be returned"; - return nullptr; - } - } - - if (!host_data_tensor_) { - return nullptr; - } - return host_data_tensor_->GetBuffer(); -} - -const uint8_t *DeviceTensor::GetDeviceBuffer() { return device_data_; } - -uint8_t *DeviceTensor::GetDeviceMutableBuffer() { return device_data_; } - -DataType DeviceTensor::DeviceDataType() const { return device_data_type_; } - -uint32_t DeviceTensor::DeviceDataSize() { return size_; } - -Status DeviceTensor::SetYuvStrideShape_(const uint32_t &width, const uint32_t &widthStride, const uint32_t &height, - const uint32_t &heightStride) { - YUV_shape_ = {width, widthStride, height, heightStride}; - return Status::OK(); -} - -std::vector DeviceTensor::GetYuvStrideShape() { return YUV_shape_; } - -Status DeviceTensor::SetAttributes(uint8_t *data_ptr, const uint32_t &dataSize, const uint32_t &width, - const uint32_t &widthStride, const uint32_t &height, const uint32_t &heightStride) { - device_data_ = data_ptr; - CHECK_FAIL_RETURN_UNEXPECTED(device_data_ != nullptr, "Fail to get the device data."); - RETURN_IF_NOT_OK(SetSize_(dataSize)); - RETURN_IF_NOT_OK(SetYuvStrideShape_(width, widthStride, height, heightStride)); - return Status::OK(); -} - -Status DeviceTensor::SetSize_(const uint32_t &new_size) { - size_ = new_size; - return Status::OK(); -} - -Status DeviceTensor::DataPop_(std::shared_ptr *host_tensor) { - CHECK_FAIL_RETURN_UNEXPECTED(host_tensor != nullptr, "host tensor pointer is NULL."); - void *resHostBuf = nullptr; - APP_ERROR ret = AclAdapter::GetInstance().MallocHost(&resHostBuf, this->DeviceDataSize()); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return Status(StatusCode::kMDNoSpace); - } - - std::shared_ptr outBuf(resHostBuf, [](void *ptr) { AclAdapter::GetInstance().FreeHost(ptr); }); - auto processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = AclAdapter::GetInstance().Memcpy(outBuf.get(), this->DeviceDataSize(), this->GetDeviceBuffer(), - this->DeviceDataSize(), 2); // 2 means ACL_MEMCPY_DEVICE_TO_HOST - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return Status(StatusCode::kMDOutOfMemory); - } - - auto data = std::static_pointer_cast(processedInfo_); - unsigned char *ret_ptr = data.get(); - - mindspore::dataset::dsize_t dvppDataSize = this->DeviceDataSize(); - const mindspore::dataset::TensorShape dvpp_shape({dvppDataSize, 1, 1}); - - CHECK_FAIL_RETURN_UNEXPECTED(this->GetYuvStrideShape().size() >= kYuvDefaultChannels, - "Invalid YuvShape, should be greater than 4"); - - uint32_t _output_width_ = this->GetYuvStrideShape()[0]; - uint32_t _output_widthStride_ = this->GetYuvStrideShape()[1]; - uint32_t _output_height_ = this->GetYuvStrideShape()[2]; - uint32_t _output_heightStride_ = this->GetYuvStrideShape()[3]; - const mindspore::dataset::DataType dvpp_data_type(mindspore::dataset::DataType::DE_UINT8); - - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, host_tensor)); - CHECK_FAIL_RETURN_UNEXPECTED(host_tensor != nullptr, "Allocate memory failed."); - - (*host_tensor)->SetYuvShape(_output_width_, _output_widthStride_, _output_height_, _output_heightStride_); - if (!(*host_tensor)->HasData()) { - return Status(StatusCode::kMCDeviceError); - } - - MS_LOG(INFO) << "Successfully pop DeviceTensor data onto host"; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/device_tensor.h b/mindspore-lite/minddata/dataset/core/device_tensor.h deleted file mode 100644 index cbe9e01cf..000000000 --- a/mindspore-lite/minddata/dataset/core/device_tensor.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DEVICE_TENSOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DEVICE_TENSOR_H_ -#include -#include -#include -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class Tensor; -class DATASET_API DeviceTensor : public Tensor { - public: - DeviceTensor(const TensorShape &shape, const DataType &type); - - ~DeviceTensor() override = default; - - Status SetAttributes(uint8_t *data_ptr, const uint32_t &dataSize, const uint32_t &width, const uint32_t &widthStride, - const uint32_t &height, const uint32_t &heightStride); - - static Status CreateEmpty(const TensorShape &shape, const DataType &type, std::shared_ptr *out); - - static Status CreateFromDeviceMemory(const TensorShape &shape, const DataType &type, uint8_t *data_ptr, - const uint32_t &dataSize, const std::vector &attributes, - std::shared_ptr *out); - - const unsigned char *GetHostBuffer(); - - const uint8_t *GetDeviceBuffer(); - - uint8_t *GetDeviceMutableBuffer(); - - std::vector GetYuvStrideShape(); - - uint32_t DeviceDataSize(); - - DataType DeviceDataType() const; - - bool HasDeviceData() { return device_data_ != nullptr; } - - private: - Status SetSize_(const uint32_t &new_size); - - Status SetYuvStrideShape_(const uint32_t &width, const uint32_t &widthStride, const uint32_t &height, - const uint32_t &heightStride); - - Status DataPop_(std::shared_ptr *host_tensor); - - std::vector YUV_shape_; // YUV_shape_ = {width, widthStride, height, heightStride} - - uint8_t *device_data_; - - uint32_t size_; - - DataType device_data_type_; - - // We use this Tensor to store device_data when DeviceTensor pop onto host - std::shared_ptr host_data_tensor_; -}; - -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_DEVICE_TENSOR_H_ diff --git a/mindspore-lite/minddata/dataset/core/example.proto b/mindspore-lite/minddata/dataset/core/example.proto deleted file mode 100644 index eb893f4be..000000000 --- a/mindspore-lite/minddata/dataset/core/example.proto +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ - -// Protocol messages for describing input data Examples for machine learning -// model training or inference. -syntax = "proto3"; - -import "feature.proto"; -option cc_enable_arenas = true; -option java_outer_classname = "ExampleProtos"; -option java_multiple_files = true; -option java_package = "org.dataengine.example"; - -package dataengine; - -message Example { - Features features = 1; -}; - -message SequenceExample { - Features context = 1; - FeatureLists feature_lists = 2; -}; diff --git a/mindspore-lite/minddata/dataset/core/feature.proto b/mindspore-lite/minddata/dataset/core/feature.proto deleted file mode 100644 index 03c0f0938..000000000 --- a/mindspore-lite/minddata/dataset/core/feature.proto +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ - -syntax = "proto3"; -option cc_enable_arenas = true; -option java_multiple_files = true; -option java_package = "org.dataengine.example"; -option java_outer_classname = "FeatureProtos"; - -package dataengine; - -// define Int64 -message Int64List { - repeated int64 value = 1 [packed = true]; -} - -// define Float -message FloatList { - repeated float value = 1 [packed = true]; -} - -//define Bytes -message BytesList { - repeated bytes value = 1; -} - -// define Feature -message Feature { - oneof kind { - BytesList bytes_list = 1; - FloatList float_list = 2; - Int64List int64_list = 3; - } -}; - -//define Features as Feature map -message Features { - map feature = 1; -}; - -// define FeatureList -message FeatureList { - repeated Feature feature = 1; -}; - -// define FeatureLists as FeatureList map -message FeatureLists { - map feature_list = 1; -}; diff --git a/mindspore-lite/minddata/dataset/core/global_context.cc b/mindspore-lite/minddata/dataset/core/global_context.cc deleted file mode 100644 index 1e8abb6d7..000000000 --- a/mindspore-lite/minddata/dataset/core/global_context.cc +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/global_context.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#endif -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/system_pool.h" - -namespace mindspore { -namespace dataset { -// Global static pointer for the singleton GlobalContext -std::unique_ptr GlobalContext::global_context_ = nullptr; -std::once_flag GlobalContext::init_instance_flag_; - -constexpr int GlobalContext::kArenaSize; -constexpr int GlobalContext::kMaxSize; -constexpr bool GlobalContext::kInitArena; - -// Singleton initializer -GlobalContext *GlobalContext::Instance() { - // If the single global context is not created yet, then create it. Otherwise the - // existing one is returned. - std::call_once(init_instance_flag_, []() { - global_context_.reset(new GlobalContext()); - Status rc = global_context_->Init(); - if (rc.IsError()) { - std::terminate(); - } - }); - return global_context_.get(); -} - -Status GlobalContext::Init() { - config_manager_ = std::make_shared(); - mem_pool_ = std::make_shared(); - // For testing we can use Dummy pool instead - - // Create some tensor allocators for the different types and hook them into the pool. - tensor_allocator_ = std::make_unique>(mem_pool_); -#if !defined(ENABLE_ANDROID) - cv_tensor_allocator_ = std::make_unique>(mem_pool_); -#endif - device_tensor_allocator_ = std::make_unique>(mem_pool_); - int_allocator_ = std::make_unique(mem_pool_); - profiler_manager_ = std::make_shared(); - return Status::OK(); -} - -// A print method typically used for debugging -void GlobalContext::Print(std::ostream &out) const { - out << "GlobalContext contains the following default config: " << *config_manager_ << "\n"; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/global_context.h b/mindspore-lite/minddata/dataset/core/global_context.h deleted file mode 100644 index 0fb9f9313..000000000 --- a/mindspore-lite/minddata/dataset/core/global_context.h +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_GLOBAL_CONTEXT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_GLOBAL_CONTEXT_H_ - -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" - -namespace mindspore { -namespace dataset { -// forward declare -class MemoryPool; -class Tensor; -class CVTensor; -class DeviceTensor; - -using TensorAlloc = Allocator; // An allocator for Tensors -using CVTensorAlloc = Allocator; // An allocator CVTensors -using DeviceTensorAlloc = Allocator; // An allocator for Device_Tensors -using IntAlloc = Allocator; - -class GlobalContext { - // some consts for pool config - static constexpr int kArenaSize = 128; - static constexpr int kMaxSize = -1; - static constexpr bool kInitArena = true; - - public: - // Singleton pattern. This method either: - // - creates the single version of the GlobalContext for the first time and returns it - // OR - // - returns the already existing single instance of the GlobalContext - // @return the single global context - static GlobalContext *Instance(); - - // Destructor - ~GlobalContext() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - void Print(std::ostream &out) const; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param g_c - reference to the GlobalContext to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const GlobalContext &g_c) { - g_c.Print(out); - return out; - } - - // Getter method - // @return the client config as raw const pointer - static std::shared_ptr config_manager() { return Instance()->config_manager_; } - - /// Getter method - /// \return Shared pointer to the ProfilingManager Singleton - static std::shared_ptr profiling_manager() { return Instance()->profiler_manager_; } - // Getter method - // @return the mem pool - const std::shared_ptr &mem_pool() const { return mem_pool_; } - - // Getter method - // @return the tensor allocator as raw pointer - const TensorAlloc *tensor_allocator() const { return tensor_allocator_.get(); } - - // Getter method - // @return the CVTensor allocator as raw pointer - const CVTensorAlloc *cv_tensor_allocator() const { return cv_tensor_allocator_.get(); } - - // Getter method - // @return the DeviceTensor allocator as raw pointer - const DeviceTensorAlloc *device_tensor_allocator() const { return device_tensor_allocator_.get(); } - - // Getter method - // @return the integer allocator as raw pointer - const IntAlloc *int_allocator() const { return int_allocator_.get(); } - - private: - // Constructor. - // @note Singleton. Instantiation flows through instance() - // @return This is a constructor. - GlobalContext() = default; - - Status Init(); - - static std::once_flag init_instance_flag_; - static std::unique_ptr global_context_; // The instance of the singleton (global) - std::shared_ptr mem_pool_; // A global memory pool - std::shared_ptr config_manager_; // The configs - std::unique_ptr tensor_allocator_; // An allocator for Tensors - std::unique_ptr cv_tensor_allocator_; // An allocator for CV Tensors - std::unique_ptr device_tensor_allocator_; // An allocator for Device Tensors - std::unique_ptr int_allocator_; // An allocator for ints - std::shared_ptr profiler_manager_; // ProfilerManager instance for all trees -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_GLOBAL_CONTEXT_H_ diff --git a/mindspore-lite/minddata/dataset/core/pybind_support.h b/mindspore-lite/minddata/dataset/core/pybind_support.h deleted file mode 100644 index 8418d2397..000000000 --- a/mindspore-lite/minddata/dataset/core/pybind_support.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_PYBIND_SUPPORT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_PYBIND_SUPPORT_H_ - -#include - -#include "pybind11/numpy.h" -#include "pybind11/pybind11.h" -#include "base/float16.h" - -namespace py = pybind11; - -namespace pybind11 { -namespace detail { -// Similar to enums in `pybind11/numpy.h`. Determined by doing: -// python3 -c 'import numpy as np; print(np.dtype(np.float16).num)' -constexpr int kNpyFloat16 = 23; - -template -struct npy_scalar_caster { - PYBIND11_TYPE_CASTER(T, _("PleaseOverride")); - using Array = array_t; - - bool load(handle src, bool convert) { - // Taken from Eigen casters. Permits either scalar dtype or scalar array. - handle type = dtype::of().attr("type"); // Could make more efficient. - if (!convert && !isinstance(src) && !isinstance(src, type)) { - return false; - } - - Array tmp = Array::ensure(src); - if (tmp && tmp.size() == 1 && tmp.ndim() == 0) { - this->value = *tmp.data(); - return true; - } - - return false; - } - - static handle cast(T src, return_value_policy, handle) { - Array tmp({1}); - tmp.mutable_at(0) = src; - tmp.resize({}); - - // You could also just return the array if you want a scalar array. - object scalar = tmp[tuple()]; - return scalar.release(); - } -}; - -template <> -struct npy_format_descriptor { - static constexpr auto name = "float16"; - static pybind11::dtype dtype() { - handle ptr = npy_api::get().PyArray_DescrFromType_(kNpyFloat16); - return reinterpret_borrow(ptr); - } - virtual ~npy_format_descriptor() = default; - - static std::string format() { - // following: https://docs.python.org/3/library/struct.html#format-characters - return "e"; - } -}; - -template <> -struct type_caster : public npy_scalar_caster { - static constexpr auto name = "float16"; -}; -} // namespace detail -} // namespace pybind11 - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_PYBIND_SUPPORT_H_ diff --git a/mindspore-lite/minddata/dataset/core/tensor.cc b/mindspore-lite/minddata/dataset/core/tensor.cc deleted file mode 100644 index 919edd387..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor.cc +++ /dev/null @@ -1,1318 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#endif -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -#endif -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" -#include "utils/ms_utils.h" - -#ifdef ENABLE_PYTHON -namespace py = pybind11; -#endif - -namespace mindspore { -namespace dataset { -// Helper macros for printing tensor elements -#define CASE_PRINT(de_type, native_type) \ - case de_type: { \ - native_type o; \ - rc = GetItemAt(&o, index); \ - out << o; \ - break; \ - } - -#define CASE_PRINT_HEX(de_type, native_type) \ - case de_type: { \ - native_type o; \ - rc = GetItemAt(&o, index); \ - out << std::hex << std::setw(2) << std::setfill('0') << o << std::dec << std::setfill(' '); \ - break; \ - } - -Tensor::Tensor(TensorShape shape, DataType type) : shape_(std::move(shape)), type_(type), data_(nullptr) {} - -Tensor::Tensor(Tensor &&other) noexcept - : shape_(std::move(other.shape_)), type_(other.type_), data_(other.data_), data_end_(other.data_end_) { -#ifdef ENABLE_PYTHON - if (type_.value() == DataType::DE_PYTHON) { - py::gil_scoped_acquire gil_acquire; - python_dict_ = std::move(other.python_dict_); - } - // If other.python_array_ has value, assign it to this->python_array_ - if (static_cast(other.python_array_)) { - py::gil_scoped_acquire gil_acquire; - python_array_ = (other.python_array_); - } -#endif - other.Invalidate(); -} - -Tensor &Tensor::operator=(Tensor &&other) noexcept { - if (&other != this) { - shape_ = std::move(other.shape_); - type_ = other.type_; - data_ = other.data_; - data_end_ = other.data_end_; - yuv_shape_ = std::move(other.yuv_shape_); -#ifdef ENABLE_PYTHON - if (type_.value() == DataType::DE_PYTHON) { - py::gil_scoped_acquire gil_acquire; - python_dict_ = std::move(other.python_dict_); - } - // If other.python_array_ has value, assign it to this->python_array_ - if (static_cast(other.python_array_)) { - py::gil_scoped_acquire gil_acquire; - python_array_ = (other.python_array_); - } -#endif - other.Invalidate(); - } - return *this; -} - -Status Tensor::CreateEmpty(const TensorShape &shape, const DataType &type, TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - CHECK_FAIL_RETURN_UNEXPECTED(shape.known(), "Failed to create empty tensor, tensor shape is unknown."); - CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, "Failed to create empty tensor, data type is unknown."); - *out = std::make_shared(shape, type); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Failed to create empty tensor, allocate memory failed."); - // if it's a string tensor and it has no elements, Just initialize the shape and type. - if (!type.IsNumeric()) { - if (shape.NumOfElements() == 0) { - return Status::OK(); - } else { - RETURN_STATUS_UNEXPECTED( - "Failed to create empty tensor, number of elements should be 0 when data type is string."); - } - } - - int64_t byte_size = (*out)->SizeInBytes(); - - // Don't allocate if we have a tensor with no elements. - if (byte_size != 0) { - RETURN_IF_NOT_OK((*out)->AllocateBuffer(byte_size)); - } - return Status::OK(); -} - -Status Tensor::CreateFromMemory(const TensorShape &shape, const DataType &type, const uchar *src, TensorPtr *out) { - RETURN_IF_NOT_OK(CreateEmpty(shape, type, out)); - if (src != nullptr && out != nullptr) { - // Given the shape/type of this tensor, compute the data size and copy in the input bytes. - int64_t byte_size = (*out)->SizeInBytes(); - if (byte_size == 0) { - return Status::OK(); - } - std::string err_msg = - "Failed to copy data into tensor. If GeneratorDataset(source=Pyfunc, ...) or map(operations=Pyfunc, ...) is " - "used, please check whether the memory of the " - "Numpy object returned by Pyfunc has been unexpectedly freed. Adding copy.deepcopy(numpy_object) before " - "numpy_object returned by Pyfunc maybe solve the issue. For more details, please refer to the FAQ at " - "https://www.mindspore.cn/docs/en/master/faq/data_processing.html."; - if (byte_size < SECUREC_MEM_MAX_LEN) { - int ret_code = memcpy_s((*out)->data_, byte_size, src, byte_size); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == EOK, err_msg); - } else { - auto ret_code = std::memcpy((*out)->data_, src, byte_size); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == (*out)->data_, err_msg); - } - } - return Status::OK(); -} - -Status Tensor::CreateFromMemory(const TensorShape &shape, const DataType &type, const uchar *src, const dsize_t &length, - TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - *out = std::make_shared(shape, type); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Allocate memory failed."); - if (type.IsNumeric()) { - dsize_t calculated_length = (*out)->SizeInBytes(); - CHECK_FAIL_RETURN_UNEXPECTED(calculated_length == length, "Length of source data does not match the shape."); - } else { - // min_length is the length of a tensor with empty strings - // min_length = the number of bytes needed to store the offsets + 1 byte for each element - dsize_t min_length = (shape.NumOfElements() + 1) * kOffsetSize + shape.NumOfElements(); - CHECK_FAIL_RETURN_UNEXPECTED(min_length <= length, "Length of source data does not match the shape."); - } - - RETURN_IF_NOT_OK((*out)->AllocateBuffer(length)); - if (length == 0) { - return Status::OK(); - } - RETURN_UNEXPECTED_IF_NULL(src); // needs to be here as we may return early without using src content (empty tensors) - if (length < SECUREC_MEM_MAX_LEN) { - int ret_code = memcpy_s((*out)->data_, length, src, length); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == EOK, "Failed to copy data into tensor."); - } else { - auto ret_code = std::memcpy((*out)->data_, src, length); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == (*out)->data_, "Failed to copy data into tensor."); - } - - return Status::OK(); -} - -#ifdef ENABLE_PYTHON -Status Tensor::CreateFromNpString(py::array arr, std::shared_ptr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - std::vector shape; - for (size_t i = 0; i < arr.ndim(); i++) { - shape.push_back(static_cast(arr.shape()[i])); - } - arr.resize({arr.size()}); // flatten the py::array so we can iterate once - std::vector strings; - strings.reserve(arr.size()); - (void)std::for_each(arr.begin(), arr.end(), - [&strings](const auto &s) { strings.emplace_back(py::cast(s)); }); - arr.resize(shape); // resize arr back to the original shape - - if (arr.dtype().kind() == 'U') { // numpy dtype type is "U" - RETURN_IF_NOT_OK(CreateFromVector(strings, TensorShape{shape}, DataType(DataType::DE_STRING), out)); - } else { // numpy dtype type is "S" - RETURN_IF_NOT_OK(CreateFromVector(strings, TensorShape{shape}, DataType(DataType::DE_BYTES), out)); - } - - return Status::OK(); -} - -Status Tensor::CreateFromNpArray(py::array arr, std::shared_ptr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - DataType type = DataType::FromNpArray(arr); - CHECK_FAIL_RETURN_UNEXPECTED(type != DataType::DE_UNKNOWN, - "Failed to create tensor from numpy array, data type is unknown."); - - if (type.IsString()) { - return CreateFromNpString(arr, out); - } - - std::vector shape; - std::vector strides; - // check if strides are contiguous - bool is_strided = false; - dsize_t count = arr.size(); - for (dsize_t i = 0; i < arr.ndim(); i++) { - shape.push_back(static_cast(arr.shape()[i])); - strides.push_back(static_cast(arr.strides()[i])); - // in case of empty array num_items=0 - if (count != 0 && shape.size() > i && shape[i] != 0) { - count /= shape[i]; - if (strides[i] != arr.itemsize() * count) { - is_strided = true; - } - } - } - - if (is_strided) { - unsigned char *data = static_cast(arr.request().ptr); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape(shape), type, out)); - RETURN_IF_NOT_OK(CopyStridedArray((*out)->data_, data, shape, strides, (*out)->type_.SizeInBytes())); - } else { -#ifdef ENABLE_PYTHON - // here we create empty tensor and use this->python_array_ point to data which is np.ndarray - *out = std::make_shared(TensorShape(shape), type); - { - py::gil_scoped_acquire gil_acquire; - (*out)->python_array_ = arr; - } - unsigned char *data = static_cast((*out)->python_array_.request().ptr); - int64_t byte_size = (*out)->SizeInBytes(); - if (byte_size == 0) { - return Status::OK(); - } - (*out)->data_ = data; - (*out)->data_end_ = data + byte_size; -#else - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(TensorShape(shape), type, data, out)); -#endif - } - return Status::OK(); -} - -Status Tensor::CreateFromPythonObject(py::object obj, std::shared_ptr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - std::vector shape{}; - DataType type = DataType(DataType::DE_PYTHON); - *out = std::make_shared(TensorShape({0}), type); - { - py::gil_scoped_acquire gil_acquire; - (*out)->python_dict_ = obj; - - // serialize python object to bytes which used by dataset independent process mode - (*out)->python_dict_as_str_ = py::str(py::module::import("pickle").attr("dumps")((*out)->python_dict_)); - (*out)->data_ = reinterpret_cast((*out)->python_dict_as_str_.data()); - (*out)->data_end_ = (*out)->data_ + (*out)->python_dict_as_str_.length(); - } - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Failed to create a tensor for python object."); - return Status::OK(); -} - -#endif - -#ifndef ENABLE_ANDROID -Status Tensor::CreateFromByteList(const dataengine::BytesList &bytes_list, const TensorShape &shape, TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - *out = std::make_shared(TensorShape({static_cast(bytes_list.value_size())}), - DataType(DataType::DE_STRING)); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Allocate memory failed."); - // total bytes needed = offset array + strings - // offset array needs to store one offset var per element + 1 extra to get the length of the last string. - // strings will be null-terminated --> need 1 extra byte per element - dsize_t num_bytes = (kOffsetSize) * (*out)->shape_.NumOfElements() + kOffsetSize + bytes_list.ByteSizeLong(); - - (*out)->data_ = GetAllocator()->allocate(num_bytes); - - auto offset_arr = reinterpret_cast((*out)->data_); - uchar *buf = (*out)->GetStringsBuffer(); - - offset_t offset = buf - (*out)->data_; // the first string will start here - int32_t i = 0; - for (; i < bytes_list.value_size(); i++) { - const std::string &str = bytes_list.value(i); - // insert the start index of the string. - offset_arr[i] = offset; - // total bytes are reduced by kOffsetSize - num_bytes -= kOffsetSize; - // insert actual string - int ret_code = memcpy_s((*out)->data_ + offset, num_bytes, common::SafeCStr(str), str.length() + 1); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == EOK, "Cannot copy string into Tensor"); - // next string will be stored right after the current one. - offset = offset + str.length() + 1; - // total bytes are reduced by the length of the string - num_bytes -= str.length() + 1; - } - // store one more offset value so we can get the length of the last string - // length[last_element] = offset_arr[last_element + 1] - offset_arr[last_element] - offset_arr[i] = offset; - - (*out)->data_end_ = (*out)->data_ + offset_arr[i]; - - MS_ASSERT(num_bytes == 0); - RETURN_IF_NOT_OK((*out)->Reshape(shape)); - return Status::OK(); -} -#endif - -Status Tensor::CreateFromFile(const std::string &path, std::shared_ptr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - Path file(path); - if (file.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid file found: " + path + ", should be file, but got directory."); - } - std::ifstream fs; - fs.open(path, std::ios::binary | std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "Failed to open file: " + path); - int64_t num_bytes = fs.seekg(0, std::ios::end).tellg(); - if (num_bytes >= kDeMaxDim) { - fs.close(); - RETURN_STATUS_UNEXPECTED("Invalid file to allocate tensor memory, check path: " + path); - } - if (!fs.seekg(0, std::ios::beg).good()) { - fs.close(); - RETURN_STATUS_UNEXPECTED("Failed to find size of file, check path: " + path); - } - auto s = Tensor::CreateEmpty(TensorShape{num_bytes}, DataType(DataType::DE_UINT8), out); - if (s != Status::OK()) { - fs.close(); - return s; - } - int64_t written_bytes = fs.read(reinterpret_cast((*out)->GetMutableBuffer()), num_bytes).gcount(); - if (!(written_bytes == num_bytes && fs.good())) { - fs.close(); - RETURN_STATUS_UNEXPECTED("Error in writing to tensor, check path: " + path); - } - fs.close(); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status Tensor::CreateFromByteList(const dataengine::BytesList &bytes_list, const TensorShape &shape, - const DataType &type, dsize_t pad_size, TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, type, out)); - - RETURN_UNEXPECTED_IF_NULL(out); - unsigned char *current_tensor_addr = (*out)->GetMutableBuffer(); - int64_t tensor_bytes_remaining = bytes_list.value_size() * pad_size; - - for (int i = 0; i < bytes_list.value_size(); i++) { - // read string data into tensor - const std::string ¤t_element = bytes_list.value(i); - int return_code = - memcpy_s(current_tensor_addr, tensor_bytes_remaining, common::SafeCStr(current_element), current_element.size()); - - CHECK_FAIL_RETURN_UNEXPECTED(return_code == EOK, "memcpy_s failed when reading bytesList element into Tensor"); - - current_tensor_addr += current_element.size(); - tensor_bytes_remaining -= current_element.size(); - - // pad - int64_t chars_to_pad = pad_size - current_element.size(); - return_code = memset_s(current_tensor_addr, tensor_bytes_remaining, static_cast(' '), chars_to_pad); - CHECK_FAIL_RETURN_UNEXPECTED(return_code == EOK, "memcpy_s failed when padding Tensor"); - - current_tensor_addr += chars_to_pad; - tensor_bytes_remaining -= chars_to_pad; - } - - return Status::OK(); -} -#endif - -// Memcpy the given strided array's used part to consecutive memory -// Consider a 3-d array -// A[(i * shape[1] + j) * shape[2] + k] = B[i][j][k] = C[i * strides[0] + j * strides[1] + k * strides[2]] -// Here we convert array C to array A, by memcpy index by index (Note that not all elements in C is copied) -Status Tensor::CopyStridedArray(unsigned char *dst, unsigned char *src, std::vector shape, - std::vector strides, uint8_t type_size) { - RETURN_UNEXPECTED_IF_NULL(dst); - RETURN_UNEXPECTED_IF_NULL(src); - dsize_t size = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<>()); - for (dsize_t i = 0; i < size; ++i) { - dsize_t offset = 0; - dsize_t count = i; - for (size_t j = 0; j < shape.size(); ++j) { - // convert 1d array's index to 3d array's index (A -> B) - CHECK_FAIL_RETURN_UNEXPECTED(shape[shape.size() - 1 - j] != 0, "Invalid data, shape can't be zero."); - dsize_t idx = count % shape[shape.size() - 1 - j]; - count /= shape[shape.size() - 1 - j]; - // calculate the raw data offset based on strides (B -> C) - offset += idx * strides[shape.size() - 1 - j]; - // once count = 0, the following idxes are all zero, skip them - if (count == 0) { - break; - } - } - // strides already consider byte size of the data type, but dst doesn't. - // dst[i] = dst + i * type_size = src + offset - int ret_code = memcpy_s(dst + i * type_size, type_size, src + offset, type_size); - if (ret_code != EOK) { - RETURN_STATUS_UNEXPECTED("Failed to copy data into Tensor."); - } - } - return Status::OK(); -} - -// Name: Destructor -// Description: Destructor -Tensor::~Tensor() { -#ifdef ENABLE_PYTHON - if (!static_cast(python_array_)) { // the data is not np.ndarray from python layer -#endif - if (!type().IsPython()) { // The Tensor is a python_dict_ - if (data_ != nullptr) { - if (GetAllocator() != nullptr) { - GetAllocator()->deallocate(data_); - data_ = nullptr; - data_end_ = nullptr; - } else { - // If we didn't have an allocator, but data_ is not null then it must - // be a stand-alone tensor that used malloc directly. - free(data_); - data_ = nullptr; - data_end_ = nullptr; - } - } - } -#ifdef ENABLE_PYTHON - } else { - // release the data from python layer - py::gil_scoped_acquire gil_acquire; - python_array_ = py::object(); // let borrowed python ndarray ref - 1 - } -#endif -#ifdef ENABLE_PYTHON - try { - // The default destructor will not acquire the Python GIL when it destructs - // the class members, so we need to handle py::object manually. - if (Py_IsInitialized() > 0) { - if (static_cast(python_dict_)) { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - // We need to reduce the reference count of the py::object to which python_dict_ - // refers by 1, then break that reference relationship, otherwise the default - // destructor will destruct that py::object again while recycling class member - // python_dict_. A simple assignment to None satisfies all of the above. - python_dict_ = py::object(); - } - } - } catch (const py::error_already_set &e) { - // ignore exceptions as everything could be shutting down at this point - } -#endif -} // namespace dataset - -bool Tensor::operator==(const Tensor &rhs) const { -#ifdef ENABLE_PYTHON - if (type_.value() == DataType::DE_PYTHON) { // we are holding a python object - if (static_cast(python_dict_) && static_cast(rhs.python_dict_) && python_dict_ == rhs.python_dict_) { - return true; - } - return false; - } -#endif - // 1. different shape 2. different type 3. one data_ is nullptr and the other is not - if (shape_ != rhs.shape() || type_ != rhs.type_ || (data_ == nullptr && rhs.data_ != nullptr) || - (data_ != nullptr && rhs.data_ == nullptr)) { - return false; - } - if (data_ == nullptr && rhs.data_ == nullptr) { - return true; - } - // use mem compare to compare the two data, size are already verified - return memcmp(data_, rhs.data_, SizeInBytes()) == 0; -} - -// Name: PrintItemAt() -// Description: A function that print the value as specified by its index -void Tensor::PrintItemAt(const std::vector &index, std::ostream &out) const { - Status rc; - MS_ASSERT(data_); - - switch (type_.value()) { - CASE_PRINT_HEX(DataType::DE_BOOL, bool) - - CASE_PRINT_HEX(DataType::DE_INT8, int8_t) - - CASE_PRINT_HEX(DataType::DE_UINT8, uint8_t) - - CASE_PRINT(DataType::DE_INT16, int16_t) - - CASE_PRINT(DataType::DE_UINT16, uint16_t) - - CASE_PRINT(DataType::DE_INT32, int32_t) - - CASE_PRINT(DataType::DE_UINT32, uint32_t) - - CASE_PRINT(DataType::DE_INT64, int64_t) - - CASE_PRINT(DataType::DE_UINT64, uint64_t) - - CASE_PRINT(DataType::DE_FLOAT16, float16) - - CASE_PRINT(DataType::DE_FLOAT32, float) - - CASE_PRINT(DataType::DE_FLOAT64, double) - - case DataType::DE_STRING: { - std::string_view o{""}; - rc = GetItemAt(&o, index); - out << "\"" << o << "\""; - break; - } - default: { - out << "?"; - break; - } - } - if (rc.IsError()) { - out << rc.ToString(); - } -} - -// Name: PrintRecursive() -// Description: A function that prints Tensor recursively, first called by print -void Tensor::PrintRecursive(std::ostream &out, int32_t cur_dim, const std::vector &cur_index) const { - if (cur_index.size() == shape_.Rank()) { - PrintItemAt(cur_index, out); - } else { - out << "["; - for (dsize_t i = 0; i < shape_[cur_dim]; i++) { - std::vector new_index = cur_index; - new_index.push_back(i); - PrintRecursive(out, cur_dim + 1, new_index); - if (i < shape_[cur_dim] - 1) { - out << ","; - } - } - out << "]"; - } -} - -// Name: Print() -// Description: A function that prints info about the tensor -void Tensor::Print(std::ostream &out) const { - out << "Tensor (shape: "; - out << shape_; - out << ", Type: " << type_ << ")\n"; - if (data_) { - PrintRecursive(out, 0, std::vector{}); -#ifdef ENABLE_PYTHON - } else if (static_cast(python_dict_)) { - std::string s; - { - py::gil_scoped_acquire gil_acquire; - s = py::str(python_dict_); - } - out << s; -#endif - } else { - out << "[Data area is null]"; - } -} - -void Tensor::PrintData(std::ostream &out) const { - if (data_) { - PrintRecursive(out, 0, std::vector{}); - } -} - -Status Tensor::AllocateBuffer(const dsize_t &length) { - RETURN_UNEXPECTED_IF_NULL(GetAllocator()); - if (data_ == nullptr) { - data_ = GetAllocator()->allocate(length); - CHECK_FAIL_RETURN_UNEXPECTED(data_ != nullptr, "Failed to allocate memory for tensor."); - data_end_ = data_ + length; - } - return Status::OK(); -} - -Status Tensor::Reshape(const TensorShape &shape) { - if (shape.NumOfElements() == shape_.NumOfElements()) { - shape_ = shape; - return Status::OK(); - } else { - std::string err = "Cannot reshape, Number of elements do not match"; - RETURN_STATUS_UNEXPECTED(err); - } -} - -void Tensor::Invalidate() { - shape_ = TensorShape::CreateUnknownRankShape(); - type_ = DataType(DataType::DE_UNKNOWN); - data_ = nullptr; - data_end_ = nullptr; -#ifdef ENABLE_PYTHON - if (type_.value() == DataType::DE_PYTHON) { - py::gil_scoped_acquire gil_acquire; - python_dict_ = py::object(); - } - if (static_cast(python_array_)) { - py::gil_scoped_acquire gil_acquire; - python_array_ = py::object(); // let borrowed python ndarray ref - 1 - } -#endif -} - -template -Status Tensor::GetItemPtr(T **ptr, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(ptr); - if (type_.IsCompatible()) { - if (data_ == nullptr) { - std::string err = "Data is not allocated yet"; - RETURN_STATUS_UNEXPECTED(err); - } - dsize_t flat_idx; - RETURN_IF_NOT_OK(shape_.ToFlatIndex(index, &flat_idx)); - *ptr = reinterpret_cast(data_ + flat_idx * type_.SizeInBytes()); - RETURN_UNEXPECTED_IF_NULL(*ptr); - - return Status::OK(); - } else { - std::string err = "data type not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } -} - -Status Tensor::GetItemPtr(uchar **ptr, const std::vector &index, offset_t *length) const { - RETURN_UNEXPECTED_IF_NULL(ptr); - RETURN_UNEXPECTED_IF_NULL(length); - if (type_.IsString()) { - if (data_ == nullptr) { - std::string err = "Data is not allocated yet"; - RETURN_STATUS_UNEXPECTED(err); - } - dsize_t flat_idx; - RETURN_IF_NOT_OK(shape_.ToFlatIndex(index, &flat_idx)); - offset_t length_temp = 0; - RETURN_IF_NOT_OK(GetStringAt(flat_idx, ptr, &length_temp)); - *length = length_temp; - return Status::OK(); - } else { - std::string err = "data type not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } -} - -Status Tensor::StartAddrOfIndex(std::vector ind, uchar **start_addr_of_index, TensorShape *remaining) { - RETURN_UNEXPECTED_IF_NULL(start_addr_of_index); - RETURN_UNEXPECTED_IF_NULL(remaining); - if (type().IsString()) { - RETURN_STATUS_UNEXPECTED("StartAddrOfIndex does not support string and bytes tensors yet."); - } - - dsize_t flat_ind; - std::vector t_shape = shape().AsVector(); - std::vector r(t_shape.begin() + ind.size(), t_shape.end()); - *remaining = TensorShape(r); - ind.resize(this->Rank(), 0); // same as -> while (ind.size() < this->Rank()) ind.push_back(0); - - RETURN_IF_NOT_OK(shape_.ToFlatIndex(ind, &flat_ind)); - // check if GetBuffer() returns null, we should flag this as an error, this sanity check will only - // be true is the tensor failed to allocate memory. - if (GetMutableBuffer() == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid GetBuffer in Tensor, got nullptr"); - } - *start_addr_of_index = GetMutableBuffer() + flat_ind * this->type().SizeInBytes(); - return Status::OK(); -} - -Status Tensor::InsertTensor(const std::vector &ind, const std::shared_ptr &tensor, - const bool partial_insert) { - RETURN_UNEXPECTED_IF_NULL(tensor); - std::string err_msg; - if (partial_insert) { - err_msg += (ind.size() != 1) - ? "[Tensor] only supports 1D insertion of elements not along the full length of the axis\n" - : ""; - err_msg += - (ind.at(0) + tensor->shape().NumOfElements() > shape().NumOfElements()) ? "[Tensor] incorrect index\n" : ""; - } else { - err_msg += (ind.size() + tensor->Rank() != Rank()) ? "[Tensor] incorrect index\n" : ""; - } - err_msg += (type().IsString()) ? "[Tensor] Cannot insert into a tensor of type string or bytes\n" : ""; - err_msg += (!shape().known() || !tensor->shape().known()) ? "[Tensor] unknown shape\n" : ""; - - err_msg += tensor->type().SizeInBytes() != type().SizeInBytes() ? "[Tensor] incorrect datatype\n" : ""; - uchar *start_addr_of_ind = nullptr; - if (partial_insert) { - TensorShape remaining_shape = tensor->shape(); - err_msg += - (!StartAddrOfIndex(ind, &start_addr_of_ind, &remaining_shape).IsOk()) ? "[Tensor] incorrect index\n" : ""; - } else { - TensorShape remaining_shape = TensorShape::CreateUnknownRankShape(); - err_msg += - (!StartAddrOfIndex(ind, &start_addr_of_ind, &remaining_shape).IsOk()) ? "[Tensor] incorrect index\n" : ""; - err_msg += !(remaining_shape == tensor->shape()) ? "[Tensor] memory error\n" : ""; - } - - if (!err_msg.empty()) { - MS_LOG(DEBUG) << "Insert tensor message: " << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } else { - if (start_addr_of_ind != nullptr) { - int ret_code = - memcpy_s(start_addr_of_ind, tensor->SizeInBytes(), tensor->GetMutableBuffer(), tensor->SizeInBytes()); - if (ret_code == EOK) { - return Status::OK(); - } else { - err_msg += "[Tensor] error in memcpy_s when inserting tensor\n"; - MS_LOG(DEBUG) << "Tensor message: " << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - RETURN_STATUS_UNEXPECTED("Failed to create memory for Tensor."); - } - } -} - -Status Tensor::ExpandDim(const dsize_t &axis) { - if (axis > Rank()) { - std::string err = "Axis is out of bound"; - RETURN_STATUS_UNEXPECTED(err); - } - if (axis == Rank()) { - shape_ = shape_.AppendDim(1); - } else { - shape_ = shape_.InsertDim(axis, 1); - } - return Status::OK(); -} - -std::vector Tensor::Strides() const { - std::vector strides = shape_.Strides(); - uint8_t size = type_.SizeInBytes(); - (void)std::transform(strides.begin(), strides.end(), strides.begin(), [&size](const auto &c) { return c * size; }); - return strides; -} - -#ifdef ENABLE_PYTHON -Status Tensor::GetBufferInfo(Tensor *t, py::buffer_info *out) { - RETURN_UNEXPECTED_IF_NULL(t); - RETURN_UNEXPECTED_IF_NULL(out); - CHECK_FAIL_RETURN_UNEXPECTED(t->type().IsNumeric(), "Cannot use GetBufferInfo on tensor of strings or bytes."); - - std::string format_desc = t->type().GetPybindFormat(); - if (format_desc.empty()) { - RETURN_STATUS_UNEXPECTED("Cannot convert DE type to pybind format"); - } - *out = py::buffer_info(t->GetMutableBuffer(), /* Pointer to buffer */ - t->type().SizeInBytes(), /* Size of one scalar */ - format_desc, /* Python struct-style format descriptor */ - t->Rank(), /* Number of dimensions */ - t->shape().AsVector(), /* Buffer dimensions */ - t->Strides()); - return Status::OK(); -} -#endif - -Status Tensor::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["shape"] = shape_.AsVector(); - args["type"] = type_.ToString(); - if (type_ == DataType::DE_BOOL) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_INT8) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_INT16) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_INT32) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_INT64) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_UINT8) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_UINT16) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_UINT32) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_UINT64) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_FLOAT32) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_ == DataType::DE_FLOAT64) { - RETURN_IF_NOT_OK(to_json_convert(&args)); - } else if (type_.IsString()) { - std::vector data_out; - for (auto it = this->begin(); it != this->end(); ++it) { - data_out.emplace_back(*it); - } - args["data"] = data_out; - } else { - return Status(StatusCode::kMDUnexpectedError, "Type is not supported for tensor"); - } - *out_json = args; - return Status::OK(); -} - -template -Status Tensor::to_json_convert(nlohmann::json *args) { - std::vector data_out; - for (auto it = this->begin(); it != this->end(); it++) { - data_out.emplace_back(*it); - } - (*args)["data"] = data_out; - return Status::OK(); -} - -Status Tensor::from_json(nlohmann::json op_params, std::shared_ptr *tensor) { - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "shape", "Tensor")); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "type", "Tensor")); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "data", "Tensor")); - std::string type = op_params["type"]; - std::vector list = op_params["shape"]; - TensorShape shape = TensorShape(list); - if (type == "bool") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "int8") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "int16") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "int32") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "int64") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "uint8") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "uint16") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "uint32") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "uint64") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "float32") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "float64") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, tensor)); - } else if (type == "string") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, DataType(DataType::DE_STRING), tensor)); - } else if (type == "bytes") { - RETURN_IF_NOT_OK(from_json_convert(op_params["data"], shape, DataType(DataType::DE_BYTES), tensor)); - } else { - return Status(StatusCode::kMDUnexpectedError, "Type is not supported for tensor"); - } - return Status::OK(); -} - -template -Status Tensor::from_json_convert(const nlohmann::json &json_data, const TensorShape &shape, - std::shared_ptr *tensor) { - std::vector data = json_data; - RETURN_IF_NOT_OK(CreateFromVector(data, shape, tensor)); - return Status::OK(); -} - -Status Tensor::from_json_convert(const nlohmann::json &json_data, const TensorShape &shape, const DataType &type, - std::shared_ptr *tensor) { - std::vector data = json_data; - RETURN_IF_NOT_OK(CreateFromVector(data, shape, type, tensor)); - return Status::OK(); -} - -template -Status Tensor::GetItemAt(T *o, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(o); - if (data_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Data is not allocated yet"); - } - if (!type_.IsLooselyCompatible()) { - std::string err = "Template type and Tensor type are not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } - if (type_.IsUnsignedInt()) { - RETURN_IF_NOT_OK(GetUnsignedIntAt(o, index)); - } else if (type_.IsSignedInt()) { - RETURN_IF_NOT_OK(GetSignedIntAt(o, index)); - } else if (type_.IsFloat()) { - RETURN_IF_NOT_OK(GetFloatAt(o, index)); - } else if (type_.IsBool()) { - bool *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - } else { - std::string err = "Tensor Type is unknown"; - RETURN_STATUS_UNEXPECTED(err); - } - return Status::OK(); -} - -Status Tensor::GetItemAt(std::string_view *o, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(data_); - RETURN_UNEXPECTED_IF_NULL(o); - CHECK_FAIL_RETURN_UNEXPECTED(type_.IsString(), "Tensor type is not of string or bytes."); - - uchar *start = nullptr; - offset_t length = 0; - RETURN_IF_NOT_OK(GetItemPtr(&start, index, &length)); - std::string_view sv{reinterpret_cast(start), length}; - o->swap(sv); - return Status::OK(); -} - -#ifdef ENABLE_PYTHON -// return data as numpy, should return status -Status Tensor::GetDataAsNumpy(py::array *data) { - RETURN_UNEXPECTED_IF_NULL(data); - if (type_ == DataType::DE_BOOL) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_INT8) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_INT16) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_INT32) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_INT64) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_UINT8) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_UINT16) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_UINT32) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_UINT64) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_FLOAT16) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_FLOAT32) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_ == DataType::DE_FLOAT64) { - *data = py::array_t(shape_.AsVector(), reinterpret_cast(data_)); - } else if (type_.IsString()) { - RETURN_IF_NOT_OK(GetDataAsNumpyStrings(data)); - } else { - RETURN_STATUS_UNEXPECTED("Got unexpected type when returning numpy"); - } - return Status::OK(); -} - -Status Tensor::GetDataAsNumpyStrings(py::array *data) { - RETURN_UNEXPECTED_IF_NULL(data); - if (type_ == DataType::DE_STRING) { - RETURN_IF_NOT_OK(GetDataAsNumpyStrings(data)); - } else if (type_ == DataType::DE_BYTES) { - RETURN_IF_NOT_OK(GetDataAsNumpyStrings(data)); - } else { - RETURN_STATUS_UNEXPECTED("Can not convert a numeric Tensor to a string NumPy array."); - } - return Status::OK(); -} - -Status Tensor::GetDataAsPythonObject(py::dict *data) { - RETURN_UNEXPECTED_IF_NULL(data); - { - py::gil_scoped_acquire gil_acquire; - *data = python_dict_; - } - return Status::OK(); -} -#endif - -void Tensor::Squeeze() { shape_ = shape_.Squeeze(); } - -template -Status Tensor::GetUnsignedIntAt(T *o, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(o); - if (data_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Data is not allocated yet"); - } - if (!type_.IsLooselyCompatible()) { - std::string err = "Template type and Tensor type are not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } - switch (type_.value()) { - case DataType::DE_UINT8: { - uint8_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_UINT16: { - uint16_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_UINT32: { - uint32_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_UINT64: { - uint64_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - default: - std::string err = "Tensor Type is not an unsigned Integer"; - RETURN_STATUS_UNEXPECTED(err); - } - return Status::OK(); -} - -template -Status Tensor::GetSignedIntAt(T *o, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(o); - if (data_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Data is not allocated yet"); - } - if (!type_.IsLooselyCompatible()) { - std::string err = "Template type and Tensor type are not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } - switch (type_.value()) { - case DataType::DE_INT8: { - int8_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_INT16: { - int16_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_INT32: { - int32_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_INT64: { - int64_t *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - default: - std::string err = "Tensor Type is not a signed Integer"; - RETURN_STATUS_UNEXPECTED(err); - } - return Status::OK(); -} - -template -Status Tensor::GetFloatAt(T *o, const std::vector &index) const { - RETURN_UNEXPECTED_IF_NULL(o); - if (data_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Data is not allocated yet"); - } - if (!type_.IsLooselyCompatible()) { - std::string err = "Template type and Tensor type are not compatible"; - RETURN_STATUS_UNEXPECTED(err); - } - switch (type_.value()) { - case DataType::DE_FLOAT16: { - float16 *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_FLOAT32: { - float *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - case DataType::DE_FLOAT64: { - double *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *o = static_cast(*ptr); - break; - } - default: - std::string err = "Tensor Type is not a float/double"; - RETURN_STATUS_UNEXPECTED(err); - } - return Status::OK(); -} - -Status Tensor::GetStringAt(dsize_t index, uchar **string_start, offset_t *length) const { - CHECK_FAIL_RETURN_UNEXPECTED(type_.IsString(), "Type is not string or bytes."); - RETURN_UNEXPECTED_IF_NULL(data_); - RETURN_UNEXPECTED_IF_NULL(string_start); - RETURN_UNEXPECTED_IF_NULL(length); - auto *offset_ptr = reinterpret_cast(data_); // offsets starts here - offset_t start = offset_ptr[index]; - *string_start = data_ + start; - *length = offset_ptr[index + 1] - start - 1; // -1 to skip the \0 from the string length - return Status::OK(); -} - -Status Tensor::CopyLastDimAt(const std::shared_ptr &src, const std::vector &index) { - RETURN_UNEXPECTED_IF_NULL(src); - CHECK_FAIL_RETURN_UNEXPECTED(src->type() == type_, "Source Tensor has a different type"); - CHECK_FAIL_RETURN_UNEXPECTED(index.back() == 0, "Last dim in index should be 0"); - - uint8_t type_size = type_.SizeInBytes(); - size_t len = std::min(src->shape()[-1], shape_[-1]) * type_size; - dsize_t src_flat_ind = 0, dst_flat_ind = 0; - RETURN_IF_NOT_OK(src->shape().ToFlatIndex(index, &src_flat_ind)); - RETURN_IF_NOT_OK(shape_.ToFlatIndex(index, &dst_flat_ind)); - - const unsigned char *src_addr = src->GetBuffer() + src_flat_ind * type_size; - unsigned char *dst_addr = GetMutableBuffer() + dst_flat_ind * type_size; - CHECK_FAIL_RETURN_UNEXPECTED(memcpy_s(dst_addr, len, src_addr, len) == EOK, "memcpy error"); - return Status::OK(); -} - -Status Tensor::GetSliceOption(const SliceOption &slice_option, const int32_t &slice_index, - SliceOption *slice_option_ptr) { - RETURN_UNEXPECTED_IF_NULL(slice_option_ptr); - if (slice_option.indices_.empty() && !slice_option.slice_.valid()) { - RETURN_STATUS_UNEXPECTED("Both indices and slices can not be empty."); - } - - if (!slice_option.indices_.empty() && slice_option.slice_.valid()) { - RETURN_STATUS_UNEXPECTED("Both indices and slices can not be given."); - } - - CHECK_FAIL_RETURN_UNEXPECTED(shape_.Size() > slice_index, "Invalid shape, should be greater than slices index."); - // if slice object was provided, indices should be empty. Generate indices from the slice object. - if (slice_option.indices_.empty()) { - // check if slice is valid - mindspore::dataset::Slice slice_copy = slice_option.slice_; - slice_copy.start_ = HandleNeg(slice_option.slice_.start_, shape_[slice_index]); - slice_copy.stop_ = HandleNeg(slice_option.slice_.stop_, shape_[slice_index]); - slice_copy.start_ = slice_copy.start_ < 0 ? 0 : slice_copy.start_; - slice_copy.stop_ = slice_copy.stop_ < 0 ? 0 : slice_copy.stop_; - dsize_t max_idx = shape_[slice_index]; - slice_copy.start_ = slice_copy.start_ > max_idx ? max_idx : slice_copy.start_; - slice_copy.stop_ = slice_copy.stop_ > max_idx ? max_idx : slice_copy.stop_; - *slice_option_ptr = SliceOption(slice_copy); - } else { - // indices validation - std::vector indices_copy; - for (int j = 0; j < slice_option.indices_.size(); j++) { - dsize_t index = HandleNeg(slice_option.indices_[j], shape_[slice_index]); - CHECK_FAIL_RETURN_UNEXPECTED(index < shape_[slice_index] && index >= 0, - "Index " + std::to_string(index) + " is out of bounds."); - indices_copy.emplace_back(index); - } - *slice_option_ptr = SliceOption(indices_copy); - } - return Status::OK(); -} - -Status Tensor::Slice(std::shared_ptr *out, const std::vector &slice_options) { - RETURN_UNEXPECTED_IF_NULL(out); - std::vector converted_slice_objects; - - CHECK_FAIL_RETURN_UNEXPECTED(slice_options.size() <= static_cast(std::numeric_limits::max()), - "The size of slice_options_ must not be more than \"INT64_MAX\"."); - for (size_t k = 0; k < slice_options.size(); k++) { - SliceOption slice_option = slice_options[k]; - - if (slice_option.all_) { - auto slice = mindspore::dataset::Slice(shape_[static_cast(k)]); - converted_slice_objects.emplace_back(slice); - continue; - } - - CHECK_FAIL_RETURN_UNEXPECTED(k <= static_cast(std::numeric_limits::max()), - "GetSliceOption() can't function properly if there are " - "more than \"INT32_MAX\" slice options"); - SliceOption slice_option_item(false); - RETURN_IF_NOT_OK(GetSliceOption(slice_option, static_cast(k), &slice_option_item)); - converted_slice_objects.emplace_back(slice_option_item); - } - - // partial slices, pass in the rest - if (slice_options.size() != Rank()) { - for (auto j = static_cast(slice_options.size()); j < Rank(); j++) { - mindspore::dataset::Slice slice = mindspore::dataset::Slice(0, shape_[j]); - converted_slice_objects.emplace_back(SliceOption(slice)); - } - } - - // determine final shape: - TensorShape t = TensorShape({}); - dsize_t slice_len = slice_options.size(); - dsize_t slice_len_ind; - for (int i = 0; i < shape_.Rank(); i++) { - if (i < slice_len) { - // if it's a slice - if (converted_slice_objects[i].indices_.empty() && converted_slice_objects[i].slice_.step_ != 0) { - slice_len_ind = (converted_slice_objects[i].slice_.stop_ - converted_slice_objects[i].slice_.start_) / - converted_slice_objects[i].slice_.step_; - if ((converted_slice_objects[i].slice_.stop_ - converted_slice_objects[i].slice_.start_) % - converted_slice_objects[i].slice_.step_ != - 0) { - slice_len_ind++; - } - // account for slices that would return no data - slice_len_ind = slice_len_ind < 0 ? 0 : slice_len_ind; - t = t.AppendDim(slice_len_ind); - } else { - // if its a vector of indices - // need to introduce a way of handling indices and slices - if (!converted_slice_objects[i].indices_.empty()) { - t = t.AppendDim(converted_slice_objects[i].indices_.size()); - } - } - } else { - // add in the rest of the dimensions - slice_len_ind = shape_[i]; - t = t.AppendDim(slice_len_ind); - } - } - - std::vector> indices_vector = IndexGenerator(converted_slice_objects); - - if (indices_vector.empty()) { - return CreateEmpty(t, type_, out); - } - if (type_.IsNumeric()) { - return SliceNumeric(out, indices_vector, t); - } else { - return SliceString(out, indices_vector, t); - } -} - -Status Tensor::SliceNumeric(std::shared_ptr *out, const std::vector> &indices, - const TensorShape &shape) { - RETURN_UNEXPECTED_IF_NULL(out); - RETURN_IF_NOT_OK(CreateEmpty(shape, type_, out)); - - RETURN_UNEXPECTED_IF_NULL(out); - (*out)->GetMutableBuffer(); - dsize_t out_index = 0; - std::vector dim_length = shape_.AsVector(); - dsize_t type_size = type_.SizeInBytes(); - std::vector src_start = HandleNegIndices(indices[0], dim_length); - dsize_t src_start_index; - RETURN_IF_NOT_OK(shape_.ToFlatIndex(src_start, &src_start_index)); - - uchar *dst_addr = (*out)->data_; - dsize_t count = 1; - - // to handle partial slices - dsize_t current_stride = shape_.Strides()[indices[0].size() - 1]; - auto indices_size = static_cast(indices.size()); - for (dsize_t i = 0; i < indices_size; i++) { - std::vector cur_index = HandleNegIndices(indices[i], dim_length); - if (i < indices_size - 1) { - std::vector next_index = HandleNegIndices(indices[i + 1], dim_length); - dsize_t flat_idx_curr; - dsize_t flat_idx_next; - - RETURN_IF_NOT_OK(shape_.ToFlatIndex(cur_index, &flat_idx_curr)); - RETURN_IF_NOT_OK(shape_.ToFlatIndex(next_index, &flat_idx_next)); - - if (flat_idx_next == flat_idx_curr + current_stride) { - count++; - continue; - } - } - - int return_code = memcpy_s(dst_addr + out_index * type_size, (*out)->SizeInBytes(), - data_ + src_start_index * type_size, count * type_size * current_stride); - CHECK_FAIL_RETURN_UNEXPECTED(return_code == EOK, "memcpy_s failed in SliceNumeric"); - out_index += count * current_stride; - if (i < indices_size - 1) { - src_start = HandleNegIndices(indices[i + 1], dim_length); // next index - RETURN_IF_NOT_OK(shape_.ToFlatIndex(src_start, &src_start_index)); - } - count = 1; - } - return Status::OK(); -} - -Status Tensor::SliceString(std::shared_ptr *out, const std::vector> &indices, - const TensorShape &shape) { - RETURN_UNEXPECTED_IF_NULL(out); - std::vector dim_length = shape_.AsVector(); - std::vector strings; - - for (const std::vector &index : indices) { - std::vector cur_index = HandleNegIndices(index, dim_length); - dsize_t cur_flat_index; - RETURN_IF_NOT_OK(shape_.ToFlatIndex(cur_index, &cur_flat_index)); - std::string_view sv; - RETURN_IF_NOT_OK(GetItemAt(&sv, {cur_index})); - strings.emplace_back(sv); - } - return CreateFromVector(strings, shape, type_, out); -} - -Status Tensor::CreateFromMSTensor(const MSTensor &in, TensorPtr *out) { - if (in.Data() == nullptr) { - *out = nullptr; - return Status::OK(); - } - return Tensor::CreateFromMemory(TensorShape(in.Shape()), MSTypeToDEType(static_cast(in.DataType())), - (const uchar *)(in.Data().get()), in.DataSize(), out); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/tensor.h b/mindspore-lite/minddata/dataset/core/tensor.h deleted file mode 100644 index b4059de7e..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor.h +++ /dev/null @@ -1,913 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_H_ - -#include -#include -#include -#include -#include -#if defined(_WIN32) || defined(_WIN64) -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#endif - -#include "include/securec.h" -#ifndef ENABLE_ANDROID -#include "proto/example.pb.h" -#endif -#ifdef ENABLE_PYTHON -#include "pybind11/numpy.h" -#include "pybind11/pybind11.h" -#include "pybind11/stl.h" -#endif - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/de_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_helpers.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "utils/ms_utils.h" - -#ifdef ENABLE_PYTHON -namespace py = pybind11; -#endif - -namespace mindspore::dataset { -class Tensor; -template -class Allocator; - -using offset_t = uint32_t; // type of offset values to store strings locations -using TensorPtr = std::shared_ptr; - -/// const of the size of the offset variable -constexpr uint8_t kOffsetSize = sizeof(offset_t); - -class DATASET_API Tensor { - public: - Tensor() = delete; - Tensor(const Tensor &other) = delete; - Tensor &operator=(const Tensor &other) = delete; - - /// Create a tensor using shape and type. This constructor should not be used directly, use CreateFromTensor instead. - /// \note The shape and type information should be known and valid - /// \note The constructor does not allocate data - /// \param shape TensorShape - /// \param type DataType - Tensor(TensorShape shape, DataType type); - - /// Move constructor - /// \param other Tensor to be moved - Tensor(Tensor &&other) noexcept; - - /// Move assignment operator - /// \param other Tensor to be moved - Tensor &operator=(Tensor &&other) noexcept; - - /// Create a numeric tensor with type and shape. Items of the tensor would be uninitialized. - /// \param[in] shape shape of the output tensor - /// \param[in] type type of the output tensor - /// \param[out] out Generated tensor - /// \return Status code - static Status CreateEmpty(const TensorShape &shape, const DataType &type, TensorPtr *out); - - /// Create a numeric tensor from a pointer in memory. Length of the source data is determined from the shape and type. - /// Data will be copied into the new created tensor. - /// \param[in] shape shape of the output tensor - /// \param[in] type type of the output tensor - /// \param[in] src pointer to the source data - /// \param[out] out Generated tensor - /// \return Status code - static Status CreateFromMemory(const TensorShape &shape, const DataType &type, const uchar *src, TensorPtr *out); - - /// Create a tensor from a pointer in memory and length. Data will be copied into the new created tensor. - /// \param[in] shape shape of the output tensor - /// \param[in] type type of the output tensor - /// \param[in] src pointer to the source data - /// \param[in] length length of the src data - /// \param[out] out Generated tensor - /// \return Status code - static Status CreateFromMemory(const TensorShape &shape, const DataType &type, const uchar *src, - const dsize_t &length, TensorPtr *out); - - /// Create a copy of the input tensor - /// \param[in] in original tensor to be copied - /// \param[out] out output tensor to be generated - /// \return Status - static Status CreateFromTensor(const TensorPtr &in, TensorPtr *out) { - return CreateFromMemory(in->shape(), in->type(), in->GetBuffer(), in->SizeInBytes(), out); - } - - /// Create a copy of the input tensor - /// \param[in] in MSTensor to create DETensor from. - /// \param[in] out DETensor created. - /// \return Status - static Status CreateFromMSTensor(const MSTensor &in, TensorPtr *out); - -#ifdef ENABLE_PYTHON - /// Create a Tensor from a given py::array and reuse the memory of numpy - /// \param[in] arr py::array - /// \param[out] out Created tensor - /// \return Status Code - static Status CreateFromNpArray(py::array arr, TensorPtr *out); - - /// Helper function to create a tensor from a Python dictionary object - /// \param[in] obj pybind11 wrapper for Python dictionary object - /// \param[out] out Created Tensor - /// \return Status - static Status CreateFromPythonObject(py::object obj, TensorPtr *out); -#endif - -#ifndef ENABLE_ANDROID - /// Create a tensor of type DE_STRING from a BytesList. - /// \param[in] bytes_list protobuf's Bytelist - /// \param[in] shape shape of the output tensor - /// \param[out] out created Tensor - /// \return Status Code - static Status CreateFromByteList(const dataengine::BytesList &bytes_list, const TensorShape &shape, TensorPtr *out); - - /// Create a tensor of type UINT8 or INT8 from a BytesList. - /// The tensor will be padded with ' ' to reach the required pad_size. - /// \param[in] bytes_list protobuf's Bytelist - /// \param[in] shape shape of the output tensor - /// \param[in] type type of created tensor. Should be DE_UINT8 or INT8 - /// \param[in] pad_size The size of the tensor after padding - /// \param[out] out created Tensor - /// \return Status Code - static Status CreateFromByteList(const dataengine::BytesList &bytes_list, const TensorShape &shape, - const DataType &type, dsize_t pad_size, TensorPtr *out); -#endif - - /// Create a Tensor from a given list of values. - /// \param[in] items elements of the tensor - /// \param[in] shape shape of the output tensor - /// \param[out] out output argument to hold the created Tensor - /// \return Status Code - template - static Status CreateFromVector(const std::vector &items, const TensorShape &shape, TensorPtr *out) { - CHECK_FAIL_RETURN_UNEXPECTED( - static_cast(items.size()) == shape.NumOfElements(), - "Number of elements in the vector does not match the number of elements of the shape required"); - const DataType type = DataType::FromCType(); - // if items is empty, items_ptr would be nullptr. CreateFromMemory will handle this case. - const auto items_ptr = reinterpret_cast(&items[0]); - return CreateFromMemory(shape, type, items_ptr, out); - } - - /// Create a 1D Tensor from a given list of values. - /// \param[in] items elements of the tensor - /// \param[out] out output argument to hold the created Tensor - /// \return Status Code - template - static Status CreateFromVector(const std::vector &items, TensorPtr *out) { - return CreateFromVector(items, TensorShape({static_cast(items.size())}), out); - } - - /// Create a 1D boolean Tensor from a given list of boolean values. - /// \param[in] items elements of the tensor - /// \param[in] shape shape of the output tensor - /// \param[out] out output argument to hold the created Tensor - /// \return Status Code - static Status CreateFromVector(const std::vector &items, const TensorShape &shape, TensorPtr *out) { - const std::vector temp(items.begin(), items.end()); - RETURN_IF_NOT_OK(CreateFromVector(temp, shape, out)); - (*out)->type_ = DataType(DataType::DE_BOOL); - return Status::OK(); - } - - /// Create a Tensor from a given list of strings. - /// @note: The memory layout of a Tensor of strings consists of the Offset_array followed by the strings. - /// The offset array will store one extra value to find the length of the last string. - /// OFFSET_1, OFFSET_2, ..., OFFSET_n+1, STRING_1, STRING_2, ..., STRING_n - /// The value of each offset is the start index of the corresponding string - /// Offsets is of type offset_t - /// strings will ne null-terminated - /// example: Tensor(['abc', 'de'], shape={2}, type=DE_STRING) - /// |------------------------------------------------------------------------| - /// | OFFSET ARRAY | STRINGS | - /// | bytes 0-3 | bytes 4-7 | bytes 8-11 | bytes 12-15 | bytes 16-18 | - /// | 12 | 16 | 19 | abc\0 | de\0 | - /// |------------------------------------------------------------------------| - /// | first offset | second offset | end offset | first value | second value | - /// |------------------------------------------------------------------------| - /// \param[in] items elements of the tensor - /// \param[in] shape shape of the output tensor - /// \param[in] type data type of the output tensor, can only be DE_STRING or DE_BYTES - /// \param[out] out output argument to hold the created Tensor - /// \return Status Code - static Status CreateFromVector(const std::vector &items, const TensorShape &shape, const DataType &type, - TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - CHECK_FAIL_RETURN_UNEXPECTED(static_cast(items.size()) == shape.NumOfElements(), - "The number of elements in the vector: " + std::to_string(items.size()) + - " does not match the number of elements: " + std::to_string(shape.NumOfElements()) + - " the shape required."); - CHECK_FAIL_RETURN_UNEXPECTED(type.IsString(), "Can not create a numeric Tensor from a string vector."); - *out = std::make_shared(TensorShape({static_cast(items.size())}), type); - CHECK_FAIL_RETURN_UNEXPECTED(out != nullptr, "Allocate memory failed."); - if (items.empty()) { - if (shape.known()) { - return (*out)->Reshape(shape); - } - } - auto length_sum = [](size_t sum, const std::string &s) { return s.length() + sum; }; - const dsize_t total_length = std::accumulate(items.begin(), items.end(), 0, length_sum); - - // total bytes needed = offset array + strings - // offset array needs to store one offset var per element + 1 extra to get the length of the last string. - // strings will be null-terminated --> need 1 extra byte per element - const size_t num_bytes = (kOffsetSize + 1) * (*out)->shape_.NumOfElements() + kOffsetSize + total_length; - - RETURN_IF_NOT_OK((*out)->AllocateBuffer(num_bytes)); - auto offset_arr = reinterpret_cast((*out)->data_); - const uchar *buf = (*out)->GetStringsBuffer(); - - offset_t offset = buf - (*out)->data_; // the first string will start here - uint32_t i = 0; - for (const auto &str : items) { - // insert the start index of the string. - offset_arr[i++] = offset; - // insert actual string - const int ret_code = - memcpy_s((*out)->data_ + offset, num_bytes - offset, common::SafeCStr(str), str.length() + 1); - if (ret_code != 0) { - MS_LOG(ERROR) << "Cannot copy string into Tensor"; - } - // next string will be stored right after the current one. - offset = offset + str.length() + 1; - } - // store one more offset value so we can get the length of the last string - offset_arr[i] = offset; - - (*out)->data_end_ = (*out)->data_ + offset_arr[i]; - - MS_ASSERT(num_bytes - offset == 0); - if (shape.known()) { - RETURN_IF_NOT_OK((*out)->Reshape(shape)); - } - return Status::OK(); - } - - // Create a string Tensor from a string vector by default. - static Status CreateFromVector(const std::vector &items, const TensorShape &shape, TensorPtr *out) { - return CreateFromVector(items, shape, DataType(DataType::DE_STRING), out); - } - - /// Create a numeric scalar Tensor from the given value. - /// \tparam T type of value - /// \param[in] item value - /// \param[out] out Created tensor - /// \return Status code - template - static Status CreateScalar(const T &item, TensorPtr *out) { - const DataType type = DataType::FromCType(); - const auto item_ptr = reinterpret_cast(&item); - return CreateFromMemory(TensorShape::CreateScalar(), type, item_ptr, out); - } - - /// Create a tensor from a binary file on disk. - /// \param[in] path file to be read - /// \param[out] out Created Tensor - /// \return Status code - static Status CreateFromFile(const std::string &path, TensorPtr *out); - - /// Destruct the tensor and release the memory using the allocator - virtual ~Tensor(); - - /// Equality operator. compares tensor shape, type and data - /// \param[in] rhs Tensor to be compared with - /// \return bool - bool operator==(const Tensor &rhs) const; - - bool operator!=(const Tensor &rhs) const { return !((*this) == rhs); } - - Status to_json(nlohmann::json *out_json); - - template - Status to_json_convert(nlohmann::json *args); - - static Status from_json(nlohmann::json op_params, std::shared_ptr *tensor); - - template - static Status from_json_convert(const nlohmann::json &json_data, const TensorShape &shape, - std::shared_ptr *tensor); - - static Status from_json_convert(const nlohmann::json &json_data, const TensorShape &shape, const DataType &type, - std::shared_ptr *tensor); - - /// Get item located at `index`, caller needs to provide the type. - /// \tparam T - /// \param[in] index vector - /// \return return the item specified at index - template - Status GetItemAt(T *o, const std::vector &index) const; - - /// Get string located at `index`. - /// \param[in] index vector - /// \return return std::string_view specified at index - Status GetItemAt(std::string_view *o, const std::vector &index) const; - - template - Status GetUnsignedIntAt(T *o, const std::vector &index) const; - - template - Status GetSignedIntAt(T *o, const std::vector &index) const; - - template - Status GetFloatAt(T *o, const std::vector &index) const; - - /// set item at location specified by index - /// \param[in] index - /// \param[in] value of type `T` - template - Status SetItemAt(const std::vector &index, const T &value) { - T *ptr = nullptr; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index)); - *ptr = value; - return Status::OK(); - } - - /// set string item at location specified by index - /// \param[in] index - /// \param[in] value of type std::string - Status SetItemAt(const std::vector &index, const std::string &value) { - RETURN_UNEXPECTED_IF_NULL(data_); - uchar *ptr = nullptr; - offset_t length = 0; - RETURN_IF_NOT_OK(GetItemPtr(&ptr, index, &length)); - if (value.length() != length) { - RETURN_STATUS_UNEXPECTED("Length of the new string does not match the item."); - } - const int ret_code = memcpy_s(reinterpret_cast(ptr), length, value.c_str(), length); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == 0, "Failed to set data into tensor."); - - return Status::OK(); - } - - /// Fill tensor with zeros. Does not support string or bytes. - Status Zero() { - CHECK_FAIL_RETURN_UNEXPECTED(!type_.IsString(), "Can not fill zeros on tensor of type string or bytes."); - dsize_t size = SizeInBytes(); - CHECK_FAIL_RETURN_UNEXPECTED(memset_sp(GetMutableBuffer(), size, 0, size) == 0, - "Failed to fill tensor with zeroes."); - return Status::OK(); - } - - /// Fill all elements in the Tensor with the given value of type `T`. Does not support string or bytes. - /// \tparam T - /// \param value[in] - template - Status Fill(const T &value) { - CHECK_FAIL_RETURN_UNEXPECTED(!type_.IsString(), "Can not fill on tensor of type string or bytes."); - const int64_t cellSize = type_.SizeInBytes(); - if ((data_ != nullptr) && type_.IsCompatible()) { - for (dsize_t i = 0; i < Size(); i++) { - CHECK_FAIL_RETURN_UNEXPECTED(memcpy_s((data_ + i * cellSize), cellSize, &value, cellSize) == 0, "memcpy err"); - } - return Status::OK(); - } else { - std::string err; - err += (data_ == nullptr) ? "data_ is nullptr \t" : ""; - err += type_.IsCompatible() ? "data type not compatible\t" : ""; - return {StatusCode::kMDUnexpectedError, err}; - } - } - - /// Getter function for shape - /// \return - const TensorShape &shape() const { return shape_; } - - /// Check if tensor has data - /// \return bool - true if tensor is not empty - bool HasData() const { return data_ != nullptr; } - - /// Check if tensor is complex - /// \return bool - true if tensor is complex - bool IsComplex() const { - if (shape_.empty()) { - return false; - } - // check the last dim all be 2 - return shape_[-1] == 2; - } - - /// Reshape the tensor. The given shape should have the same number of elements in the Tensor - /// \param shape - virtual Status Reshape(const TensorShape &shape); - - /// \return number of elements in this tensor - dsize_t Size() const { return shape().NumOfElements(); } - - /// \return the number of bytes this tensor is needs - dsize_t SizeInBytes() const { - if (data_end_ == nullptr) { - return type_.SizeInBytes() * shape_.NumOfElements(); - } - return data_end_ - data_; - } - - /// Get the exact length of string / bytes - Status GetStringLength(uint32_t *length) const { - CHECK_FAIL_RETURN_UNEXPECTED(type().IsString(), "Only support to get the length of string or bytes Tensor."); - *length = data_end_ - data_ - (Size() + 1) * kOffsetSize - Size(); - return Status::OK(); - } - - /// \return the rank of the tensor - dsize_t Rank() const { return shape().Rank(); } - - /// Get the starting memory address as a constant for the data of the tensor. This potentially - /// drives an allocation if the data area. - /// \return const unsigned char* - const uchar *GetBuffer() const { return data_; } - - /// Getter of the type - /// \return - DataType type() const { return type_; } - - /// Provide stream operator for displaying the Tensor. - /// \param out Output stream. - /// \param tensor Tensor object to be printed. - /// \return Output stream. - friend std::ostream &operator<<(std::ostream &out, const Tensor &tensor) { - tensor.Print(out); - return out; - } - - /// Invalidate this Tensor by setting the type and shape to unknown and MData to null. - /// Calling this method will make the Tensor and its data inaccessible, use it with caution. - void Invalidate(); - - /// Copy input tensor into self at the location index. - /// Index is a vector of axes which can be incomplete: - /// Ex: shape <2,3>, inserting into index {0} will replace the first row. index {1,2} will replace the last cell. - /// \param index - /// \param input - /// \param partial_insert: boolean to determine if insertion along the full axis is enforced - /// \return Status code - Status InsertTensor(const std::vector &index, const std::shared_ptr &input, - bool partial_insert = false); - - /// Find the address of the given index. Used in InsertTensor. - /// Example: - /// Tensor t= [[1,2],[3,4]] , StartAddrOfIndex({0}) -> &1 - /// \param[in] ind Element index. - /// \param[out] start_addr_of_index Starting address of the element index. - /// \param[out] remaining Remaining shape from the index. - /// \return Status code. - Status StartAddrOfIndex(std::vector ind, uchar **start_addr_of_index, TensorShape *remaining); - - /// Expand the shape of the Tensor with one extra dimension. - /// For example, if the shape is <512,512,3>: - /// *- ExpandDim(0) gives: <1,512,512,3> - /// *- ExpandDim(1) gives: <512,1,512,3> - /// *- ExpandDim(3) gives: <512,512,3,1> - /// \param axis location of the dim - virtual Status ExpandDim(const dsize_t &axis); - - virtual void Squeeze(); - - /// Calculates the strides of the Tensor - /// Ex: Tensor of shape <4,2,2> and type DE_UINT8 (1 byte) - /// The strides will be {6,2,1}. - /// Ex: Tensor of shape <4,2,2> and type DE_UINT32 (4 byte) - /// The strides will be {24,8,4}. - /// \return vector of integers - std::vector Strides() const; - - std::string ToString() const { - std::stringstream ss; - this->Print(ss); - return ss.str(); - } - - /// Handle negative indices. - /// \param[in] index Index to be handled. - /// \param[in] length Axis length of this index. - /// \return Handled index. - static inline dsize_t HandleNeg(dsize_t index, dsize_t length) { return (index < 0) ? (index + length) : index; } - - /// Handle negative indices. - /// \param[in] index_vector Vector of indices. - /// \param[in] length Length of each axis. - /// \return Modified vector of indices. - static inline std::vector HandleNegIndices(const std::vector &index_vector, - const std::vector &length) { - if (length.size() < index_vector.size()) { - MS_LOG(ERROR) << "The size of length should be greater than the shape of index_vector"; - return {}; - } - std::vector indices(index_vector.size(), 0); - for (size_t i = 0; i < index_vector.size(); i++) { - indices[i] = HandleNeg(index_vector[i], length[i]); - } - return indices; - } - - /// Slice tensor bases on the given indices. Copy the sliced data into out tensor. - /// Based on the type of tensor, SliceNumeric or SliceString will be called - /// \param[out] out Tensor - /// \param[in] slice_options vector of SliceOption objects - /// \return Status error code - Status Slice(TensorPtr *out, const std::vector &slice_options); - - /// Get slice_option according to shape and index. - /// \param[in] slice_option input SliceOption object - /// \param[in] slice_index index of SliceOption object - /// \param[out] output slice_option with shape info - /// \return Status error code - Status GetSliceOption(const SliceOption &slice_option, const int32_t &slice_index, SliceOption *slice_option_ptr); - -#ifdef ENABLE_PYTHON - /// Constructs numpy array from input tensor - /// \param[out] data this data is the location of python data - /// \return Status code - Status GetDataAsNumpy(py::array *data); - - /// Constructs numpy array of string or bytes - /// \param[out] data this data is the location of python data - /// \return Status code - Status GetDataAsNumpyStrings(py::array *data); - - template - Status GetDataAsNumpyStrings(py::array *data) { - RETURN_UNEXPECTED_IF_NULL(data); - if (Size() == 0) { - // NumPy will create empty array in type of float64 by default. So we must define the data type. - *data = py::array(type_.AsNumpyType(), shape_.AsVector(), nullptr); - } else { - std::vector string_vector; - string_vector.reserve(Size()); - // Iterate over tensor and create a vector of string_views of strings in the tensor. - try { - (void)std::transform(begin(), end(), std::back_inserter(string_vector), - [](const auto &element) { return static_cast(element); }); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Failed to cast to Unicode string: " + std::string(e.what())); - } - *data = py::array(py::cast(string_vector)); - data->resize(shape_.AsVector()); - } - return Status::OK(); - } - - static Status GetBufferInfo(Tensor *t, py::buffer_info *out); - - /// Returns the Python dictionary stored in the tensor - /// \param[out] data this data is the location of Python data (pybind11 wrapper) - /// \return Status code - Status GetDataAsPythonObject(py::dict *data); - -#endif - - Status SetYuvShape(const uint32_t &width, const uint32_t &widthStride, const uint32_t &height, - const uint32_t &heightStride) { - const std::vector tmp{width, widthStride, height, heightStride}; - yuv_shape_ = tmp; - return Status::OK(); - } - - std::vector GetYuvShape() { return yuv_shape_; } - - /// TensorIterator is a linear iterator that can be used to iterate over the elements of the Tensor - /// The order elements is as the memory layout (i.e., row-major) [[1,2,3],[4,5,6] --> 1,2,3,4,5,6 - /// \tparam T type of values in the Tensor Iterator - template - class TensorIterator { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T *; - using reference = T &; - - explicit TensorIterator(uchar *ptr = nullptr) : ptr_(reinterpret_cast(ptr)) {} - - TensorIterator(const TensorIterator &raw_iterator) { ptr_ = raw_iterator.ptr_; } - - ~TensorIterator() = default; - - TensorIterator &operator=(const TensorIterator &rhs) { - if (this == &rhs) { - return *this; - } - ptr_ = rhs.ptr_; - return *this; - } - - TensorIterator &operator=(T *rhs) { - ptr_ = rhs; - return *this; - } - - bool operator==(const TensorIterator &rhs) const { return ptr_ == rhs.ptr_; } - - bool operator!=(const TensorIterator &rhs) const { return !(*this == rhs); } - - operator bool() const { return ptr_ != nullptr; } - - T &operator*() { return *ptr_; } - - const T &operator*() const { return *ptr_; } - - T *operator->() { return ptr_; } - - TensorIterator &operator+=(const ptrdiff_t &inc) { - ptr_ += inc; - return *this; - } - - TensorIterator &operator-=(const ptrdiff_t &inc) { - ptr_ -= inc; - return *this; - } - - TensorIterator &operator++() { - ++ptr_; - return *this; - } - - TensorIterator &operator--() { - --ptr_; - return *this; - } - - TensorIterator operator++(int) { - auto temp(*this); - ++ptr_; - return temp; - } - - TensorIterator operator--(int) { - auto temp(*this); - --ptr_; - return temp; - } - - TensorIterator operator+(const ptrdiff_t &inc) { - auto temp(*this); - temp.ptr_ += inc; - return temp; - } - - TensorIterator operator-(const ptrdiff_t &inc) { - auto temp(*this); - temp.ptr_ -= inc; - return temp; - } - - protected: - T *ptr_; - }; - - // Specialization of TensorIterator for strings. It returns std::string_view for every item. - // \tparam DUMMY, used to mbe able to specialize the inner class - template - class TensorIterator { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = std::string_view; - using difference_type = ptrdiff_t; - using pointer = std::string_view *; - using reference = std::string_view &; - - explicit TensorIterator(const uchar *data = nullptr, dsize_t index = 0) { - data_ = reinterpret_cast(data); - index_ = index; - } - - TensorIterator(const TensorIterator &raw_iterator) { - data_ = raw_iterator.data_; - index_ = raw_iterator.index_; - } - - ~TensorIterator() = default; - - bool operator==(const TensorIterator &rhs) const { - return data_ == rhs.data_ && index_ == rhs.index_; - } - - bool operator!=(const TensorIterator &rhs) { return !(*this == rhs); } - - operator bool() const { return data_ != nullptr; } - - std::string_view operator*() const { - const auto offset_ = reinterpret_cast(data_); - const offset_t start = offset_[index_]; - const offset_t end = offset_[index_ + 1]; - return std::string_view{data_ + start, end - start - 1}; // -1 to skip the \0 at the end - } - - TensorIterator &operator+=(const dsize_t &inc) { - index_ += inc; - return *this; - } - - TensorIterator &operator-=(const dsize_t &inc) { - index_ -= inc; - return *this; - } - - TensorIterator &operator++() { - ++index_; - return *this; - } - - TensorIterator &operator--() { - --index_; - return *this; - } - - TensorIterator operator++(int) { - auto temp(*this); - ++index_; - return temp; - } - - TensorIterator operator--(int) { - auto temp(*this); - --index_; - return temp; - } - - TensorIterator operator+(const dsize_t &inc) { - auto temp(*this); - temp.index_ += inc; - return temp; - } - - TensorIterator operator-(const dsize_t &inc) { - auto temp(*this); - temp.index_ -= inc; - return temp; - } - - protected: - dsize_t index_; - const char *data_; - }; - - /// Return a TensorIterator that points to the start of the Tensor. - /// It's the user responsibility to use the correct type that matches the Tensor type - /// \tparam T The type of values in the Tensor - /// \return TensorIterator - template - TensorIterator begin() { - return TensorIterator(data_); - } - - /// Return a linear iterator that points to the place after the last element of the Tensor. - /// \tparam T The type of values in the Tensor - /// \return TensorIterator - template - TensorIterator end() { - return TensorIterator(data_end_); - } - - /// Copies the last dimension at `index` from Tensor `src` to this Tensor. - /// \param[in] src Tensor - /// \param[in] index vector to the start of the dimension. The last dim should be 0 - /// \return Status - Status CopyLastDimAt(const std::shared_ptr &src, const std::vector &index); - - /// Get the starting memory address for the data of the tensor. This potentially - /// drives an allocation if the data is null. - /// \return unsigned char* - uchar *GetMutableBuffer() { return data_; } - - /// Skip the offsets and returns the start of the buffer where the real strings is stored. Caller needs to check if - /// the tensor's type is a string, otherwise undefined address would be returned. - /// \return return the address of the first string of the tensor. - uchar *GetStringsBuffer() const { return data_ + kOffsetSize * shape_.NumOfElements() + kOffsetSize; } - - protected: - /// Allocate memory for the tensor using the data_allocator - /// \param[in] length number of bytes to be allocated - /// \return Error Status - Status AllocateBuffer(const dsize_t &length); - - /// A function that prints Tensor recursively, first called by print - /// \param[in] out - /// \param[in] cur_dim - /// \param[in] cur_index - void PrintRecursive(std::ostream &out, int32_t cur_dim, const std::vector &cur_index) const; - - /// Print the info and data of tensor. - /// \param[out] out Output stream. - void Print(std::ostream &out) const; - - /// Print the data of tensor. - /// \param[out] out Output stream. - void PrintData(std::ostream &out) const; - - /// A function that print the value as specified by its index - /// \param[in] index vector representing the index - /// \param[out] out - void PrintItemAt(const std::vector &index, std::ostream &out) const; - - /// Get pointer to item located at `index`, caller needs to provide the type. - /// \tparam T - /// \param[in] index vector - /// \return return a pointer to the item specified at index of type `T` - template - Status GetItemPtr(T **ptr, const std::vector &index) const; - - /// Get pointer to string located at `index` and the length of string - /// \param[in] index vector - /// \return return a pointer to the string specified at index and the length of the string - Status GetItemPtr(uchar **ptr, const std::vector &index, offset_t *length = nullptr) const; - - /// Given a flat index of an item string, return the start and length of the item. - /// \param[in] index Flat index of the item. - /// \param[out] string_start Starting address of the ths string. - /// \param[out] length Length of the string. - /// \return Status code. - Status GetStringAt(dsize_t index, uchar **string_start, offset_t *length) const; - - static const std::unique_ptr> &GetAllocator() { - static auto allocator = std::make_unique>(GlobalContext::Instance()->mem_pool()); - return allocator; - } - - /// all access to shape_ should be via shape - TensorShape shape_; - /// data type of tensor - DataType type_; - /// pointer to the start of the physical data - unsigned char *data_; - /// pointer to the end of the physical data - unsigned char *data_end_ = nullptr; - - /// shape for interpretation of YUV image - std::vector yuv_shape_; - -#ifdef ENABLE_PYTHON - /// Store python dictionary wrapper - py::object python_dict_; - std::string python_dict_as_str_; - - /// Hold the np.ndarray which is from python layer without memcpy cost - py::buffer python_array_; -#endif - - private: - friend class DETensor; - - /// Slice numeric tensors. - Status SliceNumeric(TensorPtr *out, const std::vector> &indices, const TensorShape &shape); - - /// Slice string tensors - Status SliceString(TensorPtr *out, const std::vector> &indices, const TensorShape &shape); - - /// Copy raw data of a array based on shape and strides to the destination pointer - /// \param dst [out] Pointer to the destination array where the content is to be copied - /// \param[in] src Pointer to the source of strided array to be copied - /// \param[in] shape shape of the source array - /// \param[in] strides strides of the source array - /// \param[in] type_size number of bytes needed to store one array element's type - /// \return Status Code - static Status CopyStridedArray(unsigned char *dst, unsigned char *src, std::vector shape, - std::vector strides, uint8_t type_size); - -#ifdef ENABLE_PYTHON - /// Helper function to create a tensor from Numpy array of strings - /// \param[in] arr Numpy array - /// \param[out] out Created Tensor - /// \return Status - static Status CreateFromNpString(py::array arr, TensorPtr *out); -#endif -}; - -template <> -inline Tensor::TensorIterator Tensor::end() { - return TensorIterator(data_, shape_.NumOfElements()); -} - -/// Create a string scalar Tensor from the given value. -/// \param[in] item value -/// \param[out] out Created tensor -/// \return Status code -template <> -inline Status Tensor::CreateScalar(const std::string &item, TensorPtr *out) { - RETURN_UNEXPECTED_IF_NULL(out); - return CreateFromVector({item}, TensorShape::CreateScalar(), DataType(DataType::DE_STRING), out); -} -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_H_ diff --git a/mindspore-lite/minddata/dataset/core/tensor_helpers.cc b/mindspore-lite/minddata/dataset/core/tensor_helpers.cc deleted file mode 100644 index da327c72d..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_helpers.cc +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/core/tensor_helpers.h" -#include - -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -namespace mindspore { -namespace dataset { - -void IndexGeneratorHelper(int8_t depth, std::vector *numbers, - const std::vector &slice_list, - std::vector> *matrix) { - if (numbers == nullptr || matrix == nullptr) { - MS_LOG(ERROR) << "Invalid input pointer, can't be NULL"; - return; - } - // for loop changes if its an index instead of a slice object - if (depth > 0) { - int8_t new_depth = depth - 1; - // depth is always less than or equal to numbers->size() (based on the caller functions) - size_t curr_ind = static_cast(numbers->size() - static_cast(depth)); - if (curr_ind >= slice_list.size()) { - MS_LOG(ERROR) << "The index is out of range in slice_list."; - return; - } - - if (slice_list[curr_ind].slice_.valid()) { - dsize_t increment = slice_list[curr_ind].slice_.step_; - - if (increment > 0) { - for (dsize_t i = slice_list[curr_ind].slice_.start_; i < slice_list[curr_ind].slice_.stop_; - i = i + slice_list[curr_ind].slice_.step_) { - (*numbers)[curr_ind] = i; - IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); - } - } else { - for (dsize_t j = slice_list[curr_ind].slice_.start_; j > slice_list[curr_ind].slice_.stop_; - j = j + slice_list[curr_ind].slice_.step_) { - (*numbers)[curr_ind] = j; - IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); - } - } - } else { - for (size_t k = 0; k < slice_list[curr_ind].indices_.size(); k++) { - (*numbers)[curr_ind] = slice_list[curr_ind].indices_[k]; - IndexGeneratorHelper(new_depth, numbers, slice_list, matrix); - } - } - - } else { - (*matrix).emplace_back((*numbers)); - } -} - -// Used to generate slice indices -std::vector> IndexGenerator(const std::vector &slice_list) { - int8_t depth = slice_list.size(); - std::vector numbers(depth, 0); - std::vector> matrix(0, std::vector(depth, 0)); - - IndexGeneratorHelper(depth, &numbers, slice_list, &matrix); - - return matrix; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/tensor_helpers.h b/mindspore-lite/minddata/dataset/core/tensor_helpers.h deleted file mode 100644 index 05163dfb2..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_helpers.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_HELPERS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_HELPERS_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -/// Recursive helper function to generate indices based on vector of SliceOptions. It recursively iterates through each -/// range represented by slice_options to generate a list of indices to be sliced. -/// \param[out] matrix Generated nested vector of indices -/// Example: For a 4 x 2 tensor, and with slice_list = {SliceOption({0})} (the first row), matrix will become -/// {{0}}. For slice_list = {SliceOption(all), SliceOption({0})} (the first column), matrix will become -/// {{0, 0}, {1, 0}, {2, 0}, {3, 0}}. -/// For slice_list = {SliceOption({0, 2})}, matrix will become {{0}, {2}}. The size of each nested array is always -/// equal to (slice_list).size(). -/// \param[in] depth used to keep track of recursion level -/// \param[in] numbers vector used to represent current index -/// \param[in] matrix 2D vector to be populated with desired indices -/// \param[in] slice_options vector of SliceOption objects -void IndexGeneratorHelper(int8_t depth, std::vector *numbers, const std::vector &slice_list, - std::vector> *matrix); - -/// Generate indices based on vector of SliceOptions -/// Calls the recursive helper function IndexGeneratorHelper -/// \param[in] slice_list vector of SliceOption objects. Note: If the user passes -/// {SliceOption(true), SliceOption(true)}, it will return a M x 2 vector, instead of reducing it to -/// {SliceOption(true)} first to only generate a M x 1 vector. -/// \return std::vector> 2D vector of generated indices, M x (slice_list).size() -std::vector> IndexGenerator(const std::vector &slice_list); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_HELPERS_H_ diff --git a/mindspore-lite/minddata/dataset/core/tensor_row.cc b/mindspore-lite/minddata/dataset/core/tensor_row.cc deleted file mode 100644 index 19bce776d..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_row.cc +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { - -TensorRow::TensorRow() noexcept - : id_(kDefaultRowId), path_({}), tensor_row_flag_(kFlagNone), timer_(std::make_shared()) {} - -TensorRow::TensorRow(size_type n, const TensorRow::value_type &t) noexcept - : id_(kDefaultRowId), path_({}), row_(n, t), tensor_row_flag_(kFlagNone), timer_(std::make_shared()) {} - -TensorRow::TensorRow(const TensorRow::vector_type &v) - : id_(kDefaultRowId), path_({}), row_(v), tensor_row_flag_(kFlagNone), timer_(std::make_shared()) {} - -TensorRow::TensorRow(row_id_type id, const std::initializer_list &lst) - : id_(id), path_({}), row_(lst), tensor_row_flag_(kFlagNone), timer_(std::make_shared()) {} - -TensorRow::TensorRow(const TensorRow &tr) - : id_(tr.id_), path_(tr.path_), row_(tr.row_), tensor_row_flag_(tr.tensor_row_flag_), timer_(tr.timer_) {} - -TensorRow::TensorRow(TensorRow::TensorRowFlags flag) - : id_(kDefaultRowId), path_({}), tensor_row_flag_(flag), timer_(std::make_shared()) {} - -TensorRow &TensorRow::operator=(const TensorRow &tr) { - if (this == &tr) { - return *this; - } - row_ = tr.row_; - id_ = tr.id_; - path_ = tr.path_; - tensor_row_flag_ = tr.tensor_row_flag_; - timer_ = tr.timer_; - return *this; -} - -Status TensorRow::Clone(TensorRow *new_tr) const { - RETURN_UNEXPECTED_IF_NULL(new_tr); - new_tr->row_.clear(); - for (const std::shared_ptr &s : row_) { - std::shared_ptr d; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(s, &d)); - (void)new_tr->row_.emplace_back(std::move(d)); - } - new_tr->id_ = id_; - new_tr->path_ = path_; - new_tr->tensor_row_flag_ = tensor_row_flag_; - new_tr->timer_ = timer_; - return Status::OK(); -} - -TensorRow &TensorRow::operator=(const std::initializer_list &lst) { - row_ = lst; - tensor_row_flag_ = kFlagNone; - return *this; -} - -TensorRow::TensorRow(TensorRow::vector_type &&v) noexcept - : id_(kDefaultRowId), - path_({}), - row_(std::move(v)), - tensor_row_flag_(kFlagNone), - timer_(std::make_shared()) {} - -TensorRow::TensorRow(row_id_type id, std::initializer_list &&lst) noexcept - : id_(id), path_({}), row_(std::move(lst)), tensor_row_flag_(kFlagNone), timer_(std::make_shared()) {} - -TensorRow::TensorRow(TensorRow &&tr) noexcept { - id_ = tr.id_; - path_ = std::move(tr.path_); - row_ = std::move(tr.row_); - tensor_row_flag_ = tr.tensor_row_flag_; - timer_ = std::move(tr.timer_); -} - -TensorRow &TensorRow::operator=(TensorRow &&tr) noexcept { - if (this == &tr) { - return *this; - } - row_ = std::move(tr.row_); - id_ = tr.id_; - tr.id_ = kDefaultRowId; - path_ = std::move(tr.path_); - tensor_row_flag_ = tr.tensor_row_flag_; - timer_ = std::move(tr.timer_); - return *this; -} - -TensorRow &TensorRow::operator=(std::initializer_list &&lst) noexcept { - row_ = std::move(lst); - tensor_row_flag_ = kFlagNone; - return *this; -} - -Status TensorRow::ValidateTensorRow(const TensorRow &input, const DataType &data_type) { - if (data_type == DataType::DE_UNKNOWN) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: Data type was not recognized."); - } - if (data_type.IsString()) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: Data type string and bytes are not supported."); - } - if (input.size() != 1) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: The input TensorRow must have exactly one tensor."); - } - return Status::OK(); -} - -// TODO(ly): need support in independent mode -void TensorRow::CopyTimerTo(TensorRow *out) const { out->timer_ = timer_; } - -void TensorRow::TimerRecord(const std::string &op_name, const std::string &info_name, - const std::vector &duration, TensorRow *copy_from) { - if (!timer_->Enabled()) { - return; - } - - // since some op(map/batch/zip) will create new TensorRow, need to copy original info - if (copy_from) { - copy_from->CopyTimerTo(this); - } - timer_->Record(op_name, info_name, duration); -} - -const char RowTimer::kMaxTime[] = " WorkerTimeMax"; -const char RowTimer::kMinTime[] = " WorkerTimeMin"; -const char RowTimer::kWorkerTime[] = "WorkerTime"; -const char RowTimer::kIOTime[] = "IOTime"; -const char RowTimer::kMaxIOTime[] = "IOTimeMax"; -const char RowTimer::kMinIOTime[] = "IOTimeMin"; -const char RowTimer::kLoadTensorTime[] = "LoadTensorTime"; -const char RowTimer::kThroughputTime[] = " PipelineTime"; -const char RowTimer::kPushToDeviceTime[] = " PushToAscendTime"; -const char RowTimer::kRowCount[] = "kRowCount"; - -bool RowTimer::Enabled() { -#ifndef ENABLE_ANDROID - return IS_VLOG_ON(VL_MD); -#else - return false; -#endif -} - -void RowTimer::Record(const std::string &op_name, const std::string &info_name, const std::vector &duration) { - time_table_[op_name][info_name] = std::move(duration); - if (std::find(op_order_.begin(), op_order_.end(), op_name) == op_order_.end()) { - op_order_.push_back(op_name); - } -} - -std::string RowTimer::Summary(const std::vector &specified_op) { - std::stringstream ss; - std::string title = "\n[PROF Dataset]\n"; - std::string title2 = - "worker_time: The worker time in OP, include max, min, avg; " - "io_time: The I/O time of in OP, which is part of worker_time; " - "throughput_time: The overall throughput time of the data pipeline; " - "push_device_time: The time taken for the host to push data to the device.\n"; - ss << title << title2; - for (auto &op : op_order_) { - // only print specified op - if (!specified_op.empty()) { - auto it = std::find_if(specified_op.begin(), specified_op.end(), - [&](const std::string &str) { return op.find(str) != std::string::npos; }); - if (it == specified_op.end()) { - continue; - } - } - ss << "-- " << std::left << std::setw(20) << op << ": "; - - // has worker time - if (time_table_[op].find(RowTimer::kWorkerTime) != time_table_[op].end()) { - ss << "worker_time ("; - ss << "avg:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kWorkerTime].front() << "ms, "; - if (time_table_[op].find(RowTimer::kMaxTime) != time_table_[op].end()) { - ss << "max:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kMaxTime].front() << "ms, "; - } else { - ss << "max: /, "; - } - if (time_table_[op].find(RowTimer::kMinTime) != time_table_[op].end()) { - ss << "min:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kMinTime].front() << "ms, "; - } else { - ss << "min: /, "; - } - if (time_table_[op].find(RowTimer::kRowCount) != time_table_[op].end()) { - ss << "count:" << std::fixed << std::setprecision(0) << time_table_[op][RowTimer::kRowCount].front() << ")"; - } else { - ss << "count:" << 1 << ") "; - } - } - - // has io time - if (time_table_[op].find(RowTimer::kIOTime) != time_table_[op].end()) { - ss << ", io_time ("; - ss << "avg:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kIOTime].front() << "ms, "; - if (time_table_[op].find(RowTimer::kMaxIOTime) != time_table_[op].end()) { - ss << "max:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kMaxIOTime].front() << "ms, "; - } else { - ss << "max: /, "; - } - if (time_table_[op].find(RowTimer::kMinIOTime) != time_table_[op].end()) { - ss << "min:" << std::fixed << std::setprecision(2) << time_table_[op][RowTimer::kMinIOTime].front() << "ms, "; - } else { - ss << "min: /, "; - } - if (time_table_[op].find(RowTimer::kRowCount) != time_table_[op].end()) { - ss << "count:" << std::fixed << std::setprecision(0) << time_table_[op][RowTimer::kRowCount].front() << ") "; - } else { - ss << "count:" << 1 << ") "; - } - } - - // has kThroughputTime time - if (time_table_[op].find(RowTimer::kThroughputTime) != time_table_[op].end()) { - ss << "throughput_time: " << time_table_[op][RowTimer::kThroughputTime].front() << "ms"; - // has kPushToDeviceTime time - if (time_table_[op].find(RowTimer::kPushToDeviceTime) != time_table_[op].end()) { - ss << ", push_device_time: " << time_table_[op][RowTimer::kPushToDeviceTime].front() << "ms"; - } - } - ss << "\n"; - } - return ss.str(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/tensor_row.h b/mindspore-lite/minddata/dataset/core/tensor_row.h deleted file mode 100644 index 5c87cdd55..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_row.h +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_ROW_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_ROW_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -namespace mindspore { -namespace dataset { - -class TensorRow; // A set of Tensor pointers with an id -using TensorTable = std::vector; // The table of tensors is a vector of rows -using TensorQTable = std::deque; // A different flavour of tensor table, this one has queue functionality - -class RowTimer { - public: - RowTimer() = default; - ~RowTimer() = default; - - static const char kMaxTime[]; - static const char kMinTime[]; - static const char kWorkerTime[]; - static const char kMaxIOTime[]; - static const char kMinIOTime[]; - static const char kIOTime[]; - static const char kLoadTensorTime[]; - static const char kThroughputTime[]; - static const char kPushToDeviceTime[]; - static const char kRowCount[]; - - // time_info {max: [], min: [], avg: [], worker_time: []} - using time_info = std::map>; - - // time_table {op1: time_table, op2: time_table2, ...} - std::map time_table_; - - // op order sequential - std::vector op_order_; - - // return time table - const std::map &TimeTable() { return time_table_; } - - // status to indicate on or off - bool Enabled(); - - // record to time info - void Record(const std::string &op_name, const std::string &info_name, const std::vector &duration); - - // test - std::string Summary(const std::vector &specified_op = {}); -}; - -class TensorRow { - public: - static constexpr row_id_type kDefaultRowId = -1; // Default row id - - enum TensorRowFlags : uint32_t { - kFlagNone = 0, - kFlagEOE = 1U, // The row is an eoe end-of-epoch msg - kFlagEOF = 1U << 1, // The row is an eof end-of-data msg - kFlagWait = 1U << 2, // The row is an control signal for workers to suspend operations - kFlagQuit = 1U << 3, // The row is a control signal for workers to quit - kFlagSkip = 1U << 4, // The row is a control signal for workers to skip this row - kFlagError = 1U << 5, // The row is an error row (needs to be replaced with another row or skipped, as per - // ErrorSamplesMode config) - kFlagEOB = 1U << 6 // The row is an eob end-of-batch msg - }; - - // Type definitions - using size_type = size_t; - using value_type = std::shared_ptr; - using reference = std::shared_ptr &; - using const_reference = const std::shared_ptr &; - using vector_type = std::vector>; - using iterator = std::vector>::iterator; - using const_iterator = std::vector>::const_iterator; - - TensorRow() noexcept; - - TensorRow(size_type n, const value_type &t) noexcept; - - // Copy Constructors - explicit TensorRow(const vector_type &v); - - TensorRow(row_id_type id, const std::initializer_list &lst); - - TensorRow(const TensorRow &tr); - - TensorRow &operator=(const TensorRow &tr); - - TensorRow &operator=(const std::initializer_list &lst); - - // Move Constructors - explicit TensorRow(vector_type &&v) noexcept; - - TensorRow(row_id_type id, std::initializer_list &&lst) noexcept; - - TensorRow(TensorRow &&tr) noexcept; - - TensorRow &operator=(TensorRow &&tr) noexcept; - - TensorRow &operator=(std::initializer_list &&lst) noexcept; - - // Destructor - ~TensorRow() = default; - - // Deep copy - /// \param[in] new_tr the tensor row to clone to - Status Clone(TensorRow *new_tr) const; - - /// Convert a vector of primitive types to a TensorRow consisting of one 1-D Tensor with the shape n. - /// \tparam `T` - /// \param[in] o input vector - /// \param[out] output TensorRow - template - static Status ConvertToTensorRow(const std::vector &o, TensorRow *output) { - RETURN_UNEXPECTED_IF_NULL(output); - DataType data_type = DataType::FromCType(); - if (data_type == DataType::DE_UNKNOWN) { - RETURN_STATUS_UNEXPECTED("ConvertToTensorRow: Data type was not recognized."); - } - if (data_type.IsString()) { - RETURN_STATUS_UNEXPECTED("ConvertToTensorRow: Data type string and bytes are not supported."); - } - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(o, &tensor)); - output->push_back(tensor); - return Status::OK(); - } - - /// Convert a single primitive type to a TensorRow consisting of one single data Tensor. - /// \tparam `T` - /// \param[in] o input - /// \param[out] output TensorRow - template - static Status ConvertToTensorRow(const T &o, TensorRow *output) { - RETURN_UNEXPECTED_IF_NULL(output); - DataType data_type = DataType::FromCType(); - if (data_type == DataType::DE_UNKNOWN) { - RETURN_STATUS_UNEXPECTED("ConvertToTensorRow: Data type was not recognized."); - } - if (data_type.IsString()) { - RETURN_STATUS_UNEXPECTED("ConvertToTensorRow: Data type string and bytes are not supported."); - } - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(o, &tensor)); - output->push_back(tensor); - return Status::OK(); - } - - /// Return the value in a TensorRow consisting of 1 single data Tensor. - /// \tparam `T` - /// \param[in] input TensorRow - /// \param[out] o the primitive variable - template - static Status ConvertFromTensorRow(const TensorRow &input, T *o) { - RETURN_UNEXPECTED_IF_NULL(o); - DataType data_type = DataType::FromCType(); - RETURN_IF_NOT_OK(ValidateTensorRow(input, data_type)); - if (input.at(0)->type() != data_type) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: The output type doesn't match the input tensor type."); - } - if (input.at(0)->shape() != TensorShape({})) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: The input tensors must be a scalar tensor."); - } - return input.at(0)->GetItemAt(o, {}); - } - - /// Convert a TensorRow consisting of one 1-D tensor to a vector of size n. - /// \tparam `T` - /// \param[in] o TensorRow consisting of one 1-D tensor - /// \param[out] o vector of primitive variable - template - static Status ConvertFromTensorRow(const TensorRow &input, std::vector *o) { - RETURN_UNEXPECTED_IF_NULL(o); - DataType data_type = DataType::FromCType(); - RETURN_IF_NOT_OK(ValidateTensorRow(input, data_type)); - if (input.at(0)->Rank() != 1) { - RETURN_STATUS_UNEXPECTED("ConvertFromTensorRow: The input tensor must have a rank of 1."); - } - for (auto it = input.at(0)->begin(); it != input.at(0)->end(); it++) { - o->push_back(*it); - } - return Status::OK(); - } - - // Functions to fetch/set id/vector - row_id_type getId() const { return id_; } - - void setId(row_id_type id) { id_ = id; } - - std::vector getPath() const { return path_; } - - void setPath(const std::vector &path) { path_ = path; } - - const vector_type &getRow() const { return row_; } - - dsize_t SizeInBytes() const { - dsize_t sz = 0; - for (auto &it : row_) { - sz += it->SizeInBytes(); - } - return sz; - } - - // Wrapper functions to support vector operations - void emplace_back(value_type t) { row_.emplace_back(t); } - - void push_back(value_type t) { row_.push_back(t); } - - void clear() noexcept { row_.clear(); } - - // Reset both the tensor row vector and flags - void reset() noexcept { - row_.clear(); - tensor_row_flag_ = kFlagNone; - } - - size_type size() const noexcept { return row_.size(); } - - void reserve(size_type size) { row_.reserve(size); } - - void resize(size_type size) { row_.resize(size); } - - const bool empty() { return row_.empty(); } - - void insert(const_iterator position, const_iterator first, const_iterator last) { - row_.insert(position, first, last); - } - - // Wrapper functions to support vector element access - reference at(size_type index) { return row_.at(index); } - - const_reference at(size_type index) const { return row_.at(index); } - - reference front() { return row_.front(); } - - const_reference front() const { return row_.front(); } - - reference back() { return row_.back(); } - - const_reference back() const { return row_.back(); } - - reference operator[](size_type index) { return row_[index]; } - - const_reference operator[](size_type index) const { return row_[index]; } - - // Wrapper functions to support vector iteration - iterator begin() { return row_.begin(); } - - const_iterator begin() const { return row_.begin(); } - - iterator end() { return row_.end(); } - - const_iterator end() const { return row_.end(); } - - // Convenience getter functions for flag checking - bool eof() const { return (static_cast(tensor_row_flag_) & static_cast(kFlagEOF)); } - - bool eoe() const { return (static_cast(tensor_row_flag_) & static_cast(kFlagEOE)); } - - bool eob() const { return (static_cast(tensor_row_flag_) & static_cast(kFlagEOB)); } - - bool wait() const { return (static_cast(tensor_row_flag_) & static_cast(kFlagWait)); } - - bool quit() const { return (static_cast(tensor_row_flag_) & static_cast(kFlagQuit)); } - - bool skip() const { - return static_cast(static_cast(tensor_row_flag_) & static_cast(kFlagSkip)); - } - - bool error() const { - return static_cast(static_cast(tensor_row_flag_) & static_cast(kFlagError)); - } - - TensorRowFlags Flags() const { return tensor_row_flag_; } - - std::string FlagName() const { - switch (tensor_row_flag_) { - case TensorRowFlags::kFlagNone: - return "Data"; - case TensorRowFlags::kFlagEOE: - return "EOE"; - case TensorRowFlags::kFlagEOF: - return "EOF"; - case TensorRowFlags::kFlagWait: - return "Wait"; - case TensorRowFlags::kFlagQuit: - return "Quit"; - case TensorRowFlags::kFlagSkip: - return "Skip"; - case TensorRowFlags::kFlagError: - return "Error"; - default: - return "Unknown"; - } - } - - explicit TensorRow(TensorRowFlags flag); - - std::shared_ptr Timer() const { return timer_; } - - void CopyTimerTo(TensorRow *out) const; - - void TimerRecord(const std::string &op_name, const std::string &info_name, const std::vector &duration, - TensorRow *copy_from = nullptr); - - protected: - row_id_type id_; - std::vector path_; - std::vector> row_; - - TensorRowFlags tensor_row_flag_; - std::shared_ptr timer_; - - private: - /// Validate data type of TensorRow for conversions. - /// \param[in] input TensorRow - /// \param[in] data_type data type of the tensor row - static Status ValidateTensorRow(const TensorRow &input, const DataType &data_type); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_ROW_H_ diff --git a/mindspore-lite/minddata/dataset/core/tensor_shape.cc b/mindspore-lite/minddata/dataset/core/tensor_shape.cc deleted file mode 100644 index f52d184fa..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_shape.cc +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#define MAX_INTEGER_DTYPE 9223372036854775807 - -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" - -#include - -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -constexpr dsize_t TensorShape::kDimUnknown; - -bool multi_ok(dsize_t x, dsize_t y) { - dsize_t p = x * y; - if (x == 0) { - return true; - } - return p / x == y; -} - -dsize_t TensorShape::NumOfElements() const { - if (!known() && strides_.size() < 1) { - return 0; - } - return strides_[0]; -} - -void TensorShape::Print(std::ostream &out) const { - if (!known() && raw_shape_.empty()) { - out << ""; - } else { - out << "<"; - for (auto i = 0; i < this->Rank(); i++) { - if (raw_shape_[i] == kDimUnknown) { - out << "*"; - } else { - out << raw_shape_[i]; - } - if (i != this->Rank() - 1) { - out << ","; - } - } - out << ">"; - } -} - -TensorShape::TensorShape(const std::initializer_list &list) { AddListToShape(list); } - -TensorShape::TensorShape(const std::vector &list) { AddListToShape(list); } - -TensorShape::TensorShape(const TensorShape &shape) - : raw_shape_(shape.raw_shape_), strides_(shape.strides_), known_(shape.known_) {} - -TensorShape::TensorShape(TensorShape &&shape) noexcept - : raw_shape_(std::move(shape.raw_shape_)), strides_(std::move(shape.strides_)), known_(shape.known_) {} - -TensorShape &TensorShape::operator=(const TensorShape &shape) { - if (this != &shape) { - raw_shape_ = shape.raw_shape_; - strides_ = shape.strides_; - known_ = shape.known_; - } - return *this; -} - -TensorShape &TensorShape::operator=(TensorShape &&shape) noexcept { - if (this != &shape) { - raw_shape_ = std::move(shape.raw_shape_); - strides_ = std::move(shape.strides_); - known_ = shape.known_; - } - return *this; -} - -#ifdef ENABLE_PYTHON -TensorShape::TensorShape(py::list l) { - std::vector list_c; - for (auto &i : l) { - if (!i.is_none()) { - list_c.push_back(i.cast()); - } else { - list_c.push_back(TensorShape::kDimUnknown); - } - } - AddListToShape(list_c); -} -#endif - -#ifndef ENABLE_ANDROID -TensorShape::TensorShape(cv::MatSize cv_size, uint32_t type) : known_(true) { - for (int i = 0; i < cv_size.dims(); i++) { - raw_shape_.push_back(cv_size[i]); - } - auto channels = static_cast(1 + (type >> static_cast(CV_CN_SHIFT))); - if (channels != 1) { - raw_shape_.push_back(channels); - } -} -#endif - -TensorShape TensorShape::CreateUnknownRankShape() { - TensorShape s({}); - s.known_ = false; - return s; -} - -TensorShape TensorShape::InsertDim(dsize_t axis, dsize_t dim) const { - std::vector tmp = AsVector(); - (void)tmp.insert(tmp.begin() + axis, dim); - return TensorShape(tmp); -} - -std::vector TensorShape::AsVector() const { - return std::vector(raw_shape_.begin(), raw_shape_.end()); -} - -bool TensorShape::IsValidIndex(const std::vector &index) const { - dsize_t s_rank = Rank(); - if (index.size() != s_rank) { - return false; - } - for (dsize_t i = 0; i < s_rank; i++) { - if (index[i] < 0 || raw_shape_[i] <= index[i]) { - return false; - } - } - return true; -} - -template -void TensorShape::AddListToShape(const T &list) { - raw_shape_.resize(list.size()); - strides_.resize(list.size() + 1); - strides_[list.size()] = 1; - known_ = true; - dsize_t size = 0; - auto itr = std::rbegin(list); // iterate over the list in reverse order - auto s = list.size() - 1; // to compute strides while adding dims - for (; itr != std::rend(list); itr++, s--) { - dsize_t dim = *itr; - if (dim > 0) { - if (strides_[s + 1] > std::numeric_limits::max() / dim) { - MS_LOG(ERROR) << "Invalid shape data, overflow occurred!"; - known_ = false; - raw_shape_.clear(); - return; - } - strides_[s] = dim * strides_[s + 1]; - } - if (dim < 0) { - known_ = false; - } - if (dim > kDeMaxDim) { - std::stringstream ss; - ss << "Invalid shape data, dim (" << dim << ") is larger than the maximum dim size(" << kDeMaxDim << ")!"; - MS_LOG(ERROR) << ss.str().c_str(); - known_ = false; - raw_shape_.clear(); - return; - } - raw_shape_[s] = dim; - size++; - } - if (size > kDeMaxRank) { - std::stringstream ss; - ss << "Invalid shape data, rank (" << size << ") is larger than the maximum rank size(" << kDeMaxRank << ")."; - MS_LOG(ERROR) << ss.str().c_str(); - known_ = false; - raw_shape_.clear(); - return; - } -} - -TensorShape TensorShape::CreateUnknownShapeWithRank(dsize_t rank) { - TensorShape s({}); - for (dsize_t i = 0; i < rank; i++) { - s.raw_shape_.push_back(kDimUnknown); - } - s.known_ = false; - return s; -} - -TensorShape TensorShape::PrependDim(dsize_t dim) const { - if (Size() == 0) { - return TensorShape({dim}); - } - return InsertDim(0, dim); -} - -TensorShape TensorShape::AppendDim(dsize_t dim) const { - auto vec = AsVector(); - vec.push_back(dim); - return TensorShape(vec); -} - -#ifdef ENABLE_PYTHON -py::list TensorShape::AsPyList() { - py::list list; - for (auto i : raw_shape_) { - list.append(i); - } - return list; -} -#endif - -TensorShape TensorShape::Squeeze() const { - std::vector new_shape(raw_shape_.size()); - auto it = std::copy_if(raw_shape_.begin(), raw_shape_.end(), new_shape.begin(), [](auto s) { return s != 1; }); - new_shape.resize(std::distance(new_shape.begin(), it)); - return TensorShape(new_shape); -} - -std::vector TensorShape::Strides() const { return std::vector{strides_.begin() + 1, strides_.end()}; } - -// Name: ToFlatIndex() -// Description: convert a vector style index to number, used to access memory internal use only -Status TensorShape::ToFlatIndex(const std::vector &index, dsize_t *flat_index) const { - RETURN_UNEXPECTED_IF_NULL(flat_index); - if (index.size() != raw_shape_.size()) { - std::stringstream ss; - ss << "Index size (" << index.size() << ") does not match the shape size (" << raw_shape_.size() << ")."; - RETURN_STATUS_UNEXPECTED(ss.str()); - } - *flat_index = 0; - for (size_t k = 0; k < index.size(); k++) { - *flat_index += - (index[k] == 0) ? 0 : index[k] * strides_[k + 1]; // skip the first element of strides_ which is numOfElements - } - CHECK_FAIL_RETURN_UNEXPECTED(*flat_index < NumOfElements(), "Not a valid index"); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/core/tensor_shape.h b/mindspore-lite/minddata/dataset/core/tensor_shape.h deleted file mode 100644 index 8f85d7d81..000000000 --- a/mindspore-lite/minddata/dataset/core/tensor_shape.h +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_SHAPE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_SHAPE_H_ - -#include -#include -#include -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include -#endif - -#ifdef ENABLE_PYTHON -#include "pybind11/pybind11.h" -namespace py = pybind11; -#endif - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" - -namespace mindspore { -namespace dataset { -// Class that represents a shape of a Tensor. A shape can be: -// -# Known shape (mKnown = true) -// -# Scalar --> empty vector --> <> -// -# n-Dim --> not empty vector --> where di is >= 0\n -// Example: <1,2>, <1>, <1,13,10,11,1> -// -# Unknown shape (mKnown = false) -// -# Rank is unknown --> empty vector --> <> -// -# one or more dim is unknown --> not empty vector --> where di is unknown\n -// Example: <3,?> (the 1st dim is unknown)\n -// <2,?,?,?> (all dims but the 0th dim are unknown) - -/// \brief TensorShape supports any dim > 0 and < 2^31-1 -class DATASET_API TensorShape { - public: - static constexpr dsize_t kDimUnknown = -1; // constant for an unknown dimension - - // Force the compiler to not create a no-arg constructor - TensorShape() = delete; - - /// \brief Create a Shape from an initialization list (e.g., TensorShape s = {2,2}). - /// If one of the dims is set to DIM_UNKNOWN, the shape will flagged as unKnown - /// \param[in] list Length list of each axis. - explicit TensorShape(const std::initializer_list &list); - - /// \brief Create a Shape from a vector (e.g., TensorShape s = std::vector({2,2}) ). - /// If one of the dims is set to DIM_UNKNOWN, the shape will flagged as unKnown - /// \param[in] list - explicit TensorShape(const std::vector &list); - - /// \brief Copy constructor. - /// \param[in] shape TensorShape to copy from. - TensorShape(const TensorShape &shape); - - /// \brief Move constructor. - /// \param[in] shape TensorShape to copy from. - TensorShape(TensorShape &&shape) noexcept; - - /// \brief Copy assignment. - /// \param[in] shape TensorShape to move from. - TensorShape &operator=(const TensorShape &shape); - - /// \brief Move assignment. - /// \param[in] shape TensorShape to move from. - TensorShape &operator=(TensorShape &&shape) noexcept; - -#ifdef ENABLE_PYTHON - /// \brief Construct a TensorShape via a python list. - /// \param[in] l A py::list of the shape. - explicit TensorShape(py::list l); -#endif - - ~TensorShape() = default; - - /// \brief Create a scalar Shape (i.e., empty shape with mKnown = true) - /// \return TensorShape - static TensorShape CreateScalar() { - static std::vector empty_shape{}; - return TensorShape(empty_shape); - } - - /// \brief Create a shape with an unknown rank. - /// \return TensorShape - static TensorShape CreateUnknownRankShape(); - - /// \brief Create a shape with a known rank . - /// \return TensorShape - static TensorShape CreateUnknownShapeWithRank(dsize_t rank); - - /// \brief Insert a new dim into a copy of the current shape. - /// \param[in] dim to be added - /// \param[in] axis the index where dim should be added - /// \return New modified shape - TensorShape InsertDim(dsize_t axis, dsize_t dim) const; - - /// \brief Insert new dim at index 0. For example, <2,4> --> PrependDim(4) --> <4,2,4> - /// \param[in] dim - /// \return - TensorShape PrependDim(dsize_t dim) const; - - /// \brief Insert a new dim at the end of the shape. For example, <2,4> --> AppendDim(4) --> <2,4,4> - /// \param[in] dim - /// \return - TensorShape AppendDim(dsize_t dim) const; - -#ifndef ENABLE_ANDROID - /// \brief Create a shape based on OpenCV shape and type - /// \param[in] cv_size - /// \param[in] type int that represent the type in OpenCV, example CV_8U, CV_64S - TensorShape(cv::MatSize cv_size, uint32_t type); -#endif - - dsize_t Size() const { return raw_shape_.size(); } - - dsize_t Rank() const { return raw_shape_.size(); } - - bool known() const { return known_; } - - bool empty() const { return raw_shape_.empty(); } - - dsize_t NumOfElements() const; - - bool operator==(const TensorShape &rhs) const { return known_ == rhs.known_ && raw_shape_ == rhs.raw_shape_; } - - bool operator!=(const TensorShape &rhs) const { return !(rhs == *this); } - - dsize_t operator[](const dsize_t index) const { - if (index < 0) { - return raw_shape_[raw_shape_.size() + index]; - } - return raw_shape_[index]; - } - - /// \brief Return the Shape as a vector - /// \return - std::vector AsVector() const; - - /// \brief Returns the class info as a string - /// \return - std::string ToString() const { - std::stringstream ss; - ss << *this; - return ss.str(); - } - - /// \brief Actual print function used by operator<< - /// \param out output string stream - void Print(std::ostream &out) const; - - /// \brief << Stream output operator overload - /// This allows you to print the info using stream operators - /// \param[in] out - reference to the output stream being overloaded - /// \param[in] rO - reference to the TensorShape to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const TensorShape &so) { - so.Print(out); - return out; - } - -#ifdef ENABLE_PYTHON - py::list AsPyList(); -#endif - - /// \brief Checks if the given index is a valid index for this tensor. - /// For example: Tensor<3,4> Index<1,1> is valid. But Index<4,1> or <1> are not. - /// \param[in] index - /// \return bool - bool IsValidIndex(const std::vector &index) const; - - TensorShape Squeeze() const; - - std::vector Strides() const; - - /// \brief Returns the location of the item assuming row major memory layout. - /// \param[in] index - /// \param[out] flat_index - /// \return - Status ToFlatIndex(const std::vector &index, dsize_t *flat_index) const; - - private: - // Vector to keep the dims of the shape. - std::vector raw_shape_; - // Vector to keep the strides of the shape. The size is rank+1 - std::vector strides_; - // True if known and valid shape, false otherwise - bool known_; - - /// \brief Internal utility function to iterate over a list, - /// check if the dim is valid and then insert it into the shape. - /// \param[in] list Iterable list - /// \return true if the shape is valid and no overflow would be generated when counting the number of elements. - /// False otherwise. - template - void AddListToShape(const T &list); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CORE_TENSOR_SHAPE_H_ diff --git a/mindspore-lite/minddata/dataset/core/type_id.h b/mindspore-lite/minddata/dataset/core/type_id.h deleted file mode 100644 index a3f3c4ed1..000000000 --- a/mindspore-lite/minddata/dataset/core/type_id.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_TYPEID_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_TYPEID_H_ - -#include "ir/dtype/type_id.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" - -namespace mindspore { -namespace dataset { -inline dataset::DataType MSTypeToDEType(const TypeId data_type) { - switch (data_type) { - case kNumberTypeBool: - return dataset::DataType(dataset::DataType::DE_BOOL); - case kNumberTypeInt8: - return dataset::DataType(dataset::DataType::DE_INT8); - case kNumberTypeUInt8: - return dataset::DataType(dataset::DataType::DE_UINT8); - case kNumberTypeInt16: - return dataset::DataType(dataset::DataType::DE_INT16); - case kNumberTypeUInt16: - return dataset::DataType(dataset::DataType::DE_UINT16); - case kNumberTypeInt32: - return dataset::DataType(dataset::DataType::DE_INT32); - case kNumberTypeUInt32: - return dataset::DataType(dataset::DataType::DE_UINT32); - case kNumberTypeInt64: - return dataset::DataType(dataset::DataType::DE_INT64); - case kNumberTypeUInt64: - return dataset::DataType(dataset::DataType::DE_UINT64); - case kNumberTypeFloat16: - return dataset::DataType(dataset::DataType::DE_FLOAT16); - case kNumberTypeFloat32: - return dataset::DataType(dataset::DataType::DE_FLOAT32); - case kNumberTypeFloat64: - return dataset::DataType(dataset::DataType::DE_FLOAT64); - case kObjectTypeString: - return dataset::DataType(dataset::DataType::DE_STRING); - default: - return dataset::DataType(dataset::DataType::DE_UNKNOWN); - } -} - -inline TypeId DETypeToMSType(dataset::DataType data_type) { - switch (data_type.value()) { - case dataset::DataType::DE_BOOL: - return mindspore::TypeId::kNumberTypeBool; - case dataset::DataType::DE_INT8: - return mindspore::TypeId::kNumberTypeInt8; - case dataset::DataType::DE_UINT8: - return mindspore::TypeId::kNumberTypeUInt8; - case dataset::DataType::DE_INT16: - return mindspore::TypeId::kNumberTypeInt16; - case dataset::DataType::DE_UINT16: - return mindspore::TypeId::kNumberTypeUInt16; - case dataset::DataType::DE_INT32: - return mindspore::TypeId::kNumberTypeInt32; - case dataset::DataType::DE_UINT32: - return mindspore::TypeId::kNumberTypeUInt32; - case dataset::DataType::DE_INT64: - return mindspore::TypeId::kNumberTypeInt64; - case dataset::DataType::DE_UINT64: - return mindspore::TypeId::kNumberTypeUInt64; - case dataset::DataType::DE_FLOAT16: - return mindspore::TypeId::kNumberTypeFloat16; - case dataset::DataType::DE_FLOAT32: - return mindspore::TypeId::kNumberTypeFloat32; - case dataset::DataType::DE_FLOAT64: - return mindspore::TypeId::kNumberTypeFloat64; - case dataset::DataType::DE_STRING: - return mindspore::TypeId::kObjectTypeString; - default: - return kTypeUnknown; - } -} -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_TYPEID_H_ diff --git a/mindspore-lite/minddata/dataset/core/types.cc b/mindspore-lite/minddata/dataset/core/types.cc deleted file mode 100644 index 0e89820e1..000000000 --- a/mindspore-lite/minddata/dataset/core/types.cc +++ /dev/null @@ -1,526 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef BUILDING_DLL -#define BUILDING_DLL -#include "include/api/types.h" -#undef BUILDING_DLL -#endif - -#include -#include -#include "include/securec.h" -#include "src/common/api_tensor_impl.h" -#include "utils/convert_utils_base.h" -#include "utils/file_utils.h" - -namespace mindspore { -class Buffer::Impl { - public: - Impl() : data_() {} - ~Impl() = default; - Impl(const void *data, size_t data_len) { - if (data != nullptr) { - (void)SetData(data, data_len); - } else { - ResizeData(data_len); - } - } - - const void *Data() const { return data_.data(); } - void *MutableData() { return data_.data(); } - size_t DataSize() const { return data_.size(); } - - bool ResizeData(size_t data_len) { - data_.resize(data_len); - return true; - } - - bool SetData(const void *data, size_t data_len) { - ResizeData(data_len); - if (DataSize() != data_len) { - MS_LOG(ERROR) << "Set data failed, tensor current data size " << DataSize() << " not match data len " << data_len; - return false; - } - - if (data == nullptr) { - return data_len == 0; - } - - if (MutableData() == nullptr) { - MS_LOG(ERROR) << "Set data failed, data len " << data_len; - return false; - } - - auto ret = memcpy_s(MutableData(), DataSize(), data, data_len); - if (ret != EOK) { - MS_LOG(ERROR) << "Set data memcpy_s failed, ret = " << ret; - return false; - } - return true; - } - - protected: - std::vector data_; -}; - -class TensorDefaultImpl : public MSTensor::Impl { - public: - TensorDefaultImpl() : buffer_(), name_(), type_(DataType::kTypeUnknown), shape_() {} - ~TensorDefaultImpl() override = default; - TensorDefaultImpl(const std::string &name, enum DataType type, const std::vector &shape, const void *data, - size_t data_len) - : buffer_(data, data_len), name_(name), type_(type), shape_(shape) {} - - const std::string &Name() const override { return name_; } - enum DataType DataType() const override { return type_; } - const std::vector &Shape() const override { return shape_; } - void SetShape(const std::vector &shape) override { shape_ = shape; } - - std::shared_ptr Data() const override { - return std::shared_ptr(buffer_.Data(), [](const void *) {}); - } - - void *MutableData() override { return buffer_.MutableData(); } - size_t DataSize() const override { return buffer_.DataSize(); } - - bool IsDevice() const override { return false; } - - std::shared_ptr Clone() const override { - return std::make_shared(name_, type_, shape_, buffer_.Data(), buffer_.DataSize()); - } - - private: - Buffer buffer_; - std::string name_; - enum DataType type_; - std::vector shape_; -}; - -class TensorReferenceImpl : public MSTensor::Impl { - public: - TensorReferenceImpl() - : data_(nullptr), data_size_(0), name_(), type_(DataType::kTypeUnknown), shape_(), is_device_(false) {} - ~TensorReferenceImpl() override = default; - TensorReferenceImpl(const std::string &name, enum DataType type, const std::vector &shape, const void *data, - size_t data_len, bool is_device) - : data_(data), data_size_(data_len), name_(name), type_(type), shape_(shape), is_device_(is_device) {} - - const std::string &Name() const override { return name_; } - enum DataType DataType() const override { return type_; } - const std::vector &Shape() const override { return shape_; } - void SetShape(const std::vector &shape) override { shape_ = shape; } - - std::shared_ptr Data() const override { - return std::shared_ptr(data_, [](const void *) {}); - } - - void *MutableData() override { return const_cast(data_); } - size_t DataSize() const override { return data_size_; } - - bool IsDevice() const override { return is_device_; } - - std::shared_ptr Clone() const override { - return std::make_shared(name_, type_, shape_, data_, data_size_, is_device_); - } - - protected: - const void *data_; - size_t data_size_; - std::string name_; - enum DataType type_; - std::vector shape_; - bool is_device_; -}; - -MSTensor *MSTensor::CreateTensor(const std::vector &name, enum DataType type, const std::vector &shape, - const void *data, size_t data_len, const std::vector &device, - int device_id) noexcept { - std::string name_str = CharToString(name); - try { - std::shared_ptr impl = std::make_shared(name_str, type, shape, data, data_len); - MSTensor *ret = new MSTensor(impl); - return ret; - } catch (const std::bad_alloc &) { - MS_LOG(ERROR) << "Malloc memory failed."; - return nullptr; - } catch (...) { - MS_LOG(ERROR) << "Unknown error occurred."; - return nullptr; - } -} - -MSTensor *MSTensor::CreateTensor(const std::vector &name, const MSTensor &tensor, const std::vector &device, - int device_id) noexcept { - MS_LOG(ERROR) << "Invalid implement."; - return nullptr; -} - -MSTensor *MSTensor::CreateRefTensor(const std::vector &name, enum DataType type, - const std::vector &shape, const void *data, size_t data_len, - bool) noexcept { - std::string name_str = CharToString(name); - try { - std::shared_ptr impl = std::make_shared(name_str, type, shape, data, data_len, false); - MSTensor *ret = new MSTensor(impl); - return ret; - } catch (const std::bad_alloc &) { - MS_LOG(ERROR) << "Malloc memory failed."; - return nullptr; - } catch (...) { - MS_LOG(ERROR) << "Unknown error occurred."; - return nullptr; - } -} - -MSTensor MSTensor::CreateDeviceTensor(const std::vector &name, enum DataType type, - const std::vector &shape, void *data, size_t data_len) noexcept { - std::string name_str = CharToString(name); - try { - std::shared_ptr impl = std::make_shared(name_str, type, shape, data, data_len, true); - return MSTensor(impl); - } catch (const std::bad_alloc &) { - MS_LOG(ERROR) << "Malloc memory failed."; - return MSTensor(nullptr); - } catch (...) { - MS_LOG(ERROR) << "Unknown error occurred."; - return MSTensor(nullptr); - } -} - -MSTensor *MSTensor::CreateTensorFromFile(const std::vector &file, enum DataType type, - const std::vector &shape) noexcept { - std::string file_str = CharToString(file); - - try { - auto realpath = FileUtils::GetRealPath(file_str.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Get real path failed, path=" << file_str; - return nullptr; - } - - // Read image file - auto file_path = realpath.value(); - if (file_path.empty()) { - MS_LOG(ERROR) << "Can not find any input file."; - return nullptr; - } - - std::ifstream ifs(file_path, std::ios::in | std::ios::binary); - if (!ifs.good()) { - MS_LOG(ERROR) << "File: " + file_path + " does not exist."; - return nullptr; - } - if (!ifs.is_open()) { - MS_LOG(ERROR) << "File: " + file_path + " open failed."; - return nullptr; - } - - auto &io_seekg1 = ifs.seekg(0, std::ios::end); - if (!io_seekg1.good() || io_seekg1.fail() || io_seekg1.bad()) { - ifs.close(); - MS_LOG(ERROR) << "Failed to seekg file: " + file_path; - return nullptr; - } - - size_t size = static_cast(ifs.tellg()); - std::vector tensor_shape; - tensor_shape = shape.empty() ? std::vector{static_cast(size)} : shape; - MSTensor *ret = new MSTensor(file_path, type, tensor_shape, nullptr, size); - - auto &io_seekg2 = ifs.seekg(0, std::ios::beg); - if (!io_seekg2.good() || io_seekg2.fail() || io_seekg2.bad()) { - ifs.close(); - MS_LOG(ERROR) << "Failed to seekg file: " + file_path; - return nullptr; - } - - std::map TypeByte = { - {DataType::kTypeUnknown, 0}, {DataType::kObjectTypeString, 0}, {DataType::kNumberTypeBool, 1}, - {DataType::kNumberTypeInt8, 1}, {DataType::kNumberTypeInt16, 2}, {DataType::kNumberTypeInt32, 4}, - {DataType::kNumberTypeInt64, 8}, {DataType::kNumberTypeUInt8, 1}, {DataType::kNumberTypeUInt16, 2}, - {DataType::kNumberTypeUInt32, 4}, {DataType::kNumberTypeUInt64, 8}, {DataType::kNumberTypeFloat16, 2}, - {DataType::kNumberTypeFloat32, 4}, {DataType::kNumberTypeFloat64, 8}, {DataType::kNumberTypeBFloat16, 2}, - }; - - if (LongToSize(ret->ElementNum()) * TypeByte[type] != size) { - ifs.close(); - MS_LOG(ERROR) << "Tensor data size: " << LongToSize(ret->ElementNum()) * TypeByte[type] - << " not match input data length: " << size; - return nullptr; - } - - auto &io_read = ifs.read(static_cast(ret->MutableData()), static_cast(size)); - if (!io_read.good() || io_read.fail() || io_read.bad()) { - ifs.close(); - MS_LOG(ERROR) << "Failed to read file: " + file_path; - return nullptr; - } - ifs.close(); - - return ret; - } catch (const std::bad_alloc &) { - MS_LOG(ERROR) << "Malloc memory failed."; - return nullptr; - } catch (...) { - MS_LOG(ERROR) << "Unknown error occurred."; - return nullptr; - } -} - -MSTensor *MSTensor::CharStringsToTensor(const std::vector &name, const std::vector> &str) { - // num(4 bytes) + offset1(4 bytes) + offset2(4 bytes) + ... + data1(str1.len) + data2(str2.len) + ... - // str1.len() = offset2 - offset1 - // data1.begin() = start + offset1 - size_t mem_size = 0; - mem_size += sizeof(int32_t); // for num - for (const auto &s : str) { - mem_size += sizeof(int32_t); // for offset - mem_size += s.size(); // for data - } - - auto tensor = CreateTensor(name, DataType::kObjectTypeString, {static_cast(mem_size)}, nullptr, mem_size); - if (tensor == nullptr) { - MS_LOG(ERROR) << "Create tensor failed."; - return nullptr; - } - - int32_t *data = static_cast(tensor->MutableData()); - if (data == nullptr) { - MS_LOG(ERROR) << "Create tensor failed."; - DestroyTensorPtr(tensor); - return nullptr; - } - uint8_t *cur_data = reinterpret_cast(data + 1 + str.size()); - *static_cast(data) = SizeToInt(str.size()); - for (size_t i = 0; i < str.size(); ++i) { - int32_t offset = (cur_data - reinterpret_cast(data)); - data[i + 1] = offset; - if (str[i].empty()) { - continue; - } - auto ret = memcpy_s(static_cast(cur_data), str[i].size(), str[i].data(), str[i].size()); - if (ret != EOK) { - MS_LOG(ERROR) << "memcpy_s failed, ret = " << ret; - DestroyTensorPtr(tensor); - return nullptr; - } - cur_data += str[i].size(); - } - - return tensor; -} - -std::vector> MSTensor::TensorToStringChars(const MSTensor &tensor) { - constexpr auto minimum_tensor_size = 4; - if (tensor == nullptr || tensor.DataType() != DataType::kObjectTypeString || - tensor.DataSize() < minimum_tensor_size) { - MS_LOG(ERROR) << "Invalid tensor."; - return {}; - } - - std::vector> strings; - auto host_data = tensor.Data(); - const int32_t *data = static_cast(host_data.get()); - int32_t str_num = data[0]; - if (str_num == 0) { - return {}; - } - if (str_num < 0) { - MS_LOG(ERROR) << "str num " << str_num << " cannot be negative."; - return {}; - } - - if (tensor.DataSize() < (str_num + 1) * sizeof(int32_t)) { - MS_LOG(ERROR) << "Invalid tensor data size " << tensor.DataSize() << ", need " - << IntToSize(str_num + 1) * sizeof(int32_t) << " at least for " << str_num << " strings."; - return {}; - } - for (size_t i = 0; i < static_cast(str_num); ++i) { - strings.push_back({}); - auto &str = strings[i]; - int32_t str_len; - int32_t offset = data[i + 1]; - if (i + 1 != static_cast(str_num)) { - str_len = data[i + 1 + 1] - offset; - } else { - str_len = tensor.DataSize() - offset; - } - - if (str_len == 0) { - continue; - } - - if (str_len < 0) { - MS_LOG(ERROR) << "str " << i << " len " << str_len << " cannot be negative."; - return {}; - } - - str.resize(str_len); - const uint8_t *cur_data = reinterpret_cast(data) + offset; - auto ret = memcpy_s(static_cast(str.data()), str.size(), cur_data, static_cast(str_len)); - if (ret != EOK) { - MS_LOG(ERROR) << "memcpy_s failed, ret = " << ret; - return {}; - } - } - - return strings; -} - -void MSTensor::DestroyTensorPtr(MSTensor *tensor) noexcept { - if (tensor != nullptr) { - delete tensor; - } -} - -MSTensor::MSTensor() : impl_(std::make_shared()) {} -MSTensor::MSTensor(std::nullptr_t) : impl_(nullptr) {} -MSTensor::MSTensor(const std::shared_ptr &impl) : impl_(impl) { MS_EXCEPTION_IF_NULL(impl); } -MSTensor::MSTensor(const std::vector &name, enum DataType type, const std::vector &shape, - const void *data, size_t data_len) - : impl_(std::make_shared(CharToString(name), type, shape, data, data_len)) {} -MSTensor::~MSTensor() = default; - -bool MSTensor::operator==(std::nullptr_t) const { return impl_ == nullptr; } - -bool MSTensor::operator!=(std::nullptr_t) const { return impl_ != nullptr; } - -bool MSTensor::operator==(const MSTensor &tensor) const { return impl_ == tensor.impl_; } - -bool MSTensor::operator!=(const MSTensor &tensor) const { return impl_ != tensor.impl_; } - -MSTensor *MSTensor::Clone() const { - MS_EXCEPTION_IF_NULL(impl_); - try { - MSTensor *ret = new MSTensor(); - ret->impl_ = impl_->Clone(); - return ret; - } catch (const std::bad_alloc &) { - MS_LOG(ERROR) << "Malloc memory failed."; - return nullptr; - } catch (...) { - MS_LOG(ERROR) << "Unknown error occurred."; - return nullptr; - } -} - -std::vector MSTensor::CharName() const { - MS_EXCEPTION_IF_NULL(impl_); - return StringToChar(impl_->Name()); -} - -enum DataType MSTensor::DataType() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->DataType(); -} - -const std::vector &MSTensor::Shape() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->Shape(); -} - -int64_t MSTensor::ElementNum() const { - MS_EXCEPTION_IF_NULL(impl_); - const auto &shape = impl_->Shape(); - if (shape.empty()) { - // element number of scalar is 1 - return 1; - } - return std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); -} - -std::shared_ptr MSTensor::Data() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->Data(); -} - -void *MSTensor::MutableData() { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->MutableData(); -} - -size_t MSTensor::DataSize() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->DataSize(); -} - -bool MSTensor::IsDevice() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->IsDevice(); -} - -void MSTensor::SetShape(const std::vector &) { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetDataType(enum DataType) { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetTensorName(const std::vector &) { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetAllocator(std::shared_ptr) { MS_LOG_EXCEPTION << "Invalid implement."; } - -std::shared_ptr MSTensor::allocator() const { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetFormat(mindspore::Format) { MS_LOG_EXCEPTION << "Invalid implement."; } - -mindspore::Format MSTensor::format() const { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetData(void *, bool) { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetDeviceData(void *) { MS_LOG_EXCEPTION << "Invalid implement."; } - -void *MSTensor::GetDeviceData() { MS_LOG_EXCEPTION << "Invalid implement."; } - -std::vector MSTensor::QuantParams() const { MS_LOG_EXCEPTION << "Invalid implement."; } - -void MSTensor::SetQuantParams(std::vector) { MS_LOG_EXCEPTION << "Invalid implement."; } - -Buffer::Buffer() : impl_(std::make_shared()) {} -Buffer::Buffer(const void *data, size_t data_len) : impl_(std::make_shared(data, data_len)) {} -Buffer::~Buffer() = default; - -Buffer Buffer::Clone() const { - MS_EXCEPTION_IF_NULL(impl_); - Buffer ret; - ret.impl_ = std::make_shared(*impl_); - return ret; -} - -const void *Buffer::Data() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->Data(); -} - -void *Buffer::MutableData() { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->MutableData(); -} - -size_t Buffer::DataSize() const { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->DataSize(); -} - -bool Buffer::ResizeData(size_t data_len) { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->ResizeData(data_len); -} - -bool Buffer::SetData(const void *data, size_t data_len) { - MS_EXCEPTION_IF_NULL(impl_); - return impl_->SetData(data, data_len); -} - -std::vector CharVersion() { return {}; } -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/connector.h b/mindspore-lite/minddata/dataset/engine/connector.h deleted file mode 100644 index 1f45c4c14..000000000 --- a/mindspore-lite/minddata/dataset/engine/connector.h +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONNECTOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONNECTOR_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/cond_var.h" - -namespace mindspore { -namespace dataset { -// Connector is a communication data structure between two group of threads that -// preserve the order. -// -// Example use case: -// An initial tasks-list of [1,2,3,4,5,6,7,8,9] with 5 threads getting/processing elements from that list, -// and pushing the processed elements to a Connector in any order whoever finishes processing first. -// If the consumer of the Connector is single threaded, when the consumer pop() the -// element from the Connector one by one, it will get [1,2,3,4,5,6,7,8,9]. -// -// Requirements: -// 1. Each thread in the group of consumer or producer threads must be assigned ids starting from 0. -// 2. If your multi-threads program is not reading from a Connector class but -// want to push to a Connector class, you must follow roundrobin element distribution, -// i.e., the thread-id0 must have the first element, thread-id1 has the second element, -// and so on; then each of this worker can push to the Connector class async in parallel. -// -// Blocking conditions: -// 1. Connector.push(int, T) can block when the internal queue it's trying to push is full. -// 2. Connector.pop(int) can block when -// - The internal queue it's trying to pop is empty. -// - The caller thread of pop() is not equal to the _expectConsumer. This is to enforce -// the ordering. -// -// Future improvement: -// 1. Fault tolerant: Right now, if one of the worker dies, the Connector will not work -// properly. -template -class Connector { - public: - // Name: Constructor - // Description: Initializing private members with the given input arguments. - // expect_consumer_ and pop_from_ is initialized to 0 as part of - // our requirements. We instantiate nProducers number of internal - // queues so that each producer thread can push to its queue without - // any sync overhead. - // Constructor of Connector - // Initializing private members with the given input arguments. - // _expectConsumer and _popFrom is initialized to 0 as part of - // our requirements. We instantiate nProducers number of internal - // queues so that each producer thread can push to its queue without - // any sync overhead. - // @param n_producers The number of threads producing data into this DbConnector. - // @param n_consumers The number of thread consuming data from this DbConnector. - // @param queue_capacity The number of element for each queue. - Connector(int32_t n_producers, int32_t n_consumers, int32_t queue_capacity) - : num_producers_(n_producers), num_consumers_(n_consumers) { - MS_LOG(DEBUG) << "A connector is created with " << n_producers << " producers and " << n_consumers << " consumers."; - my_name_ = Services::GetUniqueID(); - // We require the consumers to have ids sequentially from 0 to the num_consumers_-1, - // Otherwise a ordered list of consumer ids have to be passed here. (not implemented yet) - expect_consumer_ = 0; - - // Roundrobin pop starts from index 0 of the queues_. - pop_from_ = 0; - - // Initialize the queues_ to have num_producers_ number of queues. - // Each queue is a blocking queue and has the same queue_capacity. - queues_.Init(num_producers_, queue_capacity); - } - - // Destructor of Connector - virtual ~Connector() = default; - - // Get an element from the Connector. - // @not Call to pop() can block the caller thread, see the blocking condition at the top of this file. - // @param worker_id The id of a worker thread calling this method. - // @param result The address of an object where the popped element will be placed. - virtual Status Pop(int32_t worker_id, // The worker-id of the caller. See the requirement at the top of this file. - T *result) noexcept { - { - MS_ASSERT(worker_id < num_consumers_); - std::unique_lock lk(m_); - RETURN_IF_NOT_OK(cv_.Wait(&lk, [this, worker_id]() { return expect_consumer_ == worker_id; })); - RETURN_IF_NOT_OK(queues_[pop_from_]->PopFront(result)); - pop_from_ = (pop_from_ + 1) % num_producers_; - out_buffers_count_++; - expect_consumer_ = (expect_consumer_ + 1) % num_consumers_; - } - - cv_.NotifyAll(); - return Status::OK(); - } - - // Add an element into the DbConnector without the overhead of synchronization. - // It may block when the internal queue is full. - // The element passed to this function will be copied into the internal queue. - // @param worker_id The id of a worker thread calling this method. - // @param el A const lvalue element to be passed/added/pushed. - Status Push(int32_t worker_id, const T &el) noexcept { - MS_ASSERT(worker_id < static_cast(queues_.size())); - MS_ASSERT(queues_[worker_id] != nullptr); - return (queues_[worker_id]->Add(el)); - } - - auto out_rows_count() const { return out_buffers_count_.load(); } - - // Add an element into the DbConnector without the overhead of synchronization. - // It may block when the internal queue is full. - // The element passed to this function will be forwarded into the internal queue. - // @param worker_id The id of a worker thread calling this method. - // @param el An element to be passed/added/pushed. - virtual Status Push(int32_t worker_id, T &&el) noexcept { - MS_ASSERT(worker_id < static_cast(queues_.size())); - MS_ASSERT(queues_[worker_id] != nullptr); - return (queues_[worker_id]->Add(std::forward(el))); - } - - // Resets the internal index tracking of the queue so that it can be used again with new inputs, - // starting from the beginning. - void Reset() { - for (size_t i = 0; i < queues_.size(); ++i) { - queues_[i]->Reset(); - } - expect_consumer_ = 0; - pop_from_ = 0; - out_buffers_count_ = 0; - MS_LOG(DEBUG) << "Connector counters reset."; - } - - void Print(std::ostream &out, bool showAll) const { - out << "\n--------- Connector ------------" - << "\nConnector Name : " << my_name_ << "\nNumber of consumers : " << num_consumers_ - << "\nNumber of producers : " << num_producers_ << "\n"; - } - - friend std::ostream &operator<<(std::ostream &out, const Connector &con) { - con.print(out, false); - return out; - } - - // Get current size of connector. - size_t size() const { - size_t size = 0; - for (size_t i = 0; i < queues_.size(); ++i) { - size += queues_[i]->size(); - } - return size; - } - - size_t capacity() const { - size_t capacity = 0; - for (size_t i = 0; i < queues_.size(); ++i) { - capacity += queues_[i]->capacity(); - } - return capacity; - } - - // Register the internal resources with Task group for interruption service. - // @param vg - // @return - Status Register(TaskGroup *vg) { - Status rc = queues_.Register(vg); - if (rc.IsOk()) { - rc = cv_.Register(vg->GetIntrpService()); - } - return rc; - } - - protected: - std::string my_name_; - - // A list of Queues that are thread safe. - QueueList queues_; - - // The consumer that we allow to get the next data from pop() - int32_t expect_consumer_; - - // The index to the queues_ where the next data should be popped. - size_t pop_from_; - - int32_t num_producers_; - int32_t num_consumers_; - - // Used in the Pop(), when a thread call pop() but it is not the expect_consumer_. - std::mutex m_; - CondVar cv_; - std::atomic out_buffers_count_ = 0; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONNECTOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.cc b/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.cc deleted file mode 100644 index 384cd459e..000000000 --- a/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.cc +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h" - -#include - -namespace mindspore::dataset { -Status PullBasedIteratorConsumer::Init(const std::shared_ptr &root) { - return tree_adapter_lite_->Compile(root, num_epochs_); -} - -std::vector PullBasedIteratorConsumer::GetRows(int64_t num_rows) { - std::vector rows; - for (int i = 0; i < num_rows; i++) { - TensorRow row; - RETURN_SECOND_IF_ERROR(tree_adapter_lite_->GetNextRow(&row), {}); - if (row.empty()) { - break; - } - rows.push_back(row); - } - - return rows; -} - -Status PullBasedIteratorConsumer::GetNextAsVector(std::vector *const out) { - RETURN_UNEXPECTED_IF_NULL(out); - out->clear(); - - TensorRow res; - RETURN_IF_NOT_OK(tree_adapter_lite_->GetNextRow(&res)); - - // Return empty vector if there's no data - RETURN_OK_IF_TRUE(res.empty()); - - (void)std::copy(res.begin(), res.end(), std::back_inserter(*out)); - return Status::OK(); -} - -Status PullBasedIteratorConsumer::GetNextAsMap(std::unordered_map *const out_map) { - RETURN_UNEXPECTED_IF_NULL(out_map); - out_map->clear(); - - TensorRow res; - RETURN_IF_NOT_OK(tree_adapter_lite_->GetNextRow(&res)); - - // Return empty map if there's no data - RETURN_OK_IF_TRUE(res.empty()); - - // Populate the out map from the row and return it - for (const auto &colMap : tree_adapter_lite_->GetColumnNameMap()) { - (*out_map)[colMap.first] = std::move(res[colMap.second]); - } - return Status::OK(); -} - -Status PullBasedIteratorConsumer::GetNextAsOrderedPair( - std::vector>> *const vec) { - CHECK_FAIL_RETURN_UNEXPECTED(vec != nullptr && vec->empty(), "vec is null or non-empty."); - - TensorRow curr_row; - - RETURN_IF_NOT_OK(tree_adapter_lite_->GetNextRow(&curr_row)); - RETURN_OK_IF_TRUE(curr_row.empty()); - size_t num_cols = curr_row.size(); // num_cols is non-empty. - // order the column names according to their ids - if (column_order_.empty()) { - const int32_t invalid_col_id = -1; - column_order_.resize(num_cols, {std::string(), invalid_col_id}); - for (const auto &itr : tree_adapter_lite_->GetColumnNameMap()) { - int32_t ind = itr.second; - CHECK_FAIL_RETURN_UNEXPECTED(ind < num_cols && ind >= 0, "column id out of bounds. Expecting in the range [0," + - std::to_string(num_cols) + "), but got " + - std::to_string(ind)); - column_order_[ind] = std::make_pair(itr.first, ind); - } - // error check, make sure the ids in col_name_id_map are continuous and starts from 0 - for (const auto &col : column_order_) { - if (col.second == invalid_col_id) { - std::string err_msg = "Invalid column id encountered."; - err_msg += " Note: It is unsupported and ambiguous to reuse the same column name for an output_column name"; - err_msg += " if it is an input_column name that will already appear as one of the output columns."; - err_msg += " Use unique columns names."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - } - vec->reserve(num_cols); - - std::transform(column_order_.begin(), column_order_.end(), std::back_inserter(*vec), - [curr_row](const auto &col) { return std::make_pair(col.first, curr_row[col.second]); }); - - return Status::OK(); -} - -TreeGetters::TreeGetters() - : root_(nullptr), - first_row_type_({}), - first_row_shape_({}), - estimated_row_shape_({}), - init_flag_(false), - first_row_obtained_(false) { - tree_adapter_lite_ = std::make_unique(TreeAdapterLite::UsageFlag::kDeGetter); -} - -Status TreeGetters::Init(const std::shared_ptr &root) { - root_ = root; - return Status::OK(); -} - -Status TreeGetters::GetRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - Status get_next_status = tree_adapter_lite_->GetNextRow(row); - return get_next_status; -} - -Status TreeGetters::GetOutputTypes(std::vector *types) { - RETURN_UNEXPECTED_IF_NULL(types); - RETURN_IF_NOT_OK(GetFirstRowShapeAndType()); - *types = first_row_type_; - return Status::OK(); -} - -Status TreeGetters::GetOutputShapes(std::vector *shapes, bool estimate) { - RETURN_UNEXPECTED_IF_NULL(shapes); - RETURN_IF_NOT_OK(GetFirstRowShapeAndType()); - *shapes = first_row_shape_; - - if (estimate) { - estimated_row_shape_ = first_row_shape_; - TensorRow row; - RETURN_IF_NOT_OK(GetRow(&row)); - - while (!row.empty()) { - std::vector cur_row_shape; - (void)std::transform(row.begin(), row.end(), std::back_inserter(cur_row_shape), - [=](auto &t) { return t->shape(); }); - - // calculate dynamic shape - CHECK_FAIL_RETURN_SYNTAX_ERROR(cur_row_shape.size() == estimated_row_shape_.size(), - "Inconsistent shapes, expect same shape for each data row, last data row: " + - std::to_string(cur_row_shape.size()) + - ", current data row: " + std::to_string(estimated_row_shape_.size())); - size_t shape_size = cur_row_shape.size(); - std::vector dynamic_shapes; - for (size_t i = 0; i < shape_size; i++) { - CHECK_FAIL_RETURN_SYNTAX_ERROR( - cur_row_shape[i].Size() == estimated_row_shape_[i].Size(), - "Inconsistent shapes, expect same shape for each data row, last data row: " + cur_row_shape[i].ToString() + - ", current data row: " + estimated_row_shape_[i].ToString()); - - std::vector vec; - for (size_t j = 0; j < estimated_row_shape_[i].Size(); j++) { - dsize_t dim = cur_row_shape[i][j] == estimated_row_shape_[i][j] ? cur_row_shape[i][j] : -1; - vec.push_back(dim); - } - dynamic_shapes.emplace_back(TensorShape(vec)); - } - estimated_row_shape_ = dynamic_shapes; - RETURN_IF_NOT_OK(GetRow(&row)); - } - - *shapes = estimated_row_shape_; - } - return Status::OK(); -} - -Status TreeGetters::GetBatchSize(int64_t *batch_size) { - RETURN_UNEXPECTED_IF_NULL(batch_size); - RETURN_IF_NOT_OK(InternalInit()); - std::shared_ptr root = std::shared_ptr(tree_adapter_lite_->GetRoot()); - RETURN_UNEXPECTED_IF_NULL(root); - *batch_size = root->GetTreeBatchSize(); - CHECK_FAIL_RETURN_UNEXPECTED(*batch_size != 0, "GetBatchSize: Failed to find the batch size in Dataset pipeline."); - return Status::OK(); -} - -Status TreeGetters::GetRepeatCount(int64_t *repeat_count) { - RETURN_UNEXPECTED_IF_NULL(repeat_count); - RETURN_IF_NOT_OK(InternalInit()); - std::shared_ptr root = std::shared_ptr(tree_adapter_lite_->GetRoot()); - RETURN_UNEXPECTED_IF_NULL(root); - *repeat_count = root->GetTreeRepeatCount(); - return Status::OK(); -} - -Status TreeGetters::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - RETURN_IF_NOT_OK(InternalInit()); - std::shared_ptr root = std::shared_ptr(tree_adapter_lite_->GetRoot()); - RETURN_UNEXPECTED_IF_NULL(root); - RETURN_IF_NOT_OK(root->GetNumClasses(num_classes)); - return Status::OK(); -} - -Status TreeGetters::GetColumnNames(std::vector *output) { - RETURN_UNEXPECTED_IF_NULL(output); - RETURN_IF_NOT_OK(InternalInit()); - std::shared_ptr root = std::shared_ptr(tree_adapter_lite_->GetRoot()); - RETURN_UNEXPECTED_IF_NULL(root); - std::unordered_map column_name_id_map = root->column_name_id_map(); - CHECK_FAIL_RETURN_UNEXPECTED(!column_name_id_map.empty(), "GetColumnNames: column_name_id map can not be empty."); - std::vector> col_name_id_vec(column_name_id_map.begin(), column_name_id_map.end()); - std::sort(col_name_id_vec.begin(), col_name_id_vec.end(), - [](const std::pair &a, const std::pair &b) { - return a.second < b.second; - }); - std::transform(col_name_id_vec.begin(), col_name_id_vec.end(), std::back_inserter(*output), - [](const std::pair &p) { return p.first; }); - return Status::OK(); -} - -Status TreeGetters::GetClassIndexing(std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - RETURN_IF_NOT_OK(InternalInit()); - std::shared_ptr root = std::shared_ptr(tree_adapter_lite_->GetRoot()); - RETURN_UNEXPECTED_IF_NULL(root); - RETURN_IF_NOT_OK(root->GetClassIndexing(output_class_indexing)); - return Status::OK(); -} - -Status TreeGetters::InternalInit() { - if (init_flag_) { - return Status::OK(); - } - - Status s = tree_adapter_lite_->Compile(root_, 1); - if (s.IsOk()) { - init_flag_ = true; - } - return s; -} - -Status TreeGetters::GetFirstRowShapeAndType() { - RETURN_OK_IF_TRUE(first_row_obtained_); - RETURN_IF_NOT_OK(InternalInit()); - TensorRow first_row; - RETURN_IF_NOT_OK(GetRow(&first_row)); - std::transform(first_row.begin(), first_row.end(), std::back_inserter(first_row_type_), - [](const TensorPtr &t) { return t->type(); }); - std::transform(first_row.begin(), first_row.end(), std::back_inserter(first_row_shape_), - [](const TensorPtr &t) { return t->shape(); }); - first_row_obtained_ = true; - return Status::OK(); -} -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h b/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h deleted file mode 100644 index c5304766b..000000000 --- a/mindspore-lite/minddata/dataset/engine/consumers/pull_based_tree_consumer.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_PULL_BASED_TREE_CONSUMER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_PULL_BASED_TREE_CONSUMER_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h" - -namespace mindspore::dataset { -class TreeAdapterLite; -class TensorRow; - -/// Consumer that iterates over the dataset and returns the rows one by one as a in a pull based fashion -class PullBasedIteratorConsumer : public TreeConsumer { - public: - /// Constructor - /// \param num_epochs number of epochs. Default: 1. - explicit PullBasedIteratorConsumer(int32_t num_epochs = 1) - : TreeConsumer(num_epochs), tree_adapter_lite_(std::make_unique()) {} - - ~PullBasedIteratorConsumer() override = default; - - Status Init(const std::shared_ptr &root) override; - - /// \brief Returns the next row in a vector format - /// \note This is currently a placeholder function - /// \param[in] num_rows the number of rows that we want to get - /// \return out std::vector of TensorRows - std::vector GetRows(int64_t num_rows); - - /// Returns the next row in a vector format - /// \param[out] out std::vector of Tensors - /// \return Status error code - Status GetNextAsVector(std::vector *const out) override; - - /// Returns the next row in as a map - /// \param[out] out std::map of string to Tensor - /// \return Status error code - Status GetNextAsMap(std::unordered_map *const out) override; - - /// Returns the next row in as a vector - /// \param[out] vec std::vector of pairs of string to Tensor - /// \return Status error code - Status GetNextAsOrderedPair(std::vector>> *const vec) override; - - /// Function to reset the current consumer to the provided step. - /// \note Reset is NOT supported for pull-based iterators. - /// \param step the step to reset the pipeline to. - /// \param dataset_size the number of steps that one epoch has. - /// \return Status error code - Status Reset(int64_t step, int64_t dataset_size) override { - RETURN_STATUS_UNEXPECTED( - "Failover reset is not supported for pull-based iterators (including when Debug mode is enabled)."); - } - - protected: - /// Method to return the name of the consumer - /// \return string - std::string Name() override { return "PullBasedIteratorConsumer"; } - std::unique_ptr tree_adapter_lite_; - - private: - std::vector> column_order_; // key: column name, val: column id -}; - -/// Consumer that is used to get some pipeline information -class TreeGetters : public PullBasedIteratorConsumer { - public: - TreeGetters(); - - ~TreeGetters() override = default; - - Status Init(const std::shared_ptr &root) override; - - Status GetOutputTypes(std::vector *types); - - Status GetOutputShapes(std::vector *shapes, bool estimate = false); - - Status GetBatchSize(int64_t *batch_size); - - Status GetRepeatCount(int64_t *repeat_count); - - Status GetNumClasses(int64_t *num_classes); - - Status GetColumnNames(std::vector *output); - - Status GetClassIndexing(std::vector>> *output_class_indexing); - - std::string Name() override { return "TreeGetters"; } - - virtual Status GetRow(TensorRow *row); - - private: - Status GetFirstRowShapeAndType(); - - std::shared_ptr root_; - std::vector first_row_type_; - std::vector first_row_shape_; - std::vector estimated_row_shape_; - bool first_row_obtained_; // whether first row (which could be empty) is obtained by TreeGetter - bool init_flag_; // indicate whether the tree has initialized - - Status InternalInit(); -}; -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_PULL_BASED_TREE_CONSUMER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.cc b/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.cc deleted file mode 100644 index 66d7d532d..000000000 --- a/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.cc +++ /dev/null @@ -1,951 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h" -#include "mindspore-lite/minddata/dataset/engine/perf/auto_tune.h" -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "minddata/mindrecord/include/shard_header.h" -#include "minddata/mindrecord/include/shard_index_generator.h" -#include "minddata/mindrecord/include/shard_writer.h" -#endif -#ifdef WITH_BACKEND -#include "utils/ms_context.h" -#endif - -namespace mindspore { -namespace dataset { -using ProfilingRegistrationState = ProfilingManager::ProfilingRegistrationState; -// TreeConsumer -TreeConsumer::TreeConsumer() : TreeConsumer(1) {} - -TreeConsumer::TreeConsumer(int32_t num_epochs) : num_epochs_(num_epochs) { - tree_adapter_ = std::make_unique(); -} - -Status TreeConsumer::Init(const std::shared_ptr &root) { - RETURN_IF_NOT_OK(tree_adapter_->Compile(root)); - profiling_manager_ = GlobalContext::profiling_manager(); - RETURN_IF_NOT_OK(RegisterProfilingManager()); - return Status::OK(); -} - -Status TreeConsumer::Init(const std::shared_ptr &root, int64_t global_step, int64_t dataset_size) { - MS_LOG(WARNING) << "TreeConsumer does not support initializing from intermediate epoch or step, change to " - "initialize from the beginning."; - return Init(root); -} - -Status TreeConsumer::Terminate() { - if (tree_adapter_->AllTasks() != nullptr) { - return tree_adapter_->AllTasks()->ServiceStop(); - } - return Status::OK(); -} - -Status IteratorConsumer::RegisterProfilingManager() { - ExecutionTree *tree = nullptr; -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (tree_adapter_->tree_) { -#endif - tree = tree_adapter_->tree_.get(); -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - } else { - tree = tree_adapter_->receive_tree_.get(); - } -#endif - auto profiler_state = profiling_manager_->GetProfilerTreeState(tree); - // This should never happen - CHECK_FAIL_RETURN_UNEXPECTED(profiler_state != ProfilingManager::kEnabledTreeRegistered, - "Something went wrong. Current tree is already registered with the MD Profiler"); - if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && profiling_manager_->IsProfiling()) { - MS_LOG(WARNING) << "Dataset Profiling is already enabled for a different data pipeline."; - } else if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && - profiling_manager_->IsAutotuning()) { - MS_LOG(WARNING) << "AutoTune for dataset is already enabled for a different data pipeline."; - } else if (profiler_state == ProfilingRegistrationState::kEnabledTreeNotRegistered) { - // Profiling infrastructures need to be initialized before Op launching - // Setup profiling manager - RETURN_IF_NOT_OK(profiling_manager_->RegisterTree(tree)); - // dataset_iterator node is used for graph mode - std::shared_ptr iterator_tracing = std::make_shared(); - RETURN_IF_NOT_OK(profiling_manager_->RegisterTracingNode(iterator_tracing)); - RETURN_IF_NOT_OK(tree_adapter_->SetProfilingManagerPtr(profiling_manager_, iterator_tracing)); - // Launch Monitor Thread - RETURN_IF_NOT_OK(profiling_manager_->LaunchMonitor()); - } else { - MS_LOG(INFO) << "Unable to register this tree with ProfilingManager."; - } - return Status::OK(); -} - -Status ToDevice::RegisterProfilingManager() { - ExecutionTree *tree = nullptr; -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (tree_adapter_->tree_) { -#endif - tree = tree_adapter_->tree_.get(); -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - } else { - tree = tree_adapter_->receive_tree_.get(); - } -#endif - auto profiler_state = profiling_manager_->GetProfilerTreeState(tree); - // This should never happen - CHECK_FAIL_RETURN_UNEXPECTED(profiler_state != ProfilingManager::kEnabledTreeRegistered, - "Something went wrong. Current tree is already registered with the MD Profiler"); - if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && profiling_manager_->IsProfiling()) { - MS_LOG(WARNING) << "Dataset Profiling is already enabled for a different data pipeline."; - } else if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && - profiling_manager_->IsAutotuning()) { - MS_LOG(WARNING) << "AutoTune for dataset is already enabled for a different data pipeline."; - } else if (profiler_state == ProfilingRegistrationState::kEnabledTreeNotRegistered) { - // Profiling infrastructures need to be initialized before Op launching - // Setup profiling manager - RETURN_IF_NOT_OK(profiling_manager_->RegisterTree(tree)); - // device_queue node is used for graph mode - std::shared_ptr device_queue_tracing = std::make_shared(); - RETURN_IF_NOT_OK(profiling_manager_->RegisterTracingNode(device_queue_tracing)); - RETURN_IF_NOT_OK(tree_adapter_->SetProfilingManagerPtr(profiling_manager_)); - // Launch Monitor Thread - RETURN_IF_NOT_OK(profiling_manager_->LaunchMonitor()); - } else { - MS_LOG(INFO) << "Unable to register this tree with ProfilingManager."; - } - return Status::OK(); -} - -Status TreeConsumer::RegisterProfilingManager() { - if (profiling_manager_->IsProfiling()) { - return {StatusCode::kMDUnexpectedError, "Dataset Profiling is not supported for this kind of dataset."}; - } - return Status::OK(); -} - -Status TreeConsumer::InitAutoTune() { - auto profiler_state = profiling_manager_->GetProfilerTreeState(tree_adapter_->tree_.get()); - if (profiler_state == ProfilingRegistrationState::kNotEnabled) { - // Init ProfilingManager to `Enable` it. - RETURN_IF_NOT_OK(profiling_manager_->Init(true)); - // Register this tree - RETURN_IF_NOT_OK(RegisterProfilingManager()); - // Start Profiler - RETURN_IF_NOT_OK(profiling_manager_->Start()); - // AutoTune object and thread init - autotune_ = std::make_unique(this->tree_adapter_.get(), GetProfilingManager()); - RETURN_IF_NOT_OK(autotune_->LaunchThread()); - } else if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && profiling_manager_->IsProfiling()) { - MS_LOG(WARNING) << "Cannot enable AutoTune for the current data pipeline as Dataset Profiling is enabled for " - "another data pipeline."; - } else if (profiler_state == ProfilingManager::kEnabledDifferentTreeRegistered && - profiling_manager_->IsAutotuning()) { - MS_LOG(WARNING) - << "Cannot enable AutoTune for the current data pipeline as it is already enabled for another data pipeline."; - } else if (profiler_state == ProfilingManager::kEnabledTreeRegistered && profiling_manager_->IsProfiling()) { - MS_LOG(WARNING) - << "Cannot enable AutoTune for the current data pipeline as Dataset Profiling is already enabled for the " - "current data pipeline."; - } else { - MS_LOG(WARNING) << "Cannot enable AutoTune for the current data pipeline."; - } - return Status::OK(); -} - -std::string TreeConsumer::GetOffload() { return (tree_adapter_->GetOffloadJson()).dump(); } - -// IteratorConsumer -Status IteratorConsumer::Init(const std::shared_ptr &root, int64_t global_step, int64_t dataset_size) { - if (global_step != 0) { - tree_adapter_ = std::make_unique(TreeAdapter::UsageFlag::kDeReset); - } - RETURN_IF_NOT_OK(tree_adapter_->Compile(root, num_epochs_, global_step, dataset_size, false)); - profiling_manager_ = GlobalContext::profiling_manager(); - if (profiling_manager_->IsProfiling()) { - // Init has been called already - RETURN_IF_NOT_OK(RegisterProfilingManager()); - } - if (GlobalContext::config_manager()->enable_autotune()) { - RETURN_IF_NOT_OK(InitAutoTune()); - } - return Status::OK(); -} - -Status IteratorConsumer::GetNextAsVector(std::vector *const out) { - RETURN_UNEXPECTED_IF_NULL(out); - uint64_t start_time = GetSyscnt(); - out->clear(); - TensorRow res; - RETURN_IF_NOT_OK(tree_adapter_->GetNext(&res)); - - // Return empty vector if there's no data - if (res.empty()) { - RETURN_IF_NOT_OK( - CollectPipelineInfo("IteratorConsumer", "GetNextAsVector", start_time, {{"TensorRowFlags", res.FlagName()}})); - return Status::OK(); - } - - if (res.Timer()->Enabled()) { - // VL_MD is 10900 - VLOG_MD(res.Timer()->Summary()); - } - - // Filter meta column - std::vector to_keep_indices; - for (const auto &colMap : tree_adapter_->GetColumnNameMap()) { - std::string column_name = colMap.first; - // Need to filter meta column start with kDftMetaColumnPrefix - size_t pos = column_name.find(kDftMetaColumnPrefix); - if (pos != std::string::npos && pos == 0) { - continue; - } - to_keep_indices.push_back(colMap.second); - } - if (to_keep_indices.empty()) { - std::string err_msg = "No effective column found, maybe all columns are meta column and will be filtered. "; - err_msg += "If you want to output meta column please rename column name to a new one which is not start with "; - err_msg += "\"" + std::string(kDftMetaColumnPrefix) + "\""; - RETURN_STATUS_UNEXPECTED(err_msg); - } - std::sort(to_keep_indices.begin(), to_keep_indices.end()); - (void)std::transform(to_keep_indices.begin(), to_keep_indices.end(), std::back_inserter(*out), - [&res](const auto &it) { return std::move(res[it]); }); - RETURN_IF_NOT_OK(CollectPipelineInfo("IteratorConsumer", "GetNextAsVector", start_time)); - return Status::OK(); -} - -Status IteratorConsumer::GetNextAsMap(std::unordered_map *const out_map) { - RETURN_UNEXPECTED_IF_NULL(out_map); - uint64_t start_time = GetSyscnt(); - - out_map->clear(); - TensorRow res; - RETURN_IF_NOT_OK(tree_adapter_->GetNext(&res)); - - // Return empty map if there's no data - if (res.empty()) { - RETURN_IF_NOT_OK( - CollectPipelineInfo("IteratorConsumer", "GetNextAsMap", start_time, {{"TensorRowFlags", res.FlagName()}})); - return Status::OK(); - } - - if (res.Timer()->Enabled()) { - // VL_MD is 10900 - VLOG_MD(res.Timer()->Summary()); - } - - // Populate the out map from the row and return it - for (const auto &colMap : tree_adapter_->GetColumnNameMap()) { - std::string column_name = colMap.first; - // Need to filter meta column start with kDftMetaColumnPrefix - size_t pos = column_name.find(kDftMetaColumnPrefix); - if (pos != std::string::npos && pos == 0) { - continue; - } - (*out_map)[colMap.first] = std::move(res[colMap.second]); - } - if (out_map->empty()) { - std::string err_msg = "No effective column found, maybe all columns are meta column and will be filtered. "; - err_msg += "If you want to output meta column please rename column name to a new one which is not start with "; - err_msg += "\"" + std::string(kDftMetaColumnPrefix) + "\""; - RETURN_STATUS_UNEXPECTED(err_msg); - } - RETURN_IF_NOT_OK(CollectPipelineInfo("IteratorConsumer", "GetNextAsMap", start_time)); - return Status::OK(); -} - -Status IteratorConsumer::GetNextAsOrderedPair(std::vector>> *const vec) { - CHECK_FAIL_RETURN_UNEXPECTED(vec != nullptr && vec->empty(), "vec is null or non-empty."); - uint64_t start_time = GetSyscnt(); - - TensorRow curr_row; - - RETURN_IF_NOT_OK(tree_adapter_->GetNext(&curr_row)); - - // Return empty pair if there's no data - if (curr_row.empty()) { - RETURN_IF_NOT_OK(CollectPipelineInfo("IteratorConsumer", "GetNextAsOrderedPair", start_time, - {{"TensorRowFlags", curr_row.FlagName()}})); - return Status::OK(); - } - - size_t num_cols = curr_row.size(); // num_cols is non-empty. - // order the column names according to their ids - if (column_order_.empty()) { - for (const auto &itr : tree_adapter_->GetColumnNameMap()) { - int32_t ind = itr.second; - CHECK_FAIL_RETURN_UNEXPECTED(ind < num_cols && ind >= 0, "column id out of bounds."); - // Need to filter meta column start with kDftMetaColumnPrefix - size_t pos = itr.first.find(kDftMetaColumnPrefix); - if (pos != std::string::npos && pos == 0) { - continue; - } - column_order_[ind] = itr.first; - } - } - - if (column_order_.empty()) { - std::string err_msg = "No effective column found, maybe all columns are meta column and will be filtered. "; - err_msg += "If you want to output meta column please rename column name to a new one which is not start with "; - err_msg += "\"" + std::string(kDftMetaColumnPrefix) + "\""; - RETURN_STATUS_UNEXPECTED(err_msg); - } - vec->reserve(column_order_.size()); - - std::transform(column_order_.begin(), column_order_.end(), std::back_inserter(*vec), - [curr_row](const auto &col) { return std::make_pair(col.second, curr_row[col.first]); }); - RETURN_IF_NOT_OK(CollectPipelineInfo("IteratorConsumer", "GetNextAsOrderedPair", start_time)); - return Status::OK(); -} - -// ToDevice -Status ToDevice::Init(const std::shared_ptr &root, int64_t global_step, int64_t dataset_size) { - if (global_step != 0) { - tree_adapter_ = std::make_unique(TreeAdapter::UsageFlag::kDeReset); - } - RETURN_IF_NOT_OK(tree_adapter_->Compile(root, num_epochs_, global_step, dataset_size, true)); - profiling_manager_ = GlobalContext::profiling_manager(); - if (profiling_manager_->IsProfiling()) { - // Init has been called already - RETURN_IF_NOT_OK(RegisterProfilingManager()); - } - if (GlobalContext::config_manager()->enable_autotune()) { - RETURN_IF_NOT_OK(InitAutoTune()); - } - return Status::OK(); -} - -Status ToDevice::Send() { - RETURN_IF_NOT_OK(tree_adapter_->Launch()); - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - return Status::OK(); -} - -Status ToDevice::Continue() { - // tree_.root() must be DataQueueOp - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "ContinueSend only supported by DataQueueOp"); - op->ContinueSend(); - return Status::OK(); -} - -Status ToDevice::Stop() { - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "StopSend only supported by DataQueueOp"); - op->StopSend(); - - return Status::OK(); -} - -Status ToDevice::GetDataInfo(std::vector *const types, std::vector *const shapes) { - RETURN_UNEXPECTED_IF_NULL(types); - RETURN_UNEXPECTED_IF_NULL(shapes); - // tree_.root() must be DataQueueOp - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "GetDataInfo only supported by DataQueueOp"); - DATA_INFO data_info; - RETURN_IF_NOT_OK(op->GetDataInfo(&data_info)); - for (auto el : data_info) { - types->push_back(el.first); - shapes->push_back(el.second); - } - return Status::OK(); -} - -Status ToDevice::GetMbufQueueSize(size_t *queue_size) { - RETURN_UNEXPECTED_IF_NULL(queue_size); - // tree_.root() must be DataQueueOp - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "GetMbufQueueSize only supported by DataQueueOp"); - RETURN_IF_NOT_OK(op->GetMbufQueueSize(queue_size)); - return Status::OK(); -} - -Status ToDevice::GetSendInfo(std::vector> *send_info) { - RETURN_UNEXPECTED_IF_NULL(send_info); - // tree_.root() must be DataQueueOp - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "GetSendInfo only supported by DataQueueOp"); - DATA_INFO data_info; - *send_info = op->GetSendInfo(); - return Status::OK(); -} - -Status ToDevice::Terminate() { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(MsContext::GetInstance()); - if (MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET) == kAscendDevice) { - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "StopSend only supported by DataQueueOp"); - op->StopWaiting(); - } -#endif - return TreeConsumer::Terminate(); -} - -Status TreeConsumer::Reset(int64_t step, const int64_t dataset_size) { - MS_LOG(INFO) << "Resetting TreeConsumer"; - - std::string unique_id = ""; -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (tree_adapter_->tree_) { -#endif - unique_id = tree_adapter_->tree_->GetUniqueId(); -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - } else { - unique_id = tree_adapter_->receive_tree_->GetUniqueId(); - } -#endif - MS_LOG(INFO) << "Terminating pipeline with UUID:" << unique_id; - std::shared_ptr old_root = tree_adapter_->input_ir_; - RETURN_IF_NOT_OK(this->Stop()); - RETURN_IF_NOT_OK(this->Terminate()); -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(MsContext::GetInstance()); - if (MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET) == kGPUDevice) { - // clear the device if GPU is used. - std::shared_ptr root = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root != nullptr, "Root is a nullptr."); - DataQueueOp *op = dynamic_cast(root.get()); - if (op != nullptr) { - MS_LOG(INFO) << "Clearing the GPU device"; - RETURN_IF_NOT_OK(op->ClearDevice()); - } - } -#endif - tree_adapter_ = std::make_unique(TreeAdapter::UsageFlag::kDeReset); - RETURN_IF_NOT_OK(tree_adapter_->Compile(old_root, num_epochs_, step, dataset_size, true)); - RETURN_IF_NOT_OK(tree_adapter_->Launch()); - MS_LOG(INFO) << "Launched a new pipeline after reset. UUID: " << tree_adapter_->tree_->GetUniqueId(); - std::shared_ptr root2 = std::shared_ptr(tree_adapter_->GetRoot()); - CHECK_FAIL_RETURN_UNEXPECTED(root2 != nullptr, "Root is a nullptr."); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// SaveToDisk -Status SaveToDisk::ValidateParams() { - if (dataset_path_.empty()) { - std::string err = "SaveToDisk failed, dataset_path must not be empty"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err); - } - Path dir(dataset_path_); - if (dir.IsDirectory()) { - std::string err = "SaveToDisk failed, dataset_path must not be a directory"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err); - } - std::string real_path; - if (dir.ParentPath().empty()) { - dir = Path(".") / dir; - } - if (Path::RealPath(dir.ParentPath(), real_path).IsError()) { - std::string err_msg = "SaveToDisk failed, can not get real dataset path: " + dir.ParentPath(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (access(dir.ParentPath().c_str(), R_OK) == -1) { - std::string err_msg = "SaveToDisk failed, no access to specified dataset path: " + dataset_path_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (num_files_ <= 0 || num_files_ > 1000) { - std::string err = "SaveToDisk failed, num_files must between 1 and 1000, but got " + std::to_string(num_files_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err); - } - if (dataset_type_ != "mindrecord") { - std::string err = "SaveToDisk failed, only \"mindrecord\" dataset format is supported, but got " + dataset_type_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err); - } - return Status::OK(); -} - -Status SaveToDisk::Save() { - uint64_t start_time = GetSyscnt(); - std::vector file_names; - if (num_files_ == 1) { - file_names.push_back(dataset_path_); - } else { - for (int32_t i = 0; i < num_files_; i++) { - file_names.push_back(dataset_path_ + std::to_string(i)); - } - } - - auto mr_header = std::make_shared(); - auto mr_writer = std::make_unique(); - std::vector blob_fields; - RETURN_IF_NOT_OK(mindrecord::ShardWriter::Initialize(&mr_writer, file_names)); - - std::unordered_map column_name_id_map; - for (auto el : tree_adapter_->GetColumnNameMap()) { - std::string column_name = el.first; - (void)std::transform(column_name.begin(), column_name.end(), column_name.begin(), - [](unsigned char c) { return ispunct(c) ? '_' : c; }); - column_name_id_map[column_name] = el.second; - } - - TensorRow row; - uint64_t mr_schema_id = 0; - bool first_loop = true; // build schema in first loop - auto PreTensorRowShapes = std::map>(); - - do { - nlohmann::json row_raw_data; - std::map>> row_bin_data; - RETURN_IF_NOT_OK(tree_adapter_->GetNext(&row)); - if (row.empty()) { - break; - } - RETURN_IF_NOT_OK(CheckTensorRowShapes(column_name_id_map, row, &PreTensorRowShapes)); - if (first_loop) { - nlohmann::json mr_json; - std::vector index_fields; - RETURN_IF_NOT_OK(FetchMetaFromTensorRow(column_name_id_map, row, &mr_json, &index_fields)); - MS_LOG(INFO) << "Schema of saved mindrecord: " << mr_json.dump(); - RETURN_IF_NOT_OK( - mindrecord::ShardHeader::Initialize(&mr_header, mr_json, index_fields, blob_fields, mr_schema_id)); - RETURN_IF_NOT_OK(mr_writer->SetShardHeader(mr_header)); - first_loop = false; - } - // construct data - if (!row.empty()) { // write data - RETURN_IF_NOT_OK(FetchDataFromTensorRow(row, column_name_id_map, &row_raw_data, &row_bin_data)); - std::shared_ptr> output_bin_data; - RETURN_IF_NOT_OK(mr_writer->MergeBlobData(blob_fields, row_bin_data, &output_bin_data)); - std::map> raw_data; - raw_data.insert( - std::pair>(mr_schema_id, std::vector{row_raw_data})); - std::vector> bin_data; - if (output_bin_data != nullptr) { - bin_data.emplace_back(*output_bin_data); - } - RETURN_IF_NOT_OK(mr_writer->WriteRawData(raw_data, bin_data)); - } - } while (!row.empty()); - - RETURN_IF_NOT_OK(mr_writer->Commit()); - RETURN_IF_NOT_OK(mindrecord::ShardIndexGenerator::Finalize(file_names)); - RETURN_IF_NOT_OK(CollectPipelineInfo("SaveToDisk", "Save", start_time)); - return Status::OK(); -} - -template -bool SaveToDisk::map_compare(T const &lhs, T const &rhs) { - return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); -} - -Status SaveToDisk::CheckTensorRowShapes(const std::unordered_map &column_name_id_map, - const TensorRow &row, - std::map> *PreTensorRowShapes_ptr) { - std::map> CurrTensorRowShapes; - for (auto &col : column_name_id_map) { - auto idx = col.second; - auto column_name = col.first; - auto &tensor = row[idx]; - auto column_type = tensor->type(); - auto column_shape = tensor->shape(); - - auto shapes = column_shape.AsVector(); - std::vector mr_shape(shapes.begin(), shapes.end()); - - if (mr_shape.empty() || mr_shape.size() == 1) { - continue; // ignore scalar and one dimension tensor - } - std::string mr_type; - std::string el = column_type.ToString(); - if (mindrecord::kTypesMap.find(el) == mindrecord::kTypesMap.end()) { - std::string err_msg("Invalid type, unsupported data type: " + el); - RETURN_STATUS_UNEXPECTED(err_msg); - } else { - mr_type = mindrecord::kTypesMap.at(el); - } - if (mr_type == "bytes" || mr_type == "string") { - continue; - } - mr_shape.erase(mr_shape.begin()); // ignore the first dimension - CurrTensorRowShapes[column_name] = mr_shape; - } - if (PreTensorRowShapes_ptr->empty()) { - *PreTensorRowShapes_ptr = CurrTensorRowShapes; - return Status::OK(); - } - auto res = map_compare(*PreTensorRowShapes_ptr, CurrTensorRowShapes); - CHECK_FAIL_RETURN_UNEXPECTED(res, - "Tensor with dynamic shape do not currently support saving. Except for the shape of " - "dimension 0, the other dimension shapes must be fixed. " - "You can reshape the Tensor to a fixed shape before saving."); - return Status::OK(); -} - -Status SaveToDisk::FetchMetaFromTensorRow(const std::unordered_map &column_name_id_map, - const TensorRow &row, nlohmann::json *schema, - std::vector *index_fields) { - if (schema == nullptr) { - RETURN_STATUS_UNEXPECTED("schema can not be nullptr."); - } - if (index_fields == nullptr) { - RETURN_STATUS_UNEXPECTED("index_fields can not be nullptr."); - } - if (column_name_id_map.empty()) { - RETURN_STATUS_UNEXPECTED("column_name_id_map can not be nullptr.."); - } - nlohmann::json dataset_schema; - for (auto &col : column_name_id_map) { - auto idx = col.second; - auto column_name = col.first; - auto &tensor = row[idx]; - auto column_type = tensor->type(); - auto column_shape = tensor->shape(); - - std::string mr_type; - auto shapes = column_shape.AsVector(); - std::vector mr_shape(shapes.begin(), shapes.end()); - std::string el = column_type.ToString(); - dataset_schema[column_name] = el; - if (mindrecord::kTypesMap.find(el) == mindrecord::kTypesMap.end()) { - std::string err_msg("Invalid type, unsupported data type: " + el); - RETURN_STATUS_UNEXPECTED(err_msg); - } else { - mr_type = mindrecord::kTypesMap.at(el); - } - if (mr_shape.empty()) { - (*schema)[column_name] = {{"type", mr_type}}; - } else { - if (mr_type == "string") { // mindrecord can not support string with shape. - std::string err_msg("Invalid data, mindrecord can not support multi-dimensional string tensor."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (mr_type == "bytes") { // ignore shape of bytes in minrecord - (*schema)[column_name] = {{"type", mr_type}}; - } else { - mr_shape[0] = -1; // make first dimension -1 - (*schema)[column_name] = {{"type", mr_type}, {"shape", mr_shape}}; - } - } - if (mr_type == "bytes" || !mr_shape.empty()) { - continue; - } - index_fields->emplace_back(column_name); // candidate of index fields - } - MS_LOG(DEBUG) << "Schema of dataset: " << dataset_schema.dump(); - return Status::OK(); -} - -inline Status ValidateInputParams(nlohmann::json *row_raw_data, - std::map>> *row_bin_data, - const std::unordered_map &column_name_id_map) { - if (row_raw_data == nullptr) { - RETURN_STATUS_UNEXPECTED("row_raw_data can not be nullptr."); - } - if (row_bin_data == nullptr) { - RETURN_STATUS_UNEXPECTED("row_bin_data can not be nullptr."); - } - if (column_name_id_map.empty()) { - RETURN_STATUS_UNEXPECTED("column_name_id_map can not be nullptr."); - } - return Status::OK(); -} - -Status SaveToDisk::FetchIntData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::unique_ptr> *data_ptr) { - RETURN_UNEXPECTED_IF_NULL(row_raw_data); - RETURN_UNEXPECTED_IF_NULL(data_ptr); - auto column_type = tensor->type(); - Status s; - if (column_type == DataType::DE_INT8) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_UINT8) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_INT16) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_UINT16) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_INT32) { - std::unique_ptr data, dummy; - RETURN_IF_NOT_OK(TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_UINT32) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_INT64 || column_type == DataType::DE_UINT64) { - std::unique_ptr data, dummy; - RETURN_IF_NOT_OK(TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } - - return Status::OK(); -} - -Status SaveToDisk::FetchFloatData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::unique_ptr> *data_ptr) { - RETURN_UNEXPECTED_IF_NULL(row_raw_data); - RETURN_UNEXPECTED_IF_NULL(data_ptr); - auto column_type = tensor->type(); - Status s; - if (column_type == DataType::DE_FLOAT16) { - std::unique_ptr data, dummy; - std::shared_ptr out_tensor; - RETURN_IF_NOT_OK(TypeCast(tensor, &out_tensor, DataType("float32"))); - RETURN_IF_NOT_OK( - TransformTensor(out_tensor->GetBuffer(), out_tensor->shape(), out_tensor->Size(), &data, data_ptr, &dummy)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_FLOAT32) { - std::unique_ptr data, dummy; - RETURN_IF_NOT_OK(TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_FLOAT64) { - std::unique_ptr data, dummy; - RETURN_IF_NOT_OK(TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, data_ptr, &dummy)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } - return Status::OK(); -} - -Status SaveToDisk::FetchItemData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::map>> *row_bin_data) { - RETURN_UNEXPECTED_IF_NULL(tensor); - RETURN_UNEXPECTED_IF_NULL(row_raw_data); - RETURN_UNEXPECTED_IF_NULL(row_bin_data); - auto column_type = tensor->type(); - Status s; - std::unique_ptr> data_ptr; - if (column_type == DataType::DE_BOOL) { - std::unique_ptr data; - std::unique_ptr dummy; - RETURN_IF_NOT_OK( - TransformTensor(tensor->GetBuffer(), tensor->shape(), tensor->Size(), &data, &data_ptr, &dummy, true)); - if (data != nullptr) { - (*row_raw_data)[column_name] = std::move(*data); - } - } else if (column_type == DataType::DE_INT8 || column_type == DataType::DE_UINT8 || - column_type == DataType::DE_INT16 || column_type == DataType::DE_UINT16 || - column_type == DataType::DE_INT32 || column_type == DataType::DE_UINT32 || - column_type == DataType::DE_INT64 || column_type == DataType::DE_UINT64) { - s = FetchIntData(tensor, column_name, row_raw_data, &data_ptr); - RETURN_IF_NOT_OK(s); - } else if (column_type == DataType::DE_FLOAT16 || column_type == DataType::DE_FLOAT32 || - column_type == DataType::DE_FLOAT64) { - s = FetchFloatData(tensor, column_name, row_raw_data, &data_ptr); - RETURN_IF_NOT_OK(s); - } else if (column_type == DataType::DE_BYTES) { - std::unique_ptr data; - std::unique_ptr dummy; - CHECK_FAIL_RETURN_UNEXPECTED(tensor->shape().Rank() == 1 || tensor->shape().Rank() == 0, - "Currently, multi-dimensional bytes cannot be converted to MindRecord."); - if (tensor->shape().Rank() == 1) { - CHECK_FAIL_RETURN_UNEXPECTED(tensor->shape().AsVector()[0] == 1, - "Currently, multi-dimensional bytes cannot be converted to MindRecord."); - } - // current only support one bytes to mindrecord field - uint32_t string_length = 0; - RETURN_IF_NOT_OK(tensor->GetStringLength(&string_length)); - RETURN_IF_NOT_OK(TransformTensor(tensor->GetBuffer() + kOffsetSize * 2, TensorShape({1}), string_length, &data, - &data_ptr, &dummy)); - } else if (column_type.IsString()) { - std::string_view sv; - RETURN_IF_NOT_OK(tensor->GetItemAt(&sv, {})); // assume scalar string tensor - std::string ss(sv); - (*row_raw_data)[column_name] = std::move(ss); - } else { - RETURN_STATUS_UNEXPECTED("Invalid dtype, got unexpected type when casting data: " + column_type.ToString()); - } - if (data_ptr != nullptr) { - (*row_bin_data)[column_name] = std::move(data_ptr); - } - return Status::OK(); -} - -Status SaveToDisk::FetchDataFromTensorRow(const TensorRow &row, - const std::unordered_map &column_name_id_map, - nlohmann::json *row_raw_data, - std::map>> *row_bin_data) { - RETURN_UNEXPECTED_IF_NULL(row_raw_data); - RETURN_UNEXPECTED_IF_NULL(row_bin_data); - Status s; - s = ValidateInputParams(row_raw_data, row_bin_data, column_name_id_map); - if (s.IsError()) { - return s; - } - for (auto &col : column_name_id_map) { - auto idx = col.second; - auto column_name = col.first; - auto &tensor = row[idx]; - s = FetchItemData(tensor, column_name, row_raw_data, row_bin_data); - RETURN_IF_NOT_OK(s); - } - return Status::OK(); -} - -template -Status SaveToDisk::TransformTensor(const unsigned char *src, const TensorShape &shape, const int64_t num_of_elements, - std::unique_ptr *data, std::unique_ptr> *data_ptr, - std::unique_ptr *s, bool need_convert) { - // No need to check src since we support some scenarios that src is nullptr and num_of_elements is 0. - RETURN_UNEXPECTED_IF_NULL(data); - RETURN_UNEXPECTED_IF_NULL(data_ptr); - RETURN_UNEXPECTED_IF_NULL(s); - - *data_ptr = std::make_unique>(num_of_elements * sizeof(T)); - if (need_convert) { - auto tmp_ptr = std::make_unique>(num_of_elements * sizeof(S)); - (void)std::copy(src, src + sizeof(S) * num_of_elements, tmp_ptr->begin()); - auto s_ptr = reinterpret_cast(&(*(tmp_ptr->begin()))); - auto el = std::make_unique(); - for (uint32_t i = 0; i < num_of_elements; ++i) { - *el = *(s_ptr + i); - auto t_ptr = reinterpret_cast(el.get()); - for (uint32_t j = 0; j < sizeof(T); ++j) { - *((*data_ptr)->begin() + i * sizeof(T) + j) = *(t_ptr + j); - } - } - } else { - (void)std::copy(src, src + sizeof(T) * num_of_elements, (*data_ptr)->begin()); - } - if (shape.empty()) { - *data = std::make_unique(); - auto t_ptr = reinterpret_cast((*data).get()); - for (uint32_t i = 0; i < sizeof(T); ++i) { - *(t_ptr + i) = *((*data_ptr)->begin() + i); - } - } - return Status::OK(); -} -#endif - -Status BuildVocabConsumer::Init(const std::shared_ptr &root) { return tree_adapter_->Compile(root, 1); } - -Status BuildVocabConsumer::Start() { - uint64_t start_time = GetSyscnt(); - // Getting one row would trigger building the vocab - TensorRow row; - RETURN_IF_NOT_OK(tree_adapter_->GetNext(&row)); - // The returned row would EOE which is an empty row - CHECK_FAIL_RETURN_UNEXPECTED(row.empty(), "BuildVocab: The fetched row from BuildVocab should be an EOE."); - RETURN_IF_NOT_OK(CollectPipelineInfo("BuildVocabConsumer", "Start", start_time)); - return Status::OK(); -} -Status DatasetSizeGetter::GetDatasetSize(int64_t *size, bool estimate) { - if (dataset_size_ == -1) { - RETURN_IF_NOT_OK(root_->GetDatasetSize(shared_from_this(), estimate, size)); - dataset_size_ = *size; // save the previous result - } - - *size = dataset_size_; - return Status::OK(); -} - -Status DatasetSizeGetter::Init(const std::shared_ptr &root) { - root_ = root; - return Status::OK(); -} - -Status DatasetSizeGetter::DryRun(const std::shared_ptr &ir_node, int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - std::shared_ptr tree_adapter = std::make_shared(TreeAdapter::UsageFlag::kDeGetter); - tree_adapters_.push_back(tree_adapter); - RETURN_IF_NOT_OK(tree_adapter->Compile(ir_node, 1)); - TensorRow row; - RETURN_IF_NOT_OK(GetRow(tree_adapter, &row)); - int64_t row_cnt = 0; - while (!row.empty()) { - ++row_cnt; - RETURN_IF_NOT_OK(GetRow(tree_adapter, &row)); - } - *dataset_size = row_cnt; - return Status::OK(); -} - -Status DatasetSizeGetter::GetRow(const std::shared_ptr &tree_adapter, TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - return tree_adapter->GetNext(row); -} - -Status DatasetSizeGetter::Terminate() { - for (const auto &tree : tree_adapters_) { - RETURN_UNEXPECTED_IF_NULL(tree); - RETURN_UNEXPECTED_IF_NULL(tree->AllTasks()); - RETURN_IF_NOT_OK(tree->AllTasks()->ServiceStop()); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h b/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h deleted file mode 100644 index 502eb6b4f..000000000 --- a/mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h +++ /dev/null @@ -1,314 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_TREE_CONSUMER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_TREE_CONSUMER_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" -#include "mindspore-lite/minddata/dataset/include/dataset/text.h" - -namespace mindspore::dataset { -// Forward declare -class TreeAdapter; -class DatasetNode; -class AutoTune; -class ProfilingManager; -/// A base class for tree consumers which would fetch rows from the tree pipeline -class TreeConsumer { - public: - /// Constructor that prepares an empty tree_adapter - TreeConsumer(); - - explicit TreeConsumer(int32_t num_epochs); - - /// \brief Destructor - virtual ~TreeConsumer() = default; - - /// Initializes the consumer, this involves constructing and preparing the tree. - /// \param root The dataset node that represent the root of the IR tree. - /// \return Status code. - virtual Status Init(const std::shared_ptr &root); - - /// Initializes the consumer, this involves constructing and preparing the tree. - /// \param root The dataset node that represent the root of the IR tree. - /// \param global_step The global step to initialize from. - /// \param dataset_size The number of steps that one epoch has. - /// \return Status code. - virtual Status Init(const std::shared_ptr &root, int64_t global_step, int64_t dataset_size); - - /// Internal function to perform the termination - /// \return Status error code - virtual Status Terminate(); - - /// Function for all consumers to get the offload JSON string. - /// \return Offload JSON string. - std::string GetOffload(); - - /// Function to reset the current consumer to the provided step. - /// The consumer will terminate the pipeline and create a new one with skip injected. - /// \param step The step to reset the pipeline to. - /// \param dataset_size The number of steps that one epoch has. - /// \return Status error code - virtual Status Reset(int64_t step, int64_t dataset_size); - - /// Function to stop the consumer. - /// \return Status error code - virtual Status Stop() { return Status::OK(); } - - virtual Status RegisterProfilingManager(); - - /// \brief Getter for profiling manager, no ownership - ProfilingManager *GetProfilingManager() { return profiling_manager_.get(); } - - /// \brief Return profiling manager pointer - std::shared_ptr GetProfilingManagerPtr() { return profiling_manager_; } - - Status InitAutoTune(); - - /// Returns the next row in a vector format - /// \param[out] out std::vector of Tensors - /// \return Status error code - virtual Status GetNextAsVector(std::vector *const out) { return Status::OK(); } - - /// Returns the next row in as a map - /// \param[out] out std::map of string to Tensor - /// \return Status error code - virtual Status GetNextAsMap(std::unordered_map *const out) { return Status::OK(); } - - /// Returns the next row in as a vector - /// \param[out] out std::vector of pairs of string to Tensor - /// \return Status error code - virtual Status GetNextAsOrderedPair(std::vector>> *const vec) { - return Status::OK(); - } - - protected: - /// The class owns the tree_adapter that handles execution tree operations. - std::unique_ptr tree_adapter_; - - /// Profiling Manager - std::shared_ptr profiling_manager_; - std::shared_ptr autotune_; - - /// Method to return the name of the consumer - /// \return string - virtual std::string Name() = 0; - - int32_t num_epochs_; -}; - -/// Consumer that iterates over the dataset and returns the rows one by one as a vector or a map -class IteratorConsumer : public TreeConsumer { - public: - /// Constructor which will call the base class default constructor. - /// \param num_epochs number of epochs. Default to -1 (infinite epochs). - explicit IteratorConsumer(int32_t num_epochs = -1) : TreeConsumer(num_epochs) {} - - ~IteratorConsumer() override = default; - - Status Init(const std::shared_ptr &root, int64_t global_step = 0, int64_t dataset_size = -1) override; - - /// Returns the next row in a vector format - /// \param[out] out std::vector of Tensors - /// \return Status error code - Status GetNextAsVector(std::vector *out) override; - - /// Returns the next row in as a map - /// \param[out] out std::map of string to Tensor - /// \return Status error code - Status GetNextAsMap(std::unordered_map *out) override; - - /// Returns the next row in as a vector - /// \param[out] out std::vector of pairs of string to Tensor - /// \return Status error code - Status GetNextAsOrderedPair(std::vector>> *vec) override; - - Status RegisterProfilingManager() override; - - protected: - /// Method to return the name of the consumer - /// \return string - std::string Name() override { return "IteratorConsumer"; } - - private: - std::map column_order_; // key: column id, val: column name -}; - -#ifndef ENABLE_ANDROID -/// Consumer that iterates over the dataset and writes it to disk -class SaveToDisk : public TreeConsumer { - public: - /// Constructor which will call the base class default constructor. - /// \param dataset_path path the the dataset - /// \param num_files number of files. Default to 1 - /// \param dataset_type The format of the dataset. Default to "mindrecod". - explicit SaveToDisk(const std::string &dataset_path, int32_t num_files = 1, - const std::string &dataset_type = "mindrecord") - : TreeConsumer(), dataset_path_(dataset_path), num_files_(num_files), dataset_type_(dataset_type) {} - - ~SaveToDisk() override = default; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams(); - - /// Save the given dataset to MindRecord format on disk. This is a blocking method (i.e., after returning, all rows - /// would be written to disk) - /// \return Status error code - virtual Status Save(); - - protected: - /// Method to return the name of the consumer - /// \return string - std::string Name() override { return "SaveToDisk"; } - - private: - template - Status TransformTensor(const unsigned char *src, const TensorShape &shape, int64_t num_of_elements, - std::unique_ptr *data, std::unique_ptr> *data_ptr, - std::unique_ptr *s, bool need_convert = false); - - Status FetchMetaFromTensorRow(const std::unordered_map &column_name_id_map, - const TensorRow &row, nlohmann::json *schema, std::vector *index_fields); - - Status FetchDataFromTensorRow(const TensorRow &row, - const std::unordered_map &column_name_id_map, - nlohmann::json *row_raw_data, - std::map>> *row_bin_data); - - Status FetchIntData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::unique_ptr> *data_ptr); - - Status FetchFloatData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::unique_ptr> *data_ptr); - - Status FetchItemData(std::shared_ptr tensor, std::string column_name, nlohmann::json *row_raw_data, - std::map>> *row_bin_data); - - template - bool map_compare(T const &lhs, T const &rhs); - - Status CheckTensorRowShapes(const std::unordered_map &column_name_id_map, const TensorRow &row, - std::map> *PreTensorRowShapes_ptr); - - std::string dataset_path_; - int32_t num_files_; - std::string dataset_type_; -}; -#endif - -/// Consumer that iterates over the dataset and send it to a device -class ToDevice : public TreeConsumer { - public: - explicit ToDevice(int32_t num_epochs = -1) : TreeConsumer(num_epochs) {} - - ~ToDevice() override = default; - - Status Init(const std::shared_ptr &root, int64_t global_step = 0, int64_t dataset_size = -1) override; - - Status RegisterProfilingManager() override; - - Status Terminate() override; - - /// Send the data to device - /// \return Status error code - virtual Status Send(); - - /// Stop to send data to device - /// \return Status error code - Status Stop() override; - - /// Continue to send data to device - /// \return Status error code - virtual Status Continue(); - - /// Get data info from TDT - /// \return Status error code - virtual Status GetDataInfo(std::vector *types, std::vector *shapes); - - /// Get data numbers from TDT - /// \return Status error code - virtual Status GetMbufQueueSize(size_t *queue_size); - - /// Get send info in sink mode - /// \return Status error code - virtual Status GetSendInfo(std::vector> *send_info); - - protected: - /// Method to return the name of the consumer - /// \return string - std::string Name() override { return "ToDevice"; } -}; - -/// Consumer that is used to get some pipeline information -class DatasetSizeGetter : public TreeConsumer, public std::enable_shared_from_this { - public: - DatasetSizeGetter() : dataset_size_(-1) {} - - ~DatasetSizeGetter() override = default; - - Status Init(const std::shared_ptr &root) override; - - Status Terminate() override; - - /// \brief Function to get the dataset size - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(int64_t *size, bool estimate = false); - - virtual Status GetRow(const std::shared_ptr &tree_adapter, TensorRow *row); - - std::string Name() override { return "DatasetSizeGetter"; } - - /// \brief Gets the dataset size by iterating over the entire dataset on a sub tree starting from ir_node - /// param[in] ir_node The node that marks the top most of the sub tree on which we want to iterate - /// \return Status - The status code return - Status DryRun(const std::shared_ptr &ir_node, int64_t *dataset_size); - - private: - std::shared_ptr root_; - std::vector> tree_adapters_; // this is vector to handle different branch of zip - int64_t dataset_size_; -}; - -class BuildVocabConsumer : public TreeConsumer { - public: - /// BuildVocabConsumer Constructor which will call the base class default constructor. - BuildVocabConsumer() = default; - - ~BuildVocabConsumer() override = default; - - Status Init(const std::shared_ptr &root) override; - - /// Start consuming - /// \return Status error code - virtual Status Start(); - - protected: - /// Method to return the name of the consumer - /// \return string - std::string Name() override { return "BuildVocab"; } -}; -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_CONSUMERS_TREE_CONSUMER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/data_schema.cc b/mindspore-lite/minddata/dataset/engine/data_schema.cc deleted file mode 100644 index 59438ba0e..000000000 --- a/mindspore-lite/minddata/dataset/engine/data_schema.cc +++ /dev/null @@ -1,491 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" - -#include -#include -#include -#include -#include -#include - -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// A macro for converting an input string representing the column type to it's actual -// numeric column type. -#define STR_TO_TENSORIMPL(in_col_str, out_type) \ - do { \ - if (in_col_str == "cvmat") { \ - out_type = TensorImpl::kCv; \ - } else if (in_col_str == "flex") { \ - out_type = TensorImpl::kFlexible; \ - } else if (in_col_str == "np") { \ - out_type = TensorImpl::kNP; \ - } else { \ - out_type = TensorImpl::kNone; \ - } \ - } while (false) - -// Constructor 1: Simple constructor that leaves things uninitialized. -ColDescriptor::ColDescriptor() - : type_(DataType::DE_UNKNOWN), rank_(0), tensor_impl_(TensorImpl::kNone), tensor_shape_(nullptr) {} - -// Constructor 2: Main constructor -ColDescriptor::ColDescriptor(const std::string &col_name, DataType col_type, TensorImpl tensor_impl, int32_t rank, - const TensorShape *in_shape) - : type_(col_type), rank_(rank), tensor_impl_(tensor_impl), col_name_(col_name) { - // If a shape was provided, create unique pointer for it and copy construct it into - // our shape. Otherwise, set our shape to be empty. - if (in_shape != nullptr) { - // Create a shape and copy construct it into our column's shape. - tensor_shape_ = std::make_unique(*in_shape); - } else { - tensor_shape_ = nullptr; - } - // If the user input a shape, then the rank of the input shape needs to match - // the input rank - if (in_shape != nullptr && in_shape->known() && in_shape->Size() != rank_) { - rank_ = in_shape->Size(); - MS_LOG(WARNING) << "Rank does not match the number of dimensions in the provided shape." - << " Overriding rank with the number of dimensions in the provided shape."; - } -} - -// Explicit copy constructor is required -ColDescriptor::ColDescriptor(const ColDescriptor &in_cd) - : type_(in_cd.type_), rank_(in_cd.rank_), tensor_impl_(in_cd.tensor_impl_), col_name_(in_cd.col_name_) { - // If it has a tensor shape, make a copy of it with our own unique_ptr. - tensor_shape_ = in_cd.HasShape() ? std::make_unique(in_cd.Shape()) : nullptr; -} - -// Assignment overload -ColDescriptor &ColDescriptor::operator=(const ColDescriptor &in_cd) { - if (&in_cd != this) { - type_ = in_cd.type_; - rank_ = in_cd.rank_; - tensor_impl_ = in_cd.tensor_impl_; - col_name_ = in_cd.col_name_; - // If it has a tensor shape, make a copy of it with our own unique_ptr. - tensor_shape_ = in_cd.HasShape() ? std::make_unique(in_cd.Shape()) : nullptr; - } - return *this; -} - -// Destructor -ColDescriptor::~ColDescriptor() = default; - -// A print method typically used for debugging -void ColDescriptor::Print(std::ostream &out) const { - out << " Name : " << col_name_ << "\n Type : " << type_ << "\n Rank : " << rank_ - << "\n Shape : ("; - if (tensor_shape_) { - out << *tensor_shape_ << ")\n"; - } else { - out << "no shape provided)\n"; - } -} - -// Given a number of elements, this function will compute what the actual Tensor shape would be. -// If there is no starting TensorShape in this column, or if there is a shape but it contains -// an unknown dimension, then the output shape returned shall resolve dimensions as needed. -Status ColDescriptor::MaterializeTensorShape(int32_t num_elements, TensorShape *out_shape) const { - if (out_shape == nullptr) { - RETURN_STATUS_UNEXPECTED("Unexpected null output shape argument."); - } - - // If the shape is not given in this column, then we assume the shape will be: {numElements} - if (tensor_shape_ == nullptr) { - if (this->Rank() == 0 && num_elements == 1) { - *out_shape = TensorShape::CreateScalar(); - return Status::OK(); - } - *out_shape = TensorShape({num_elements}); - return Status::OK(); - } - - // Build the real TensorShape based on the requested shape and the number of elements in the data. - // If there are unknown dimensions, then the unknown dimension needs to be filled in. - // Example: requestedShape: {?,4,3}. - // If numElements is 24, then the output shape can be computed to: {2,4,3} - std::vector requested_shape = tensor_shape_->AsVector(); - int64_t num_elements_of_shape = 1; // init to 1 as a starting multiplier. - - // unknownDimPosition variable is overloaded to provide 2 meanings: - // 1) If it's set to DIM_UNKNOWN, then it provides a boolean knowledge to tell us if there are - // any unknown dimensions. i.e. if it's set to unknown, then there are no unknown dimensions. - // 2) If it's set to a numeric value, then this is the vector index position within the shape - // where the single unknown dimension can be found. - int64_t unknown_dim_position = TensorShape::kDimUnknown; // Assume there are no unknown dims to start - - for (int i = 0; i < requested_shape.size(); ++i) { - // If we already had an unknown dimension, then we cannot have a second unknown dimension. - // We only support the compute of a single unknown dim. - if (requested_shape[i] == TensorShape::kDimUnknown && unknown_dim_position != TensorShape::kDimUnknown) { - RETURN_STATUS_UNEXPECTED("Requested shape has more than one unknown dimension!"); - } - - // If the current dimension in the requested shape is a known value, then compute the number of - // elements so far. - if (requested_shape[i] != TensorShape::kDimUnknown) { - num_elements_of_shape *= requested_shape[i]; - } else { - // This dimension is unknown so track which dimension position has it. - unknown_dim_position = i; - } - } - - // Sanity check the the computed element counts divide evenly into the input element count - if (num_elements < num_elements_of_shape || num_elements_of_shape == 0 || num_elements % num_elements_of_shape != 0) { - std::string err = "Requested shape has an invalid element count! Number elements: " + std::to_string(num_elements) + - ", number elements of shape: " + std::to_string(num_elements_of_shape); - RETURN_STATUS_UNEXPECTED(err); - } - - // If there was any unknown dimensions, then update the requested shape to fill in the unknown - // dimension with the correct value. If there were no unknown dim's then the output shape will - // remain to be the same as the requested shape. - if (unknown_dim_position != TensorShape::kDimUnknown) { - requested_shape[unknown_dim_position] = (num_elements / num_elements_of_shape); - } - - // Any unknown dimension is filled in now. Set the output shape - *out_shape = TensorShape(requested_shape); - return Status::OK(); -} - -// getter function for the shape -TensorShape ColDescriptor::Shape() const { - if (tensor_shape_ != nullptr) { - return *tensor_shape_; // copy construct a shape to return - } else { - return TensorShape::CreateUnknownRankShape(); // empty shape to return - } -} - -const char DataSchema::DEFAULT_DATA_SCHEMA_FILENAME[] = "datasetSchema.json"; - -// Constructor 1: Simple constructor that leaves things uninitialized. -DataSchema::DataSchema() : num_rows_(0) {} - -// Internal helper function. Parses the json schema file in any order and produces a schema that -// does not follow any particular order (json standard does not enforce any ordering protocol). -// This one produces a schema that contains all of the columns from the schema file. -Status DataSchema::AnyOrderLoad(nlohmann::json column_tree) { - // Iterate over the json file. Each parent json node is the column name, - // followed by the column properties in the child tree under the column. - // Outer loop here iterates over the parents (i.e. the column name) - if (!column_tree.is_array()) { - for (nlohmann::json::iterator it = column_tree.begin(); it != column_tree.end(); ++it) { - std::string col_name = it.key(); - nlohmann::json column_child_tree = it.value(); - RETURN_IF_NOT_OK(ColumnLoad(column_child_tree, col_name)); - } - } else { - // Case where the schema is a list of columns not a dict - for (nlohmann::json::iterator it = column_tree.begin(); it != column_tree.end(); ++it) { - nlohmann::json column_child_tree = it.value(); - RETURN_IF_NOT_OK(ColumnLoad(column_child_tree, "")); - } - } - return Status::OK(); -} - -// Internal helper function. For each input column name, perform a lookup to the json document to -// find the matching column. When the match is found, process that column to build the column -// descriptor and add to the schema in the order in which the input column names are given.id -Status DataSchema::ColumnOrderLoad(nlohmann::json column_tree, const std::vector &columns_to_load) { - if (!column_tree.is_array()) { - // the json file is dict (e.g., {image: ...}) - // Loop over the column name list - for (const auto &curr_col_name : columns_to_load) { - // Find the column in the json document - auto column_info = column_tree.find(common::SafeCStr(curr_col_name)); - if (column_info == column_tree.end()) { - RETURN_STATUS_UNEXPECTED("Invalid data, failed to find column: " + curr_col_name + " in JSON schema file."); - } - // At this point, columnInfo.value() is the subtree in the json document that contains - // all of the data for a given column. This data will formulate our schema column. - const std::string &col_name = column_info.key(); - nlohmann::json column_child_tree = column_info.value(); - RETURN_IF_NOT_OK(ColumnLoad(column_child_tree, col_name)); - } - } else { - // the json file is array (e.g., [name: image...]) - // Loop over the column name list - for (const auto &curr_col_name : columns_to_load) { - // Find the column in the json document - int32_t index = -1; - int32_t i = 0; - for (const auto &it_child : column_tree.items()) { - auto name = it_child.value().find("name"); - if (name == it_child.value().end()) { - RETURN_STATUS_UNEXPECTED("Invalid data, \"name\" field is missing for column: " + curr_col_name + - " in JSON schema file."); - } - if (name.value() == curr_col_name) { - index = i; - break; - } - i++; - } - if (index == -1) { - RETURN_STATUS_UNEXPECTED("Invalid data, failed to find column: " + curr_col_name + " in JSON schema file."); - } - nlohmann::json column_child_tree = column_tree[index]; - RETURN_IF_NOT_OK(ColumnLoad(column_child_tree, curr_col_name)); - } - } - return Status::OK(); -} - -// Internal helper function for parsing shape info and building a vector for the shape construction. -static Status BuildShape(const nlohmann::json &shapeVal, std::vector *outShape) { - if (outShape == nullptr) { - RETURN_STATUS_UNEXPECTED("outShape can not be nullptr."); - } - if (shapeVal.empty()) { - return Status::OK(); - } - - // Iterate over the integer list and add those values to the output shape tensor - auto items = shapeVal.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(*outShape), [](it_type j) { return j.value(); }); - return Status::OK(); -} - -// Internal helper function. Given the json tree for a given column, load it into our schema. -Status DataSchema::ColumnLoad(nlohmann::json column_child_tree, const std::string &col_name) { - int32_t rank_value = -1; - TensorImpl t_impl_value = TensorImpl::kFlexible; - std::string name = ""; - std::string type_str = ""; - std::vector tmp_shape = {}; - bool shape_field_exists = false; - // Iterate over this column's attributes. - // Manually iterating each of the child nodes/trees here so that we can provide our own error handling. - for (const auto &it_child : column_child_tree.items()) { - // Save the data for each of the attributes into variables. We'll use these to construct later. - if (it_child.key() == "name") { - name = it_child.value(); - } else if (it_child.key() == "type") { - type_str = it_child.value(); - } else if (it_child.key() == "rank") { - rank_value = it_child.value(); - } else if (it_child.key() == "t_impl") { - STR_TO_TENSORIMPL(it_child.value(), t_impl_value); - } else if (it_child.key() == "shape") { - shape_field_exists = true; - RETURN_IF_NOT_OK(BuildShape(it_child.value(), &tmp_shape)); - } else { - std::string err_msg = "Invalid data, unexpected column attribute " + it_child.key() + " for column " + col_name + - ", expected attribute: name, type, rank, t_impl or shape."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - if (!name.empty()) { - if (!col_name.empty() && col_name != name) { - std::string err_msg = "Invalid data, failed to find column: " + col_name + " in JSON schema file."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - if (col_name.empty()) { - std::string err_msg = "Invalid data, \"name\" field is missing for column " + col_name + " in JSON schema file."; - RETURN_STATUS_UNEXPECTED(err_msg); - } else { - name = col_name; - } - } - // data type is mandatory field - if (type_str.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid data, \"type\" field is missing for column " + col_name + - " in JSON schema file."); - } - - // rank number is mandatory field - if (rank_value <= -1) { - RETURN_STATUS_UNEXPECTED("Invalid data, \"rank\" field of column " + col_name + - " must have value >= 0 in JSON schema file."); - } - - // Create the column descriptor for this column from the data we pulled from the json file - TensorShape col_shape = TensorShape(tmp_shape); - if (shape_field_exists) { - RETURN_IF_NOT_OK(this->AddColumn(ColDescriptor(name, DataType(type_str), t_impl_value, rank_value, &col_shape))); - } else { - // Create a column descriptor that doesn't have a shape - RETURN_IF_NOT_OK(this->AddColumn(ColDescriptor(name, DataType(type_str), t_impl_value, rank_value))); - } - return Status::OK(); -} - -// Parses a schema json file and populates the columns and meta info. -Status DataSchema::LoadSchemaFile(const std::string &schema_file_path, - const std::vector &columns_to_load) { - std::ifstream in(schema_file_path, std::ifstream::in); - try { - nlohmann::json js; - in >> js; - auto s = PreLoadExceptionCheck(js); - if (s != Status::OK()) { - in.close(); - return s; - } - try { - num_rows_ = js.at("numRows").get(); - } catch (nlohmann::json::out_of_range &e) { - num_rows_ = 0; - } catch (nlohmann::json::exception &e) { - in.close(); - RETURN_STATUS_UNEXPECTED("Invalid data, unable to parse \"numRows\" field from JSON schema file: " + - schema_file_path + ", check syntax with JSON tool."); - } - nlohmann::json column_tree = js.at("columns"); - if (column_tree.empty()) { - in.close(); - RETURN_STATUS_UNEXPECTED("Invalid data, \"columns\" field is missing in JSON schema file: " + schema_file_path); - } - if (columns_to_load.empty()) { - // Parse the json tree and load the schema's columns in whatever order that the json - // layout decides - Status rc = this->AnyOrderLoad(column_tree); - if (rc.IsError()) { - in.close(); - return rc; - } - } else { - Status rc = this->ColumnOrderLoad(column_tree, columns_to_load); - if (rc.IsError()) { - rc.SetErrDescription(rc.GetErrDescription() + " file: " + schema_file_path); - in.close(); - return rc; - } - } - in.close(); - } catch (const std::exception &err) { - in.close(); - // Catch any exception and convert to Status return code - RETURN_STATUS_UNEXPECTED("Invalid file, failed to load and parse JSON schema file: " + schema_file_path + - ", check syntax with JSON tools."); - } - return Status::OK(); -} - -// Parses a schema json string and populates the columns and meta info. -Status DataSchema::LoadSchemaString(const std::string &schema_json_string, - const std::vector &columns_to_load) { - try { - nlohmann::json js = nlohmann::json::parse(schema_json_string); - RETURN_IF_NOT_OK(PreLoadExceptionCheck(js)); - num_rows_ = js.value("numRows", 0); - nlohmann::json column_tree = js.at("columns"); - if (column_tree.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid data, \"columns\" field is missing in JSON schema string."); - } - if (columns_to_load.empty()) { - // Parse the json tree and load the schema's columns in whatever order that the json - // layout decides - RETURN_IF_NOT_OK(this->AnyOrderLoad(column_tree)); - } else { - Status rc = this->ColumnOrderLoad(column_tree, columns_to_load); - if (rc.IsError()) { - rc.SetErrDescription(rc.GetErrDescription() + " file content: " + schema_json_string); - return rc; - } - } - } catch (const std::exception &err) { - // Catch any exception and convert to Status return code - RETURN_STATUS_UNEXPECTED("Invalid data, failed to load and parse JSON schema string, check syntax with JSON tool."); - } - return Status::OK(); -} - -// Destructor -DataSchema::~DataSchema() = default; - -// Getter for the ColDescriptor by index -const ColDescriptor &DataSchema::Column(int32_t idx) const { - MS_ASSERT(idx < static_cast(col_descs_.size())); - return col_descs_[idx]; -} - -// A print method typically used for debugging -void DataSchema::Print(std::ostream &out) const { - out << "Dataset schema: ("; - for (const auto &col_desc : col_descs_) { - out << col_desc << "\n"; - } -} - -// Adds a column descriptor to the schema -Status DataSchema::AddColumn(const ColDescriptor &cd) { - // Sanity check there's not a duplicate name before adding the column - for (auto i = 0; i < col_descs_.size(); ++i) { - if (col_descs_[i].Name() == cd.Name()) { - std::ostringstream ss; - ss << "column name '" << cd.Name() << "' already exists in schema."; - std::string err_msg = ss.str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - col_descs_.push_back(cd); - return Status::OK(); -} - -// Internal helper function. Performs sanity checks on the json file setup. -Status DataSchema::PreLoadExceptionCheck(const nlohmann::json &js) { - // Check if columns node exists. It is required for building schema from file. - if (js.find("columns") == js.end()) { - RETURN_STATUS_UNEXPECTED("Invalid data, \"columns\" field is missing in the JSON schema file."); - } - return Status::OK(); -} - -// Loops through all columns in the schema and returns a map with the column -// name to column index number. -Status DataSchema::GetColumnNameMap(std::unordered_map *out_column_name_map) { - if (out_column_name_map == nullptr) { - RETURN_STATUS_UNEXPECTED("unexpected null output column name map."); - } - - for (size_t i = 0; i < col_descs_.size(); ++i) { - if (col_descs_[i].Name().empty()) { - RETURN_STATUS_UNEXPECTED("Constructing column name map from schema, but found empty column name."); - } - (*out_column_name_map)[col_descs_[i].Name()] = i; - } - - return Status::OK(); -} - -Status DataSchema::GetColumnName(std::vector *column_names) const { - RETURN_UNEXPECTED_IF_NULL(column_names); - column_names->clear(); - for (const auto &col_desc : col_descs_) { - if (col_desc.Name().empty()) { - RETURN_STATUS_UNEXPECTED("Found empty column name in schema."); - } - column_names->emplace_back(col_desc.Name()); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/data_schema.h b/mindspore-lite/minddata/dataset/engine/data_schema.h deleted file mode 100644 index 59e5dcfd1..000000000 --- a/mindspore-lite/minddata/dataset/engine/data_schema.h +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATA_SCHEMA_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATA_SCHEMA_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \class ColDescriptor data_schema.h -/// \brief A simple class to provide meta info about a column. -class ColDescriptor { - public: - /// \brief Constructor 1: Simple constructor that leaves things uninitialized. - ColDescriptor(); - - /// \brief Constructor 2: Main constructor - /// \param[in] col_name - The name of the column - /// \param[in] col_type - The DE Datatype of the column - /// \param[in] tensor_impl - The (initial) type of tensor implementation for the column - /// \param[in] rank - The number of dimension of the data - /// \param[in] in_shape - option argument for input shape - ColDescriptor(const std::string &col_name, DataType col_type, TensorImpl tensor_impl, int32_t rank, - const TensorShape *in_shape = nullptr); - - /// \brief Explicit copy constructor is required - /// \param[in] in_cd - the source ColDescriptor - ColDescriptor(const ColDescriptor &in_cd); - - /// \brief Assignment overload - /// \param in_cd - the source ColDescriptor - ColDescriptor &operator=(const ColDescriptor &in_cd); - - /// \brief Destructor - ~ColDescriptor(); - - /// \brief A print method typically used for debugging - /// \param out - The output stream to write output to - void Print(std::ostream &out) const; - - /// \brief Given a number of elements, this function will compute what the actual Tensor shape would be. - /// If there is no starting TensorShape in this column, or if there is a shape but it contains - /// an unknown dimension, then the output shape returned shall resolve dimensions as needed. - /// \param[in] num_elements - The number of elements in the data for a Tensor - /// \param[in/out] out_shape - The materialized output Tensor shape - /// \return Status The status code returned - Status MaterializeTensorShape(int32_t num_elements, TensorShape *out_shape) const; - - /// \brief << Stream output operator overload - /// This allows you to write the debug print info using stream operators - /// \param[in] out - reference to the output stream being overloaded - /// \param[in] cd - reference to the ColDescriptor to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ColDescriptor &cd) { - cd.Print(out); - return out; - } - - /// \brief getter function - /// \return The column's DataType - DataType Type() const { return type_; } - - /// \brief getter function - /// \return The column's rank - int32_t Rank() const { return rank_; } - - /// \brief getter function - /// \return The column's name - std::string Name() const { return col_name_; } - - /// \brief getter function - /// \return The column's shape - TensorShape Shape() const; - - /// \brief Check if the column has a shape. - /// \return Whether the column has a shape. - bool HasShape() const { return tensor_shape_ != nullptr; } - - /// \brief Check if the column has a known shape. - /// \return Whether the column has a known shape. - bool HasKnownShape() const { return HasShape() && Shape().known(); } - - /// \brief getter function - /// \return The column's tensor implementation type - TensorImpl GetTensorImpl() const { return tensor_impl_; } - - private: - DataType type_; // The columns type - int32_t rank_; // The rank for this column (number of dimensions) - TensorImpl tensor_impl_; // The initial flavour of the tensor for this column - std::unique_ptr tensor_shape_; // The fixed shape (if given by user) - std::string col_name_; // The name of the column -}; - -/// \class DataSchema data_schema.h -/// \brief A list of the columns. -class DataSchema { - public: - /// \brief Constructor - DataSchema(); - - /// \brief Destructor - ~DataSchema(); - - /// \brief Parses a schema json file and populates the columns and meta info. - /// \param[in] schema_file_path - the schema file that has the column's info to load - /// \param[in] columns_to_load - list of strings for columns to load. if empty, assumes all columns. - /// \return Status The status code returned - Status LoadSchemaFile(const std::string &schema_file_path, const std::vector &columns_to_load); - - /// \brief Parses a schema JSON string and populates the columns and meta info. - /// \param[in] schema_json_string - the schema file that has the column's info to load - /// \param[in] columns_to_load - list of strings for columns to load. if empty, assumes all columns. - /// \return Status The status code returned - Status LoadSchemaString(const std::string &schema_json_string, const std::vector &columns_to_load); - - /// \brief A print method typically used for debugging - /// \param[in] out - The output stream to write output to - void Print(std::ostream &out) const; - - /// \brief << Stream output operator overload. This allows you to write the debug print info using stream operators - /// \param[in] out - reference to the output stream being overloaded - /// \param[in] ds - reference to the DataSchema to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const DataSchema &ds) { - ds.Print(out); - return out; - } - - /// \brief Adds a column descriptor to the schema - /// \param[in] cd - The ColDescriptor to add - /// \return Status The status code returned - Status AddColumn(const ColDescriptor &cd); - - /// \brief getter - /// \return The reference to a ColDescriptor to get (const version) - const ColDescriptor &Column(int32_t idx) const; - - /// \brief getter - /// \return The number of columns in the schema - size_t NumColumns() const { return col_descs_.size(); } - - bool Empty() const { return NumColumns() == 0; } - - /// \brief getter - /// \return The number of rows read from schema - int64_t NumRows() const { return num_rows_; } - - static const char DEFAULT_DATA_SCHEMA_FILENAME[]; - - /// \brief Loops through all columns in the schema and returns a map with the column name to column index number. - /// \param[in/out] out_column_name_map - The output map of columns names to column index - /// \return Status The status code returned - Status GetColumnNameMap(std::unordered_map *out_column_name_map); - - /// \brief Get the column name list of the schema. - /// \param[out] column_names The column names in the schema. - /// \return The status code. - Status GetColumnName(std::vector *column_names) const; - - private: - /// \brief Internal helper function. Parses the json schema file in any order and produces a schema that - /// does not follow any particular order (json standard does not enforce any ordering protocol). - /// This one produces a schema that contains all of the columns from the schema file. - /// \param[in] column_tree - The nlohmann tree from the json file to parse - /// \return Status The status code returned - Status AnyOrderLoad(nlohmann::json column_tree); - - /// \brief Internal helper function. For each input column name, perform a lookup to the json document to - /// find the matching column. When the match is found, process that column to build the column - /// descriptor and add to the schema in the order in which the input column names are given. - /// \param[in] column_tree - The nlohmann tree from the json file to parse - /// \param[in] columns_to_load - list of strings for the columns to add to the schema - /// \return Status The status code returned - Status ColumnOrderLoad(nlohmann::json column_tree, const std::vector &columns_to_load); - - /// \brief Internal helper function. Given the json tree for a given column, load it into our schema. - /// \param[in] columnTree - The nlohmann child tree for a given column to load. - /// \param[in] col_name - The string name of the column for that subtree. - /// \return Status The status code returned - Status ColumnLoad(nlohmann::json column_child_tree, const std::string &col_name); - - /// \brief Internal helper function. Performs sanity checks on the json file setup. - /// \param[in] js - The nlohmann tree for the schema file - /// \return Status The status code returned - Status PreLoadExceptionCheck(const nlohmann::json &js); - - std::vector col_descs_; // Vector of column descriptors - int64_t num_rows_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATA_SCHEMA_H_ diff --git a/mindspore-lite/minddata/dataset/engine/dataset_iterator.cc b/mindspore-lite/minddata/dataset/engine/dataset_iterator.cc deleted file mode 100644 index 08e191652..000000000 --- a/mindspore-lite/minddata/dataset/engine/dataset_iterator.cc +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" - -namespace mindspore { -namespace dataset { -// Fetches one row of data from the iterator as a column map. -Status DatasetIterator::GetNextAsMap(TensorMap *out_map) { - if (out_map == nullptr) { - RETURN_STATUS_UNEXPECTED("Null output map in iterator!"); - } - - out_map->clear(); - - TensorRow curr_row; - MS_LOG(INFO) << "get next as map start."; - RETURN_IF_NOT_OK(FetchNextTensorRow(&curr_row)); - MS_LOG(INFO) << "fetchNextTensor success."; - - // Return empty map if there's no data - if (curr_row.empty()) { - return Status::OK(); - } - - // The column name mapping is needed to be able to produce the tensor map output. - // The column name mapping comes from the source operator that is producing the data into the iterator. - // To avoid having to fetch this for every time, we'll take a local copy of the column name id mapping - // and save in the iterator. We only have to do this once. All subsequent iterations use the same mapping. - if (col_name_id_map_.empty()) { - // Determine the column name map by calling the derived class method to retrieve the column - // name map - col_name_id_map_ = this->GetColumnNameMap(); - } - - // Populate the out map from the row and return it - for (const auto &colMap : col_name_id_map_) { - std::string column_name = colMap.first; - // Need to filter meta column start with kDftMetaColumnPrefix - size_t pos = column_name.find(kDftMetaColumnPrefix); - if (pos != std::string::npos && pos == 0) { - continue; - } - (*out_map)[colMap.first] = std::move(curr_row[colMap.second]); - } - if (out_map->size() == 0) { - std::string err_msg = "No effective column found, maybe all columns are meta column and will be filtered. "; - err_msg += "If you want to output meta column please rename column name to a new one which is not start with "; - err_msg += "\"" + std::string(kDftMetaColumnPrefix) + "\""; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} -// Constructor of the DatasetIterator -DatasetIterator::DatasetIterator(std::shared_ptr exe_tree) - : root_(exe_tree->root()), - tracing_(nullptr), - cur_batch_num_(0), - cur_connector_size_(0), - cur_connector_capacity_(0), - eof_handled_(false) { - std::shared_ptr node; - Status s = GlobalContext::profiling_manager()->GetTracingNode(kDatasetIteratorTracingName, &node); - if (s.IsOk()) { - tracing_ = std::dynamic_pointer_cast(node); - } -} - -DatasetIterator::~DatasetIterator() = default; - -// Fetches one row of data from the iterator. Overrides the base class. This one fetches -// from the tree root node directly. -Status DatasetIterator::FetchNextTensorRow(TensorRow *out_row) { - if (out_row == nullptr) { - RETURN_STATUS_UNEXPECTED("Null output row in iterator!"); - } - // clear the old tensor row - out_row->clear(); - bool is_profiling_enable = GlobalContext::profiling_manager()->IsProfilingEnable(root_->Tree()); - // Once eof is handled, always return empty row. Class must be destroyed and recreated if you - // want to iterate again. - if (eof_handled_) { - std::string err = "EOF buffer encountered. Users try to fetch data beyond the specified number of epochs."; - RETURN_STATUS_UNEXPECTED(err); - } - if (tracing_ != nullptr) { - cur_connector_size_ = root_->ConnectorSize(); - cur_connector_capacity_ = root_->ConnectorCapacity(); - } - RETURN_IF_NOT_OK(root_->GetNextRow(out_row)); - - // Since GetNextRow was used rather than GetNextInput(), it means we need to manually - // handle eoe and eof messages here. - // - // An eoe row means we have iterated an epoch. - // The next row in the pipeline might be an EOF or a TensorRow for next epoch - if (out_row->eoe()) { - MS_LOG(INFO) << "End of data iteration. cur_batch_num_: " << cur_batch_num_; - if (is_profiling_enable) { - root_->Tree()->SetEpochEnd(); - GlobalContext::profiling_manager()->RecordEndOfEpoch(static_cast(cur_batch_num_)); - } - return Status::OK(); - } - - // An eof buffer means it is the end of execution and all operators are shutting down. - // Because there is no more data to return to the caller, this will change `eof_handled_` state and - // returns status unexpected error. - if (out_row->eof()) { - eof_handled_ = true; - root_->Tree()->SetFinished(); - std::string err = "EOF buffer encountered. Users try to fetch data beyond the specified number of epochs."; - RETURN_STATUS_UNEXPECTED(err); - } - if (tracing_ != nullptr) { - cur_batch_num_++; - tracing_->Record(static_cast(CONNECTOR_DEPTH), cur_connector_capacity_, cur_batch_num_, - cur_connector_size_, ProfilingTime::GetCurMilliSecond()); - } - return Status::OK(); -} - -// Getter -std::unordered_map DatasetIterator::GetColumnNameMap() const { - return root_->column_name_id_map(); -} - -// Constructor of the ChildIterator -ChildIterator::ChildIterator(DatasetOp *current_op, int32_t worker_id, int32_t child_idx) - : current_op_(current_op), child_idx_(child_idx), worker_id_(worker_id), end_epoch_(false), eof_handled_(false) {} - -ChildIterator::~ChildIterator() { current_op_ = nullptr; } - -// Fetches one row of data from the iterator. Overrides the base class. This one fetches -// only from the child/worker id as given from the constructor. -Status ChildIterator::FetchNextTensorRow(TensorRow *out_row) { - RETURN_UNEXPECTED_IF_NULL(out_row); - // clear the old tensor row - out_row->clear(); - - // Once eof is handled, always return empty row. Class must be destroyed and recreated if you - // want to iterate again. - if (eof_handled_) { - std::string err = "EOF buffer encountered. Users try to fetch data beyond the specified number of epochs."; - RETURN_STATUS_UNEXPECTED(err); - } - - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(current_op_->child(child_idx_)->GetNextRow(out_row)); - RETURN_IF_NOT_OK(CollectOpInfo(current_op_->NameWithID(), "GetFromPreviousOp", start_time, - {{"TensorRowFlags", out_row->FlagName()}})); - - // If an eoe is picked up here, we simply return an empty vector and it's up to the - // caller to decide what it wants to do next.TensorRow - if (out_row->eoe()) { - MS_LOG(DEBUG) << "(" << current_op_->NameWithID() << ", " << child_idx_ << ")" - << "Child iterator picked up EOE."; - end_epoch_ = true; - return Status::OK(); - } else { - end_epoch_ = false; - } - - if (out_row->eof()) { - MS_LOG(DEBUG) << "(" << current_op_->NameWithID() << ", " << child_idx_ << ")" - << "Child iterator picked up EOF."; - eof_handled_ = true; - *out_row = TensorRow(TensorRow::kFlagEOF); - } - return Status::OK(); -} - -// drain till the next eoe -Status ChildIterator::Drain() { - if (end_epoch_ == true) { - // Calling drain against a child that is already at it's eoe state will not result in any action. - // This allows you to do: - // - fetch until empty row - // - drain (will not actually drain because you are already at the end of the iteration) - // However, the next time after that, it will perform it's normal draining activities. - end_epoch_ = false; - MS_LOG(DEBUG) << "No operation drain, already at end of epoch."; - return Status::OK(); - } - MS_LOG(DEBUG) << "Child draining buffers until eoe."; - TensorRow row; - // else we drain until eoe or eof, eof here is for sanity check - while (!row.eoe() && !row.eof()) { - RETURN_IF_NOT_OK(current_op_->child(child_idx_)->GetNextRow(&row)); - } - if (row.eof()) { - RETURN_STATUS_UNEXPECTED("Child iterator picked up EOF in drain."); - } - return Status::OK(); -} - -// Getter -std::unordered_map ChildIterator::GetColumnNameMap() const { - return current_op_->child(child_idx_)->column_name_id_map(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/dataset_iterator.h b/mindspore-lite/minddata/dataset/engine/dataset_iterator.h deleted file mode 100644 index 40fee7e94..000000000 --- a/mindspore-lite/minddata/dataset/engine/dataset_iterator.h +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASET_ITERATOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASET_ITERATOR_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -using TensorMap = std::unordered_map>; - -// forward declare -class ExecutionTree; - -// The DatasetIterator derived class is for fetching rows off the end/root of the execution tree. -class DatasetIterator { - public: - // Constructor of the DatasetIterator - // @param exe_tree The execution tree we want to pull/iterate the data from using it's root node. - explicit DatasetIterator(std::shared_ptr exe_tree); - - // Destructor - ~DatasetIterator(); - - // Getter - // @return The string to column id mapping. - std::unordered_map GetColumnNameMap() const; - - bool EofHandled() const { return eof_handled_; } - - // Fetches one row of data from the iterator. - // the base class version simply performs error handling and returns empty row. Actual - // functionality exists in the derived versions of this function. - // @param out_row - A TensorRow (vector of shared pointers to Tensors). If any of the of data - // messages are encountered (such as eoe or eof), then an empty TensorRow is returned back. - // @return Status The status code returned - // @note The position of a Tensor/column might be different from the initial column order - // in corresponding Dataset Op. User must be aware that MapOp, ZipOps, and others might change - // the column ordering. - Status FetchNextTensorRow(TensorRow *out_row); - - // Fetches one row of data from the iterator as a column map. - // @return A unordered map from column name to shared pointer to Tensor. - Status GetNextAsMap(TensorMap *out_map); - - private: - std::shared_ptr root_; // saves the root of the executionTree - TensorRow device_queue_row_; - std::shared_ptr tracing_; // trace profiling data - int32_t cur_batch_num_; // current batch number,used for profiling - int32_t cur_connector_size_; // current connector size of root op,used for profiling - int32_t cur_connector_capacity_; // current connector capacity of root op, used for profiling - bool eof_handled_; // T/F if this op got an eof - std::unordered_map col_name_id_map_; - std::vector> column_order_; // key: column name, val: column id -}; - -// The ChildIterator derived class is for fetching rows from intermediate nodes of execution tree. -// This one should only be used by internal Dataset operators, rather than an end-user. -class ChildIterator { - public: - // Constructor of the DatasetIterator - // @param current_op - The parent op from which we'll fetch from it's children. - // @param worker_id - The worker id to use when fetching from the children. - // @param child_idx - The index to the child to fetch from. - ChildIterator(DatasetOp *current_op, int32_t worker_id, int32_t child_idx); - - // Destructor - ~ChildIterator(); - - // Fetches one row of data from the iterator. Overrides the base class. This one fetches - // only from the child/worker id as given from the constructor. - // @param out_row - A TensorRow (vector of shared pointers to Tensors). If any of the of data - // messages are encountered (such as eoe or eof), then an empty TensorRow is returned back. - // @return Status The status code returned - Status FetchNextTensorRow(TensorRow *out_row); - - // This function drains buffer until next eoe has been received. - // It will be a no-op if the previous row returned is empty. - // @return Status The status code returned - Status Drain(); - - // Getter - // @return The string to column id mapping. - std::unordered_map GetColumnNameMap() const; - - // Return T/F if end of epoch - bool EndOfEpoch() { return end_epoch_; } - - // Getter - // @return T/F if this iterator is completely done after getting an eof - bool EofHandled() const { return eof_handled_; } - - private: - DatasetOp *current_op_; // The parent operator. We consume from it's children. - int32_t child_idx_; // The specific child this iterator will fetch from. - int32_t worker_id_; // The worker id uses for fetching the child data. - bool end_epoch_; // the flag used when an empty row has been returned. - bool eof_handled_; // T/F if this op got an eof -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASET_ITERATOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.cc deleted file mode 100644 index a6e726112..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.cc +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { -// Construct BarrierOp here, local variables initialized in operator due to tree construction restrictions -BarrierOp::BarrierOp(int32_t op_connector_size, const std::string &condition_name, py::function condition_func) - : PipelineOp(op_connector_size), - clean_up_(false), - eof_(false), - condition_name_(condition_name), - condition_function_(std::move(condition_func)) {} - -// destructor -BarrierOp::~BarrierOp() {} - -// Entry point for Barrier, called by launch() -Status BarrierOp::operator()() { - // The children_num_ parameter needs to be put here - // Synchronize with TaskManager once the thread is created. - TaskManager::FindMe()->Post(); - - // create child iterator, right now this barrier is a pipeline operator - const int32_t worker_id = 0; - const int32_t child_idx = 0; - child_iterator_ = std::make_unique(this, worker_id, child_idx); - - // Loop until eof is true - while (!eof_) { - RETURN_IF_NOT_OK(prepare()); - // read the first row - TensorRow new_row; - RETURN_IF_NOT_OK(getNextTensorRow(&new_row)); - - // If an eof got picked up during the above prepare, then we're done - if (eof_) { - break; - } - - while (!clean_up_) { - // 2 Block - RETURN_IF_NOT_OK(blockCond()); - - MS_LOG(DEBUG) << "Barrier operator finished one row, pushing, cols " << new_row.size() << ", map " - << column_name_id_map_.size() << "."; - RETURN_IF_NOT_OK(out_connector_->Add(std::move(new_row))); - RETURN_IF_NOT_OK(getNextTensorRow(&new_row)); - } - - if (clean_up_) { - MS_LOG(DEBUG) << "Barrier operator sending epoch ending signal."; - // 3 Send the eoe up. - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - } - } - // 4 handle eof - // propagate eof here. - MS_LOG(INFO) << "Barrier operator got EOF, propagating."; - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - return Status::OK(); -} - -// Handles preprocessing of the main loop, used when starting new epoch -Status BarrierOp::prepare() { - MS_LOG(DEBUG) << "Barrier operator prepares for new epoch."; - clean_up_ = false; - // the update code below shouldn't do anything bad if the column name already exists. - return Status::OK(); -} - -// function executes a py_func and blocks until condition becomes true. -Status BarrierOp::blockCond() { - { - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - return Status(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized"); - } - // we have condition name, however the flexibility is in python today - try { - // Invoke python function - py::object ret_py_obj = condition_function_(); - // Process the return value - if (!py::isinstance(ret_py_obj)) { - return Status(StatusCode::kMDPyFuncException, - "Invalid parameter, condition wait function should return boolean, but got " + - std::string(ret_py_obj.get_type().str())); - } - } catch (const py::error_already_set &e) { - return Status(StatusCode::kMDPyFuncException, e.what()); - } - } - return Status::OK(); -} - -// fetches next Barrier row -Status BarrierOp::getNextTensorRow(TensorRow *new_row) { - RETURN_UNEXPECTED_IF_NULL(new_row); - // iterate over all iterators and generate a row - RETURN_IF_NOT_OK((child_iterator_)->FetchNextTensorRow(new_row)); - // add each new row to iterator, check if row is empty, if row from iterator is empty return empty row - if (new_row->empty()) { - // If we did not get a row from any of the children, then it's the end of an epoch and we can move - // to drain state. - MS_LOG(INFO) << "Barrier operator child iterator produced empty row."; - clean_up_ = true; - // If we picked up an eof here, then we are completely done. - if ((child_iterator_)->EofHandled()) { - MS_LOG(INFO) << "Barrier operator iterator got EOF."; - eof_ = true; - } - return Status::OK(); - } - return Status::OK(); -} - -// A function that prints info about the Operator -void BarrierOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nCondition: " << condition_name_ << "\n\n"; - } -} - -// overwrite function and handle eof -Status BarrierOp::EofReceived(int32_t) { - MS_LOG(DEBUG) << "Barrier operator EOF received, do nothing now."; - return Status::OK(); -} - -// overwrite function and handle eoe -Status BarrierOp::EoeReceived(int32_t) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h deleted file mode 100644 index 6f18b6850..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BARRIER_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BARRIER_OP_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -// Forward declare -class ExecutionTree; - -// BarrierOp class implements the Barrier operator. It will block sending of rows until a signal has -// been received. This signal is given from python layer. - -class BarrierOp : public PipelineOp { - public: - // Constructor for BarrierOp - // @param op_connector_size - connector size - // @param condition_name - the condition name associated with this operator - // @param condition_func - the blocking condition check per row - // The reason for this is having other values would complicate how the pipeline behaves with other operators - // One example of such case is having batch after barrier. - BarrierOp(int32_t op_connector_size, const std::string &condition_name, py::function condition_func); - - /// Destructor - ~BarrierOp(); - - Status EofReceived(int32_t) override; - - Status EoeReceived(int32_t) override; - - /// Print function for Barrier - /// @param out - output stream to print to - /// @param show_all - if it should print everything - void Print(std::ostream &out, bool show_all) const override; - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return kBarrierOp; } - - /// Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const BarrierOp &bo) { - bo.Print(out, false); - return out; - } - - /// Class functor operator () override. - /// All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - /// provide the master loop that drives the logic for performing the work - /// @return Status The status code returned - Status operator()() override; - - // Handles preprocessing of the main loop, used when starting new epoch - // @param table - a table of tensors to be moved into a row - Status prepare(); - - // Gets next tensor row and sets control signals - Status getNextTensorRow(TensorRow *new_row); - - /// This function runs the wait function on condition - Status blockCond(); - - private: - // clean up variable - bool clean_up_; - // end of file state, we stop reading data and shut down - bool eof_; - // iterator to pull new rows, we only have one child - std::unique_ptr child_iterator_; - // condition name, to support multiple barriers - std::string condition_name_; - // Function pointer of blocking function - py::function condition_function_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BARRIER_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.cc deleted file mode 100644 index 10210c3b8..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.cc +++ /dev/null @@ -1,1021 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h" - -#include -#include - -#include "utils/ms_utils.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -#endif - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -#ifdef ENABLE_PYTHON -BatchOp::BatchOp(int32_t batch_size, bool drop, bool pad, int32_t op_queue_size, int32_t num_workers, - const std::vector &in_col, const std::vector &out_col, - py::function batch_size_func, py::function batch_map_func, PadInfo pad_map) - : BatchOp(batch_size, drop, pad, op_queue_size, num_workers, in_col, std::move(pad_map)) { - batch_size_func_ = std::move(batch_size_func); - batch_map_func_ = std::move(batch_map_func); - out_col_names_ = out_col; -} -// if PYTHON is disabled. per_batch_map can't be used -#endif -BatchOp::BatchOp(int32_t batch_size, bool drop, bool pad, int32_t op_queue_size, int32_t num_workers, - std::vector cols_to_map, PadInfo pad_map) - : ParallelOp(num_workers, op_queue_size), - start_batch_size_(batch_size), - drop_(drop), - pad_(pad), - in_col_names_(std::move(cols_to_map)), - pad_info_(std::move(pad_map)), - batch_num_(0), - batch_cnt_(0), - python_multiprocessing_runtime_(nullptr) { - // Adjust connector queue size. After batch each row is batch_size times larger - worker_connector_size_ = std::max(1, worker_connector_size_ / start_batch_size_); - if (num_workers == 1) { - // Ensure there are at least 2 queue slots for whole operation. If only 1 worker, increase queue size to 2. - worker_connector_size_ = std::max(2, worker_connector_size_); - } -} - -Status BatchOp::operator()() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - // Initialize callback - RETURN_IF_NOT_OK(callback_manager_.Init(this)); - // Synchronize with TaskManager - TaskManager::FindMe()->Post(); - int64_t ep_step = 0; - int64_t total_step = 0; - int64_t batch_num = 0; - int64_t cnt = 0; - RETURN_IF_NOT_OK(callback_manager_.Begin(CallbackParam(0, ep_step, total_step))); - - TensorRow new_row; - std::unique_ptr table = std::make_unique(); - child_iterator_ = std::make_unique(this, 0, 0); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - int32_t cur_batch_size = 0; - RETURN_IF_NOT_OK(GetBatchSize(&cur_batch_size, CBatchInfo(0, 0, 0))); - while (!child_iterator_->EofHandled()) { - if (op_current_repeats_ % GetOpNumRepeatsPerEpoch() == 0) { - ep_step = 0; - RETURN_IF_NOT_OK(callback_manager_.EpochBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - } - while (!new_row.eoe()) { - // we only call stepBegin when a new batch is starting to be filled. - if (table->empty()) { - ep_step++; - total_step++; - RETURN_IF_NOT_OK(callback_manager_.StepBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - } - if (new_row.eob()) { - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->EmplaceBack( - std::make_pair(std::move(table), CBatchInfo(op_current_epochs_, batch_num++, cnt++)))); - table = std::make_unique(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } else { - (void)table->emplace_back(std::move(new_row)); - // if # of rows is enough to make 1 batch, send it to worker_queue - if (table->size() == static_cast(cur_batch_size)) { - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->EmplaceBack( - std::make_pair(std::move(table), CBatchInfo(op_current_epochs_, batch_num++, cnt++)))); - table = std::make_unique(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - if (!new_row.eoe()) { - RETURN_IF_NOT_OK(GetBatchSize(&cur_batch_size, CBatchInfo(op_current_epochs_, batch_num, cnt))); - } - } else { - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - } - } - // There should be no leftover samples when using batch sampler - CHECK_FAIL_RETURN_UNEXPECTED(cur_batch_size != -1 || table->empty(), - "Batch size cannot be -1 without using batch sampler."); - // Reminder logic, execute only when there is a remainder (table is non empty) and don't drop - if (!drop_ && !table->empty()) { - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->EmplaceBack( - std::make_pair(std::move(table), CBatchInfo(op_current_epochs_, batch_num++, cnt++)))); - } - table = std::make_unique(); // this drops when drop == true - // end of the current epoch, batch_num should start from 0 again - batch_num = 0; - RETURN_IF_NOT_OK( - worker_in_queues_[NextWorkerID()]->EmplaceBack(std::make_pair(nullptr, CBatchInfo(BatchCtrl::kEOE)))); - UpdateRepeatAndEpochCounter(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - if (!new_row.eof()) { - RETURN_IF_NOT_OK(GetBatchSize(&cur_batch_size, CBatchInfo(op_current_epochs_, batch_num, cnt))); - } - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) && defined(ENABLE_PYTHON) - if ((num_workers_ > 1 || batch_map_func_) && GetMemoryUsage() > MAX_MEMORY_USAGE_THRESHOLD) { - MS_LOG(WARNING) << "Memory consumption is more than " << (GetMemoryUsage() * 100) << "%, " - << "which may cause OOM. Please reduce num_parallel_workers size / " - << "optimize 'per_batch_map' function / other python data preprocess function to " - << "reduce memory usage."; - } -#endif - } // end of EofHandled() == false - RETURN_IF_NOT_OK( - worker_in_queues_[NextWorkerID()]->EmplaceBack(std::make_pair(nullptr, CBatchInfo(BatchCtrl::kEOF)))); - // EOF received, send quit signal to all workers - for (int32_t ind = 0; ind < num_workers_; ind++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - return Status::OK(); -} - -void BatchOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [batch size: " << start_batch_size_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nStart batch size: " << start_batch_size_ << "\nDrop remainder: " << (drop_ ? "yes" : "no") << "\n\n"; - } -} - -Status BatchOp::BatchRows(const std::unique_ptr *tensor_row_dequeue, TensorRow *batched_tensor_row, - bool concat_batch, bool contains_per_batch_map) { - RETURN_UNEXPECTED_IF_NULL(tensor_row_dequeue); - RETURN_UNEXPECTED_IF_NULL(batched_tensor_row); - auto batch_size = (*tensor_row_dequeue)->size(); - if (batch_size == 1) { - *batched_tensor_row = std::move((*tensor_row_dequeue)->front()); - (*tensor_row_dequeue)->pop_front(); - - for (const auto &tensor : (*batched_tensor_row)) { - // If concat batch rows, the result should not be expend dimension. - if (!concat_batch) { -#ifdef ENABLE_PYTHON - if (tensor->type() == DataType::DE_PYTHON) { - py::gil_scoped_acquire gil_acquire; - py::dict python_dict; - RETURN_IF_NOT_OK(tensor->GetDataAsPythonObject(&python_dict)); - for (const auto &[key, value] : python_dict) { - py::list list; - list.append(value); - try { - python_dict[key] = py::array(list); - } catch (const py::error_already_set &e) { - RETURN_STATUS_UNEXPECTED("Failed to batch data in type of Python dictionary: " + std::string(e.what())); - } - } - } else { - RETURN_IF_NOT_OK(tensor->ExpandDim(0)); - } -#else - RETURN_IF_NOT_OK(tensor->ExpandDim(0)); -#endif - } - } - return Status::OK(); - } - - auto num_columns = (*tensor_row_dequeue)->front().size(); - for (size_t i = 0; i < num_columns; i++) { - std::shared_ptr batched_tensor; - RETURN_IF_NOT_OK(ConvertRowsToTensor(tensor_row_dequeue, &batched_tensor, batch_size, i, contains_per_batch_map)); - batched_tensor_row->emplace_back(std::move(batched_tensor)); - } - - return Status::OK(); -} - -Status CopyTensorToBatch(const std::shared_ptr &element_tensor, std::shared_ptr *batched_tensor, - size_t index) { - RETURN_UNEXPECTED_IF_NULL(batched_tensor); - // ConvertRowsToTensor has confirmed that the shape of element_tensor matches the expected shape of batch_tensor - auto element_size = static_cast(element_tensor->SizeInBytes()); - auto src = reinterpret_cast(element_tensor->GetBuffer()); - auto dest = reinterpret_cast((*batched_tensor)->GetMutableBuffer() + index * element_size); - if (element_size < SECUREC_MEM_MAX_LEN) { - errno_t copy_status = memcpy_s(dest, element_size, src, element_size); - CHECK_FAIL_RETURN_UNEXPECTED(copy_status == EOK, - "Failed to copy tensor to batch, got error_t: " + std::to_string(copy_status)); - } else { - void *ptr = std::memcpy(dest, src, element_size); - CHECK_FAIL_RETURN_UNEXPECTED(ptr == dest, "Failed to copy tensor to batch."); - } - return Status::OK(); -} - -Status BatchOp::ConvertRowsToTensor(const std::unique_ptr *tensor_row_dequeue, - std::shared_ptr *batched_tensor, dsize_t batch_size, size_t column_index, - bool contains_per_batch_map) { - RETURN_UNEXPECTED_IF_NULL(tensor_row_dequeue); - RETURN_UNEXPECTED_IF_NULL(batched_tensor); - std::shared_ptr first_tensor = (*tensor_row_dequeue)->at(0).at(column_index); // first row - TensorShape first_shape = first_tensor->shape(); - DataType first_type = first_tensor->type(); - TensorShape new_shape = first_shape.PrependDim(static_cast(batch_size)); - - std::shared_ptr new_tensor; - if (first_type.IsNumeric()) { // numeric tensor - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, first_type, &new_tensor)); - for (auto row_index = 0; row_index < batch_size; ++row_index) { - const std::shared_ptr &old_tensor = (**tensor_row_dequeue)[row_index][column_index]; - // check the newly popped rows have the same dim and type as the first - if (old_tensor->shape() == first_shape && old_tensor->type() == first_type) { - if (new_shape.NumOfElements() != 0) { - RETURN_IF_NOT_OK(CopyTensorToBatch(old_tensor, &new_tensor, row_index)); - } - // Don't do anything if the tensor has no data - } else if (old_tensor->shape() != first_shape) { // newly popped rows have different dim - std::stringstream shape1, shape2; - first_shape.Print(shape1); - old_tensor->shape().Print(shape2); - RETURN_STATUS_UNEXPECTED("Cannot batch tensors with different shapes in column " + - std::to_string(column_index) + ". First element had shape " + shape1.str() + - " and this element had shape " + shape2.str()); - } else { // newly popped rows have different type - std::string type1; - std::string type2; - type1 = first_type.ToString(); - type2 = old_tensor->type().ToString(); - RETURN_STATUS_UNEXPECTED("Cannot batch tensors with different types in column " + std::to_string(column_index) + - ". First element had type " + type1 + " and this element had type " + type2); - } - } -#ifdef ENABLE_PYTHON - } else if (first_type.IsPython()) { - // handle python dictionary columns differently: - // each column of new batch will be a python dictionary where each key stores - // all values of corresponding rows in a python list if user has provided a per_batch_map. - // If no per_batch_map is provided, all values of that key will be combined in a single NumPy array. - // and we will error out if that is not feasible. - { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - try { - py::dict new_dict; - size_t num_keys = 0; - for (size_t row_index = 0; row_index < batch_size; ++row_index) { - std::shared_ptr old_tensor = (**tensor_row_dequeue)[row_index][column_index]; - py::dict old_dict; - RETURN_IF_NOT_OK(old_tensor->GetDataAsPythonObject(&old_dict)); - if (row_index == 0) { - num_keys = py::len(old_dict); - for (auto key_val : old_dict) { - py::list li; - li.append(key_val.second); - new_dict[key_val.first] = li; - } - } else { - CHECK_FAIL_RETURN_UNEXPECTED( - num_keys == py::len(old_dict), - "Failed to create a batch since number of key/value pairs in dictionaries do not match. First row: " + - std::to_string(num_keys) + ", current row: " + std::to_string(py::len(new_dict))); - for (auto key_val : old_dict) { - CHECK_FAIL_RETURN_UNEXPECTED(new_dict.contains(key_val.first), - "Python dictionary keys do not match when creating a batch: " + - py::str(key_val.first).cast() + - " was not found in previous rows."); - py::list li = new_dict[key_val.first]; - li.append(key_val.second); - } - } - } - if (contains_per_batch_map) { - RETURN_IF_NOT_OK(Tensor::CreateFromPythonObject(new_dict, &new_tensor)); - } else { // convert to NumPy array - py::dict np_dict; - for (auto item : new_dict) { - py::list li = new_dict[item.first]; - py::array arr = py::array(li); - CHECK_FAIL_RETURN_UNEXPECTED( - arr.dtype() != py::dtype("object_"), - "Batch: failed to create a NumPy array with primitive types for the dictionary objects in column " + - std::to_string(column_index) + ", key: '" + py::str(item.first).cast() + - "'.\nIf you want a customized array, define a custom 'per_batch_map' function."); - np_dict[item.first] = arr; - } - RETURN_IF_NOT_OK(Tensor::CreateFromPythonObject(np_dict, &new_tensor)); - } - } catch (const py::error_already_set &e) { - RETURN_STATUS_UNEXPECTED("Failed to batch Python dictionary: " + std::string(e.what())); - } - } -#endif - } else { // handle string column differently - std::vector strings; - strings.reserve(batch_size); - for (dsize_t row_index = 0; row_index < batch_size; ++row_index) { - std::shared_ptr old_tensor = (**tensor_row_dequeue)[row_index][column_index]; - for (auto itr = old_tensor->begin(); itr != old_tensor->end(); ++itr) { - (void)strings.emplace_back(*itr); - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(strings, new_shape, first_type, &new_tensor)); - } - *batched_tensor = std::move(new_tensor); - return Status::OK(); -} - -TensorRow TimeSumOfBatch(const TensorQTable &tensor_deque) { - TensorRow collection; - if (!collection.Timer()->Enabled()) { - return collection; - } - - auto first_row = (tensor_deque).front(); - auto timer = first_row.Timer(); - std::vector pipeline_op = timer->op_order_; - - for (auto &op : pipeline_op) { - double time_max = 0; - double time_min = std::numeric_limits::max(); - double time_avg = 0; - double io_time_max = 0; - double io_time_min = std::numeric_limits::max(); - double io_time_avg = 0; - bool has_io_time = false; - for (auto &row : tensor_deque) { - double row_time = row.Timer()->time_table_[op][RowTimer::kWorkerTime].front(); - time_max = (row_time > time_max) ? row_time : time_max; - time_min = (row_time < time_min) ? row_time : time_min; - time_avg += row_time / tensor_deque.size(); - if (row.Timer()->time_table_[op].find(RowTimer::kIOTime) != row.Timer()->time_table_[op].end()) { - double mr_io_time = row.Timer()->time_table_[op][RowTimer::kIOTime].front(); - io_time_max = (mr_io_time > io_time_max) ? mr_io_time : io_time_max; - io_time_min = (mr_io_time < io_time_min) ? mr_io_time : io_time_min; - io_time_avg += mr_io_time / tensor_deque.size(); - has_io_time = true; - } - } - collection.TimerRecord(op, RowTimer::kMaxTime, {time_max}); - collection.TimerRecord(op, RowTimer::kMinTime, {time_min}); - collection.TimerRecord(op, RowTimer::kWorkerTime, {time_avg}); - // deal with dataset.batch.batch case - double accumulate_row = tensor_deque.size(); - if (timer->time_table_[op].find(RowTimer::kRowCount) != timer->time_table_[op].end()) { - accumulate_row *= timer->time_table_[op][RowTimer::kRowCount].front(); - } - collection.TimerRecord(op, RowTimer::kRowCount, {accumulate_row}); - if (has_io_time) { - collection.TimerRecord(op, RowTimer::kIOTime, {io_time_avg}); - collection.TimerRecord(op, RowTimer::kMaxIOTime, {io_time_max}); - collection.TimerRecord(op, RowTimer::kMinIOTime, {io_time_min}); - } - } - return collection; -} - -Status BatchOp::WorkerEntry(int32_t workerId) { - TaskManager::FindMe()->Post(); - // let Python layer know the worker id of this thread - if (python_multiprocessing_runtime_ != nullptr) { - python_multiprocessing_runtime_->set_thread_to_worker(workerId); - } - std::pair, CBatchInfo> table_pair; - - uint64_t start_time = GetSyscnt(); - double row_timer_start = 0; - RETURN_IF_NOT_OK(worker_in_queues_[workerId]->PopFront(&table_pair)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerGet", start_time)); - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - - while (table_pair.second.ctrl_ != BatchCtrl::kQuit) { - if (table_pair.second.ctrl_ == BatchCtrl::kNoCtrl) { - TensorRow batched_tensor_row; - auto collection = TimeSumOfBatch(*(table_pair.first)); - RETURN_IF_NOT_OK(MakeBatchedRow(std::move(table_pair), &batched_tensor_row)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time)); - batched_tensor_row.TimerRecord(NameWithID(), RowTimer::kWorkerTime, {GetMilliTimeStamp() - row_timer_start}, - &collection); - RETURN_IF_NOT_OK(worker_out_queues_[workerId]->EmplaceBack(std::move(batched_tensor_row))); - } else if (table_pair.second.ctrl_ == BatchCtrl::kEOE) { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time)); - RETURN_IF_NOT_OK(worker_out_queues_[workerId]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOE))); - } else if (table_pair.second.ctrl_ == BatchCtrl::kEOF) { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time)); - RETURN_IF_NOT_OK(worker_out_queues_[workerId]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOF))); - } else if (table_pair.second.ctrl_ == BatchCtrl::kWait) { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time)); - RETURN_IF_NOT_OK(worker_out_queues_[workerId]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagWait))); - RETURN_IF_NOT_OK(TaskManager::FindMe()->Wait()); // wait for auto tune update workers successful - TaskManager::FindMe()->Clear(); - } - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[workerId]->PopFront(&table_pair)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerGet", start_time)); - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - } - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, - {{"TensorRowFlags", TensorRow(TensorRow::kFlagQuit).FlagName()}})); - -#ifdef ENABLE_PYTHON - // batch operation with per_batch_map use global executor in Python Layer to run transform in eager mode - // release the executor in the current thread when the thread is done - if (python_multiprocessing_runtime_ == nullptr) { - if (batch_map_func_) { - py::gil_scoped_acquire gil_acquire; - (void)batch_map_func_.attr("release_resource")(); - } - } -#endif - - return Status::OK(); -} - -Status BatchOp::MakeBatchedRow(std::pair, CBatchInfo> tensor_info_pair, - TensorRow *batched_tensor_row) { - RETURN_UNEXPECTED_IF_NULL(tensor_info_pair.first); - bool concat_batch = false; - bool contains_per_batch_map = false; -#ifdef ENABLE_PYTHON - if (batch_map_func_) { - contains_per_batch_map = true; - RETURN_IF_NOT_OK(MapColumns(&tensor_info_pair, &concat_batch)); - } // pass it through pyfunc -#endif - if (pad_) { - RETURN_IF_NOT_OK(PadColumns(&tensor_info_pair.first, pad_info_, column_name_id_map_)); - } // do padding if needed - RETURN_IF_NOT_OK(BatchRows(&tensor_info_pair.first, batched_tensor_row, concat_batch, contains_per_batch_map)); - return Status::OK(); -} - -Status BatchOp::EofReceived(int32_t) { return Status::OK(); } - -Status BatchOp::EoeReceived(int32_t) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -#ifdef ENABLE_PYTHON -Status BatchOp::MapColumns(std::pair, CBatchInfo> *table_pair, bool *concat_batch) { - RETURN_UNEXPECTED_IF_NULL(table_pair); - RETURN_UNEXPECTED_IF_NULL(table_pair->first); - std::unique_ptr in_q_table = std::move(table_pair->first); - size_t num_rows = in_q_table->size(); - TensorTable in_cols(in_col_names_.size(), TensorRow(num_rows, nullptr)), out_cols; - - std::unordered_map in_col_name_id; // name of columns that need to be fed to per-batch_map - for (size_t i = 0; i < in_col_names_.size(); i++) { - (void)in_col_name_id.insert({in_col_names_[i], i}); - } - - for (const auto &itr : child_map_) { - auto col_itr = in_col_name_id.find(itr.first); - if (col_itr != in_col_name_id.end()) { // col needs to be prepared for per_batch_map - for (size_t i = 0; i < num_rows; i++) { - in_cols[col_itr->second][i] = std::move((*in_q_table)[i][itr.second]); - } - } - } - - RETURN_IF_NOT_OK(InvokeBatchMapFunc(&in_cols, &out_cols, table_pair->second, concat_batch)); - - // If concat batch rows, the num_rows should be 1. - if (*concat_batch) { - num_rows = 1; - } - - auto out_q_table = std::make_unique(num_rows, TensorRow(column_name_id_map_.size(), nullptr)); - - for (const auto &itr : child_map_) { - auto col_itr = in_col_name_id.find(itr.first); - if (col_itr == in_col_name_id.end()) { // col needs to be prepared for per_batch_map - // col needs to be placed into the out table - size_t col_id = column_name_id_map_[itr.first]; - if (*concat_batch) { - std::shared_ptr new_tensor; - RETURN_IF_NOT_OK(ConvertRowsToTensor(&in_q_table, &new_tensor, in_q_table->size(), - static_cast(itr.second), static_cast(batch_map_func_))); - (*out_q_table)[0][col_id] = std::move(new_tensor); - } else { - for (size_t i = 0; i < num_rows; i++) { - (*out_q_table)[i][col_id] = std::move((*in_q_table)[i][itr.second]); - } - } - } - } - in_q_table.reset(); // release the input table - - for (size_t i = 0; i < out_cols.size(); i++) { - size_t col_id = column_name_id_map_[out_col_names_[i]]; - size_t row_id = 0; - CHECK_FAIL_RETURN_UNEXPECTED(num_rows == out_cols[i].size(), - "Invalid data, column: " + out_col_names_[i] + - " expects: " + std::to_string(num_rows) + - " rows returned from 'per_batch_map', got: " + std::to_string(out_cols[i].size())); - for (auto &t_row : *out_q_table) { - t_row[col_id] = out_cols[i][row_id++]; - } - } - - table_pair->first = std::move(out_q_table); - return Status::OK(); -} -#endif - -Status BatchOp::GetBatchSize(int32_t *batch_size, CBatchInfo info) { - RETURN_UNEXPECTED_IF_NULL(batch_size); -#ifdef ENABLE_PYTHON - if (batch_size_func_) { - RETURN_IF_NOT_OK(InvokeBatchSizeFunc(batch_size, info)); - } else { - (*batch_size) = start_batch_size_; - } -#else - (*batch_size) = start_batch_size_; -#endif - return Status::OK(); -} - -#ifdef ENABLE_PYTHON -Status BatchOp::InvokeBatchSizeFunc(int32_t *batch_size, CBatchInfo info) { - RETURN_UNEXPECTED_IF_NULL(batch_size); - { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - return Status(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized."); - } - try { - py::object size = batch_size_func_(info); - *batch_size = size.cast(); - if (*batch_size <= 0) { - return Status(StatusCode::kMDPyFuncException, - "Invalid batch_size function, 'batch_size' function should return an integer greater than 0, " - "but got: " + - std::to_string(*batch_size)); - } - } catch (const py::error_already_set &e) { - return Status(StatusCode::kMDPyFuncException, e.what()); - } catch (const py::cast_error &e) { - return Status( - StatusCode::kMDPyFuncException, - "Invalid batch_size function, the return value of batch_size function cast failed: " + std::string(e.what())); - } - } - return Status(StatusCode::kSuccess, "batch_size function call succeeded."); -} - -Status BatchOp::InvokeBatchMapFunc(TensorTable *input, TensorTable *output, CBatchInfo info, bool *concat_batch) { - RETURN_UNEXPECTED_IF_NULL(input); - RETURN_UNEXPECTED_IF_NULL(output); - { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - return Status(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized."); - } - try { - // Prepare batch map call back parameters - py::tuple input_args(input->size() + 1); - for (size_t i = 0; i < input->size(); i++) { // iterate over columns - std::vector column_batch; - for (std::shared_ptr t : input->at(i)) { // iterate over rows - if (t->type().IsPython()) { - py::dict new_data; - RETURN_IF_NOT_OK(t->GetDataAsPythonObject(&new_data)); - column_batch.push_back(new_data); - } else { - py::array np_array; - RETURN_IF_NOT_OK(t->GetDataAsNumpy(&np_array)); - column_batch.push_back(std::move(np_array)); - } - } - input_args[i] = column_batch; - } - input_args[input->size()] = info; - // Invoke batch map func - py::object ret_py_obj = batch_map_func_(*input_args); - - if (ret_py_obj.is_none()) { - std::string error_msg = - "The subprocess of dataset may exit unexpected or be killed, " - "main process will exit. If this is not an artificial operation, you can use " - "mindspore.dataset.config.set_enable_watchdog(False) to block this error."; - RETURN_STATUS_UNEXPECTED("Got None from Python object. " + error_msg); - } - - // return value from per_batch_map can be: - // case 1: int, float, str, bytes, np.ndarray, dict - // case 2: item1, item2, item3, ... - py::tuple ret_tuple; - if (!py::isinstance(ret_py_obj)) { - ret_tuple = py::make_tuple(ret_py_obj); - } else { - ret_tuple = py::cast(ret_py_obj); - } - - // Parse batch map return value - CHECK_FAIL_RETURN_UNEXPECTED(py::isinstance(ret_tuple), - "Invalid per_batch_map, 'per_batch_map' function should return a tuple, but got " + - std::string(ret_py_obj.get_type().str())); - CHECK_FAIL_RETURN_UNEXPECTED(ret_tuple.size() == out_col_names_.size(), - "Invalid per_batch_map, the number of columns returned in 'per_batch_map' function " - "should be " + - std::to_string(out_col_names_.size()) + - " , but got: " + std::to_string(ret_tuple.size())); - bool all_array_or_dict = true; - for (size_t i = 0; i < ret_tuple.size(); i++) { - if (!py::isinstance(ret_tuple[i]) && !py::isinstance(ret_tuple[i])) { - all_array_or_dict = false; - break; - } - } - *concat_batch = all_array_or_dict; - for (size_t i = 0; i < ret_tuple.size(); i++) { - TensorRow output_batch; - // If user returns a type that is neither a list nor a Python dictionary, issue a error msg. - if (!py::isinstance(ret_tuple[i]) && !py::isinstance(ret_tuple[i])) { - MS_LOG(INFO) << "column: " << out_col_names_[i] - << " returned by per_batch_map is not a list nor a Python dict, " - << "this could lead to conversion failure."; - } - - if (*concat_batch) { - // If concat batch rows, the batch map function result should be in 1 row. - std::shared_ptr out; - if (py::isinstance(ret_tuple[i])) { - RETURN_IF_NOT_OK(Tensor::CreateFromPythonObject(py::cast(ret_tuple[i]), &out)); - } else { - RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(py::cast(ret_tuple[i]), &out)); - } - output_batch.push_back(std::move(out)); - } else { - CHECK_FAIL_RETURN_UNEXPECTED( - !py::isinstance(ret_tuple[i]), - "Failed to convert rows: mismatched types returned from per_batch_map function. If different types are " - "returned, all of them should be convertible to Python lists. Got: Python dict"); - py::list output_list = py::cast(ret_tuple[i]); - for (size_t j = 0; j < output_list.size(); j++) { - std::shared_ptr out; - if (py::isinstance(output_list[j])) { - RETURN_IF_NOT_OK(Tensor::CreateFromPythonObject(py::cast(output_list[j]), &out)); - } else { - RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(py::cast(output_list[j]), &out)); - } - output_batch.push_back(std::move(out)); - } - } - output->push_back(std::move(output_batch)); - } - } catch (const py::error_already_set &e) { - return Status(StatusCode::kMDPyFuncException, e.what()); - } catch (const py::cast_error &e) { - return Status(StatusCode::kMDPyFuncException, - "Invalid per_batch_map, the return value of 'per_batch_map' function cast to py::tuple failed: " + - std::string(e.what())); - } - } - return Status::OK(); -} -#endif - -Status BatchOp::PadColumns(const std::unique_ptr *table, const PadInfo &pad_info, - const std::unordered_map &column_name_id_map) { - RETURN_UNEXPECTED_IF_NULL(table); // placeholder for now, might need this in the future - CHECK_FAIL_RETURN_UNEXPECTED( - (*table)->front().size() == column_name_id_map.size(), - "Invalid parameter, size of column_name_id_map must be equal to num of data columns. map size: " + - std::to_string(column_name_id_map.size()) + ", column nums: " + std::to_string((*table)->front().size())); - std::vector> pad_vals(column_name_id_map.size(), - nullptr); // value to pad each column's tensor with, default nullptr - std::set pad_cols; - // padded_shape provided by user, maximum shapes of current batch of tensors - std::vector> pad_shapes(column_name_id_map.size()), max_shapes(column_name_id_map.size()); - RETURN_IF_NOT_OK(UnpackPadInfo(pad_info, column_name_id_map, &pad_cols, &pad_vals, &pad_shapes)); - - // init each shape in max_shape to {-1,-1...} init each unspecified shape in pad_shape to -1 as well - for (size_t col_id : pad_cols) { - max_shapes[col_id] = std::vector((*table)->front()[col_id]->Rank(), -1); - if (pad_shapes[col_id].empty()) { - pad_shapes[col_id] = max_shapes[col_id]; // fill pad shape with -1 - } - CHECK_FAIL_RETURN_UNEXPECTED( - pad_shapes[col_id].size() == max_shapes[col_id].size(), - "Invalid pad_info, rank of pad_shape must be equal to rank of specified column. pad_shapes rank:" + - std::to_string(pad_shapes[col_id].size()) + ", column rank: " + std::to_string(max_shapes[col_id].size())); - } - - // calculate maximum shape for each column that needs to be padded - for (const TensorRow &row : **table) { // iterator each row in a batch - for (size_t col_id : pad_cols) { // iterator each tensor in a row - CHECK_FAIL_RETURN_UNEXPECTED( - row[col_id]->Rank() == max_shapes[col_id].size(), - "Invalid data, data to be padded together need to have the same rank, got shape 1: " + - std::to_string(row[col_id]->Rank()) + ", shape 2: " + std::to_string(max_shapes[col_id].size())); - for (size_t dim = 0; dim < row[col_id]->Rank(); dim++) { // pick the largest number in each dimension - max_shapes[col_id][dim] = std::max(max_shapes[col_id][dim], row[col_id]->shape()[dim]); - } - } - } - - // if user sets a dimension to -1 (None in python), use the max value for current dimension - for (size_t col_id : pad_cols) { - for (size_t dim = 0; dim < pad_shapes[col_id].size(); dim++) { - if (pad_shapes[col_id][dim] < 0) { - pad_shapes[col_id][dim] = max_shapes[col_id][dim]; - } - } - } - - // call pad on each tensor that needs to be padded - for (TensorRow &row : **table) { - for (size_t col_id : pad_cols) { - std::shared_ptr pad_tensor; - RETURN_IF_NOT_OK(PadEnd(row[col_id], &pad_tensor, pad_shapes[col_id], pad_vals[col_id])); - row[col_id] = pad_tensor; - } - } - return Status::OK(); -} - -Status BatchOp::UnpackPadInfo(const PadInfo &pad_info, - const std::unordered_map &column_name_id_map, - std::set *pad_cols, std::vector> *pad_vals, - std::vector> *pad_shapes) { - RETURN_UNEXPECTED_IF_NULL(pad_cols); - RETURN_UNEXPECTED_IF_NULL(pad_vals); - RETURN_UNEXPECTED_IF_NULL(pad_shapes); - if (pad_info.empty()) { // if pad_info empty, pad every columns automatically - for (size_t col_id = 0; col_id < column_name_id_map.size(); col_id++) { - (void)pad_cols->insert(col_id); - } - } else { - for (const auto &p : pad_info) { - auto location = column_name_id_map.find(p.first); - CHECK_FAIL_RETURN_UNEXPECTED(location != column_name_id_map.end(), - "Invalid pad_info, column name: " + p.first + " does not exist."); - auto col_id = static_cast(location->second); - CHECK_FAIL_RETURN_UNEXPECTED( - col_id < pad_vals->size() && col_id < pad_shapes->size(), - "Invalid pad_info, column name should be match with the size of pad value and pad shape, but got " - "column name: " + - p.first + ", the size of pad value: " + std::to_string(pad_vals->size()) + - " and the size of pad shape: " + std::to_string(pad_shapes->size()) + "."); - (void)pad_cols->insert(col_id); - (*pad_vals)[col_id] = p.second.second; // set pad values - (*pad_shapes)[col_id] = p.second.first.AsVector(); // empty vector if shape is unknown - } - } - return Status::OK(); -} - -Status BatchOp::ComputeColMap() { - CHECK_FAIL_RETURN_UNEXPECTED(child_.size() == 1, - "Invalid batch, batch operator can't be used as a single operator, " - "should be preceded by an operator that reads data, for example, " - "ds1 = ds.ImageFolderDataset().batch()."); - CHECK_FAIL_RETURN_UNEXPECTED(!(child_[0]->column_name_id_map().empty()), - "Invalid data, the column of the previous operator of the batch cannot be empty."); - -// when per_batch_map is set and input_columns is not specified: enter the if branch of ENABLE_PYTHON -// when per_batch_map is set and input_columns is specified: will not enter the if branch of ENABLE_PYTHON -// and in_col_names_.empty() -// when per_batch_map is not set and input_columns is not specified: enter the if branch of in_col_names_.empty() -// when per_batch_map is not set and input_columns is specified: ERROR -#ifdef ENABLE_PYTHON - // when per_batch_map is set and input_columns is not specified, input_columns will be automatically speculated - if (batch_map_func_ && in_col_names_.empty()) { - auto column_name = child_[0]->column_name_id_map(); - std::vector> tmp; - (void)std::copy(column_name.begin(), column_name.end(), std::back_inserter(tmp)); - std::sort(tmp.begin(), tmp.end(), - [=](const std::pair &a, const std::pair &b) { - return a.second < b.second; - }); - for (auto &it : tmp) { - (void)in_col_names_.emplace_back(it.first); - } - } -#endif - - if (in_col_names_.empty()) { // if per_batch_map is not set, do not need to deal with out_col_names - column_name_id_map_ = child_[0]->column_name_id_map(); - return Status::OK(); - } - - // from this point onward, per_batch_map is needed, therefore, child_map_ must be set - child_map_ = child_[0]->column_name_id_map(); - - // check all input columns exist - for (const auto &col : in_col_names_) { - CHECK_FAIL_RETURN_UNEXPECTED(child_map_.find(col) != child_map_.end(), - "Invalid input_columns, '" + col + "' of 'input_columns' doesn't exist."); - } - - // following logic deals with per_batch_map - bool col_name_flag = (out_col_names_.empty() || out_col_names_ == in_col_names_); // true if col name is unchanged - - // column names are unchanged - if (col_name_flag) { - if (out_col_names_.empty()) { - out_col_names_ = in_col_names_; - } - column_name_id_map_ = child_map_; - return Status::OK(); - } - // column names are changed from this point onward, this map is the child_map without input cols for per_batch_map - auto child_map_no_in_col = child_map_; - - for (const auto &col : in_col_names_) { - (void)child_map_no_in_col.erase(col); - } - - // col names are changed - if (out_col_names_.size() == in_col_names_.size()) { // column names changed, but same number of columns - // the following code rename the input keys to output keys. ["a","b"] -> ["b", "a"] is allowed - column_name_id_map_ = child_map_no_in_col; - for (auto i = 0; i < in_col_names_.size(); i++) { - column_name_id_map_[out_col_names_[i]] = child_map_[in_col_names_[i]]; - } - } else { // number of columns are different, put the output column names first, then the original ones - for (const std::string &col : out_col_names_) { - (void)column_name_id_map_.insert({col, column_name_id_map_.size()}); - } - for (const auto &itr : child_map_no_in_col) { - (void)column_name_id_map_.insert({itr.first, column_name_id_map_.size()}); - } - } - - if (column_name_id_map_.size() != (child_map_no_in_col.size() + out_col_names_.size())) { - const std::string prefix_str = std::string("["); - auto column_no_in_col = std::accumulate( - child_map_no_in_col.begin(), child_map_no_in_col.end(), prefix_str, - [](const std::string &str, const std::pair &p) { return str + p.first + ","; }); - column_no_in_col += "]"; - auto column_out = - std::accumulate(out_col_names_.begin(), out_col_names_.end(), prefix_str, - [](const std::string &str, const std::string &out_col) { return str + out_col + ","; }); - column_out += "]"; - RETURN_STATUS_UNEXPECTED( - "Invalid output_columns, columns that are not involved in 'per_batch_map' should not be " - "in output_columns, but got columns that are not in input_columns: " + - column_no_in_col + ", output_columns: " + column_out + "."); - } - return Status::OK(); -} - -int64_t BatchOp::GetTreeBatchSize() { -#ifdef ENABLE_PYTHON - if (batch_size_func_) { - return -1; - } -#endif - return start_batch_size_; -} - -void BatchOp::UpdateCounterAndSendEOE(TensorRow *const row) { - *row = TensorRow(TensorRow::kFlagEOE); - UpdateRepeatAndEpochCounter(); - batch_num_ = 0; -} - -Status BatchOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - row->clear(); - if (eoe_received_) { - UpdateCounterAndSendEOE(row); - eoe_received_ = false; - return Status::OK(); - } - std::unique_ptr table = std::make_unique(); - // Note: Create table_pair since needed for per_batch_support when calling MapColumns() - std::pair, CBatchInfo> table_pair = - std::make_pair(std::move(table), CBatchInfo(op_current_epochs_, batch_num_, batch_cnt_)); - child_iterator_ = std::make_unique(this, 0, 0); - int32_t cur_batch_size = 0; - RETURN_IF_NOT_OK(GetBatchSize(&cur_batch_size, table_pair.second)); - if (cur_batch_size == -1) { - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eoe()) { - UpdateCounterAndSendEOE(row); - return Status::OK(); - } - if (new_row.eof()) { - *row = TensorRow(TensorRow::kFlagEOF); - return Status::OK(); - } - while (!new_row.eob()) { - CHECK_FAIL_RETURN_UNEXPECTED(!new_row.empty(), "Expect EOB flag, but got: " + new_row.FlagName()); - (void)table_pair.first->emplace_back(new_row); - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - } - } else { - for (int i = 0; i < cur_batch_size; i++) { - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eoe()) { - if (drop_ || table_pair.first->empty()) { - UpdateCounterAndSendEOE(row); - return Status::OK(); - } else { - eoe_received_ = true; - } - break; - } - if (!new_row.empty()) { - (void)table_pair.first->emplace_back(new_row); - if (table_pair.first->size() == static_cast(cur_batch_size)) { - break; - } - } else { - if (drop_ || table_pair.first->empty()) { - table_pair.first = std::make_unique(); // this drops when drop == true - } - } - } - } - if (!table_pair.first->empty()) { - table_pair.second = CBatchInfo(op_current_epochs_, batch_num_, batch_cnt_); - // Generate row with batched tensors - RETURN_IF_NOT_OK(MakeBatchedRow(std::move(table_pair), row)); - // Increment batch counters - batch_cnt_++; - batch_num_++; - } - return Status::OK(); -} - -Status BatchOp::SendWaitFlagToWorker(int32_t worker_id) { - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->EmplaceBack(std::make_pair(nullptr, CBatchInfo(BatchCtrl::kWait)))); - return Status::OK(); -} - -Status BatchOp::SendQuitFlagToWorker(int32_t worker_id) { - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->EmplaceBack(std::make_pair(nullptr, CBatchInfo(BatchCtrl::kQuit)))); - return Status::OK(); -} - -Status BatchOp::AddNewWorkers(int32_t num_new_workers) { - RETURN_IF_NOT_OK(ParallelOp::AddNewWorkers(num_new_workers)); - if (python_multiprocessing_runtime_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(num_new_workers > 0, "Number of workers added should be greater than 0."); - python_multiprocessing_runtime_->add_new_workers(num_new_workers); - } - return Status::OK(); -} - -Status BatchOp::RemoveWorkers(int32_t num_workers) { - RETURN_IF_NOT_OK(ParallelOp::RemoveWorkers(num_workers)); - if (python_multiprocessing_runtime_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(num_workers > 0, "Number of workers removed should be greater than 0."); - python_multiprocessing_runtime_->remove_workers(num_workers); - } - return Status::OK(); -} - -void BatchOp::SetPythonMp(std::shared_ptr python_multiprocessing_runtime) { - python_multiprocessing_runtime_ = std::move(python_multiprocessing_runtime); -} - -Status BatchOp::Launch() { - // Launch Python multiprocessing. This will create the MP pool and shared memory if needed. - if (python_multiprocessing_runtime_) { - MS_LOG(DEBUG) << "Launch Python Multiprocessing for BatchOp:" << id(); - python_multiprocessing_runtime_->launch(id()); - } - return DatasetOp::Launch(); -} - -Status BatchOp::Terminate() { - // Terminate Python multiprocessing. This will stop the MP pooa. - if (python_multiprocessing_runtime_) { - MS_LOG(INFO) << "Terminate Python Multiprocessing for BatchOp:" << id(); - python_multiprocessing_runtime_->terminate(); - } - return Status::OK(); -} - -std::vector BatchOp::GetMPWorkerPIDs() const { - if (python_multiprocessing_runtime_ != nullptr) { - return python_multiprocessing_runtime_->get_pids(); - } - return DatasetOp::GetMPWorkerPIDs(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h deleted file mode 100644 index fb44999eb..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BATCH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BATCH_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -using PadInfo = std::map>>; - -enum BatchCtrl : int8_t { kNoCtrl = 0, kEOE = 1, kEOF = 2, kQuit = 3, kWait = 4 }; - -// Parameters associate with one batch. -// This struct is used for both internal control and python callback. -// This struct is bound to python with read-only access. -struct CBatchInfo { - CBatchInfo(int64_t ep, int64_t bat, int64_t cur, BatchCtrl ctrl) - : epoch_num_(ep), batch_num_(bat), total_batch_num_(cur), ctrl_(ctrl) {} - CBatchInfo(int64_t ep, int64_t bat, int64_t cur) : CBatchInfo(ep, bat, cur, BatchCtrl::kNoCtrl) {} - CBatchInfo() : CBatchInfo(0, 0, 0, BatchCtrl::kNoCtrl) {} - explicit CBatchInfo(BatchCtrl ctrl) : CBatchInfo(0, 0, 0, ctrl) {} - int64_t epoch_num_; // i-th epoch. i starts from 0 - int64_t batch_num_; // i-th batch since the start of current epoch. i starts from 0 - int64_t total_batch_num_; // i-th batch since the start of first epoch. i starts from 0 - BatchCtrl ctrl_; // No control=0, EOE=1, EOF=2, Quit=3 - const int64_t get_batch_num() const { return batch_num_; } - const int64_t get_epoch_num() const { return epoch_num_; } - - std::string FlagName() const { - switch (ctrl_) { - case BatchCtrl::kNoCtrl: - return "Data"; - case BatchCtrl::kEOE: - return "EOE"; - case BatchCtrl::kEOF: - return "EOF"; - case BatchCtrl::kQuit: - return "Quit"; - case BatchCtrl::kWait: - return "Wait"; - default: - return "Unknown"; - } - } -}; - -class BatchOp : public ParallelOp, CBatchInfo>, TensorRow> { - public: -#ifdef ENABLE_PYTHON - BatchOp(int32_t batch_size, bool drop, bool pad, int32_t op_queue_size, int32_t num_workers, - const std::vector &in_col_names, const std::vector &out_col_names, - py::function batch_size_func, py::function batch_map_func, PadInfo pad_map); -#endif - - BatchOp(int32_t batch_size, bool drop, bool pad, int32_t op_queue_size, int32_t num_workers, std::vector, - PadInfo pad_map); - - // BatchOp destructor - ~BatchOp() override = default; - - // @param int32_t workerId - // @return Status The status code returned - Status EofReceived(int32_t) override; - - // @param int32_t workerId - // @return Status The status code returned - Status EoeReceived(int32_t) override; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param sO - reference to the BatchOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const BatchOp &bo) { - bo.Print(out, false); - return out; - } - - // Main loop of batch - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kBatchOp; } - - // batch the rows in src table then put it to dest table - // @param const std::unique_ptr *tensor_row_dequeue - table that has the rows for batching - // @param TensorRow *batched_tensor_row - dest_table to hold batched rows - // @param bool concat_batch - whether to keep batch to 1 row or expand dimensions - // @param bool contains_per_batch_map - whether user has provided per_batch_map - // @notes contains_per_batch_map is passed to this function since some callers require this function to be static - // @return Status The status code returned - static Status BatchRows(const std::unique_ptr *tensor_row_dequeue, TensorRow *batched_tensor_row, - bool concat_batch = false, bool contains_per_batch_map = false); - - // convert the rows to tensor - // @param const std::unique_ptr *tensor_row_dequeue - table that has the rows for batching - // @param std::shared_ptr *batched_tensor - dest_table to hold batched rows - // @param int32_t size - batch_size - // @param int32_t size - column_index - // @param bool contains_per_batch_map - whether user has provided per_batch_map - // @notes contains_per_batch_map is passed to this function since some callers require this function to be static - // @return Status The status code returned - static Status ConvertRowsToTensor(const std::unique_ptr *tensor_row_dequeue, - std::shared_ptr *batched_tensor, dsize_t batch_size, size_t column_index, - bool contains_per_batch_map); - - // @param table - // @param const PadInfo &pad_info pad info - // @param const std::unordered_map& column_name_id_map - column names to index mapping - // @return Status The status code returned - static Status PadColumns(const std::unique_ptr *table, const PadInfo &pad_info, - const std::unordered_map &column_name_id_map); - - int64_t GetTreeBatchSize() override; - - bool IsPython() const override { -#ifdef ENABLE_PYTHON - if (batch_map_func_ || batch_size_func_) { - return true; - } -#endif - return false; - } - - /// Set the instance of Python multiprocessing which will passed from Python - /// \param python_multiprocessing_runtime PythonMultiprocessingRuntime - void SetPythonMp(std::shared_ptr python_multiprocessing_runtime); - - /// Return the list of PIDs of worker processes - /// \return vector of int - std::vector GetMPWorkerPIDs() const override; - - // @Used by independent dataset mode to stop the subprocess - // @return Status The status code returned - Status Terminate() override; - - private: - // Worker thread for doing the memcpy of batch - // @param int32_t param workerId - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; - - // Generate row with batched tensors - // @return Status The status code returned - Status MakeBatchedRow(std::pair, CBatchInfo> tensor_info_pair, - TensorRow *batched_tensor_row); - -#ifdef ENABLE_PYTHON - // Function that calls pyfunc to perform map on batch - // @param (std::pair, batch_stats> *table_pair - contains un-batched tensor - // @return Status The status code returned - Status MapColumns(std::pair, CBatchInfo> *table_pair, bool *concat_batch); -#endif - - // @param const PadInfo &pad_info pad info to unpack - // @param const std::unordered_map& column_name_id_map - column names to index mapping - // @param std::set *cols, col ids to perform pad on - // @param std::vector *vals, default padding value for each column - // @param std::vector> *shapes, padding shape specified by user - // @return Status The status code returned - static Status UnpackPadInfo(const PadInfo &pad_info, - const std::unordered_map &column_name_id_map, - std::set *pad_cols, std::vector> *pad_vals, - std::vector> *pad_shapes); - - // get the batch size for next batch - // @return Status The status code returned - Status GetBatchSize(int32_t *batch_size, CBatchInfo info); - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - Status SendWaitFlagToWorker(int32_t worker_id) override; - - Status SendQuitFlagToWorker(int32_t worker_id) override; - - Status ComputeColMap() override; - -#ifdef ENABLE_PYTHON - // Invoke batch size function with current BatchInfo to generate batch size. - // @return Status The status code returned - Status InvokeBatchSizeFunc(int32_t *batch_size, CBatchInfo info); - - // Invoke batch map function with current BatchInfo to generate tensors to batch. - // @return Status The status code returned - Status InvokeBatchMapFunc(TensorTable *input, TensorTable *output, CBatchInfo info, bool *concat_batch); -#endif - - void UpdateCounterAndSendEOE(TensorRow *const row); - - int32_t start_batch_size_; - const bool drop_; // bool for whether to drop remainder or not - const bool pad_; // bool for whether to perform padding on tensor - std::vector in_col_names_; // input column name for per_batch_map - std::vector out_col_names_; // output column name for per_batch_map - PadInfo pad_info_; // column names to perform padding on - std::unique_ptr child_iterator_; // child iterator for fetching TensorRows 1 by 1 - std::unordered_map child_map_; // col_name_id_map of the child node - int64_t batch_num_; - int64_t batch_cnt_; -#ifdef ENABLE_PYTHON - py::function batch_size_func_; // Function pointer of batch size function - py::function batch_map_func_; // Function pointer of per batch map function -#endif - std::shared_ptr python_multiprocessing_runtime_; // python multiprocessing instance - - protected: - Status Launch() override; - - Status AddNewWorkers(int32_t num_new_workers) override; - Status RemoveWorkers(int32_t num_workers) override; - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - bool eoe_received_ = false; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BATCH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.cc deleted file mode 100644 index 0ca2d159f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.cc +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace py = pybind11; -namespace mindspore { -namespace dataset { -BucketBatchByLengthOp::BucketBatchByLengthOp(const std::vector &length_dependent_columns, - const std::vector &bucket_boundaries, - const std::vector &bucket_batch_sizes, - std::shared_ptr element_length_function, const PadInfo &pad_info, - bool pad_to_bucket_boundary, bool drop_remainder, - int32_t op_connector_size) - : PipelineOp(op_connector_size), - length_dependent_columns_(length_dependent_columns), - bucket_boundaries_(bucket_boundaries), - bucket_batch_sizes_(bucket_batch_sizes), - element_length_function_(std::move(element_length_function)), - pad_info_(pad_info), - pad_to_bucket_boundary_(pad_to_bucket_boundary), - drop_remainder_(drop_remainder), - batch_count_(0) { - for (int i = 0; i < bucket_batch_sizes_.size(); i++) { - buckets_.push_back(std::make_unique()); - } -} - -Status BucketBatchByLengthOp::EoeReceived(int32_t) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -Status BucketBatchByLengthOp::operator()() { - TaskManager::FindMe()->Post(); - - TensorRow current_row; - child_iterator_ = std::make_unique(this, 0, 0); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - while (!child_iterator_->EofHandled()) { - while (!current_row.empty()) { - int32_t element_length; - RETURN_IF_NOT_OK(ObtainElementLength(&element_length, current_row)); - - int bucket_index = bucket_boundaries_.size() - 1; - while (element_length < bucket_boundaries_[bucket_index]) { - bucket_index--; - } - - buckets_[bucket_index]->push_back(current_row); - - if (buckets_[bucket_index]->size() == bucket_batch_sizes_[bucket_index]) { - TensorRow batched_bucket; - RETURN_IF_NOT_OK(PadAndBatchBucket(bucket_index, &batched_bucket)); - RETURN_IF_NOT_OK(out_connector_->Add(std::move(batched_bucket))); - } - - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - } - - if (!drop_remainder_) { - // output the incomplete batches - for (int i = 0; i < bucket_boundaries_.size(); i++) { - if (!buckets_[i]->empty()) { - TensorRow batched_bucket; - RETURN_IF_NOT_OK(PadAndBatchBucket(i, &batched_bucket)); - RETURN_IF_NOT_OK(out_connector_->Add(std::move(batched_bucket))); - } - } - } else { - // drop the incomplete batches - for (const auto &bucket : buckets_) { - bucket->clear(); - } - } - - // need to send EOE manually since we set state to idle in EoeRecieved() - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - } - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - - return Status::OK(); -} - -Status BucketBatchByLengthOp::ObtainElementLength(int32_t *out_element_length, TensorRow element) { - RETURN_UNEXPECTED_IF_NULL(out_element_length); - // call pyfunc here if given pyfunc, otherwise return 0th dimension of shape of - // the single column specified in length_dependent_columns_ - if (element_length_function_) { - TensorRow input, output; - size_t number_of_arguments = length_dependent_columns_.size(); - for (size_t i = 0; i < number_of_arguments; i++) { - auto map_item = column_name_id_map_.find(length_dependent_columns_[i]); - if (map_item == column_name_id_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid column, BucketBatchByLength couldn't find the specified column(" + - length_dependent_columns_[i] + ") in the dataset."); - } - int32_t column_index = map_item->second; - input.push_back(element[column_index]); - } - RETURN_IF_NOT_OK(element_length_function_->Compute(input, &output)); - RETURN_IF_NOT_OK(output.at(0)->GetItemAt(out_element_length, {0})); - if (*out_element_length < 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid element_length_function, element_length_function must return an integer greater than or equal to 0, " - "but got" + - std::to_string(*out_element_length)); - } - } else { - *out_element_length = element[0]->shape()[0]; - } - return Status::OK(); -} - -Status BucketBatchByLengthOp::PadAndBatchBucket(int32_t bucket_index, TensorRow *batched_bucket) { - RETURN_UNEXPECTED_IF_NULL(batched_bucket); - std::unique_ptr *bucket = &buckets_[bucket_index]; - - PadInfo pad_info_copy = pad_info_; - if (pad_to_bucket_boundary_) { - for (auto &pair : pad_info_copy) { - std::vector pad_shape = pair.second.first.AsVector(); - - for (auto &i : pad_shape) { - if (i == TensorShape::kDimUnknown) { - if (bucket_index + 1 >= bucket_boundaries_.size()) { - std::string error_message = - "Invalid data, requested to pad to bucket boundary failed, bucket index should be less than " + - std::to_string(bucket_boundaries_.size()) + ", but got " + std::to_string(bucket_index); - RETURN_STATUS_UNEXPECTED(error_message); - } - - i = bucket_boundaries_[bucket_index + 1] - 1; - } - } - - pair.second.first = TensorShape(pad_shape); - } - } - - // PadColumns will change the data in bucket - RETURN_IF_NOT_OK(BatchOp::PadColumns(bucket, pad_info_copy, column_name_id_map_)); - - RETURN_IF_NOT_OK(BatchOp::BatchRows(bucket, batched_bucket)); - (*bucket)->clear(); - - batch_count_++; - - return Status::OK(); -} - -// Computing the assignment of the column name map and check compute input columns. -Status BucketBatchByLengthOp::ComputeColMap() { - RETURN_IF_NOT_OK(DatasetOp::ComputeColMap()); - - for (const auto &inCol : length_dependent_columns_) { - bool found = column_name_id_map_.find(inCol) != column_name_id_map_.end(); - if (!found) { - std::string err_msg = "Invalid parameter, input column name: " + inCol + " doesn't exist in the dataset columns."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} - -Status BucketBatchByLengthOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - row->clear(); - - if (!eoe_received_) { - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - while (!new_row.eoe() && !new_row.eof()) { - int32_t element_length = 0; - RETURN_IF_NOT_OK(ObtainElementLength(&element_length, new_row)); - - int bucket_index = bucket_boundaries_.size() - 1; - while (element_length < bucket_boundaries_[bucket_index]) { - bucket_index--; - } - - buckets_[bucket_index]->push_back(new_row); - - if (buckets_[bucket_index]->size() == bucket_batch_sizes_[bucket_index]) { - RETURN_IF_NOT_OK(PadAndBatchBucket(bucket_index, row)); - return Status::OK(); - } - - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - } - } - - eoe_received_ = true; - if (!drop_remainder_) { - // output the incomplete batches - for (int i = 0; i < bucket_boundaries_.size(); i++) { - if (!buckets_[i]->empty()) { - RETURN_IF_NOT_OK(PadAndBatchBucket(i, row)); - return Status::OK(); - } - } - } else { - // drop the incomplete batches - for (const auto &bucket : buckets_) { - bucket->clear(); - } - } - eoe_received_ = false; - - auto curr_epoch = op_current_epochs_; - UpdateRepeatAndEpochCounter(); - if (curr_epoch == op_current_epochs_) { - RETURN_IF_NOT_OK(GetNextRowPullMode(row)); - } - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h deleted file mode 100644 index b1b3d56d6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUCKET_BATCH_BY_LENGTH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUCKET_BATCH_BY_LENGTH_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -class BucketBatchByLengthOp : public PipelineOp { - public: - BucketBatchByLengthOp(const std::vector &length_dependent_columns, - const std::vector &bucket_boundaries, const std::vector &bucket_batch_sizes, - std::shared_ptr element_length_function, const PadInfo &pad_info, - bool pad_to_bucket_boundary, bool drop_remainder, int32_t op_connector_size); - - // Destructor - ~BucketBatchByLengthOp() = default; - - // Might need to batch remaining buckets after receiving eoe, so override this method. - // @param int32_t workerId - // @return Status The status code returned - Status EoeReceived(int32_t) override; - - std::string Name() const override { return kBucketBatchByLengthOp; } - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param sO - reference to the BucketBatchByLengthOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const BucketBatchByLengthOp &bo) { - bo.Print(out, false); - return out; - } - - // Main loop of batch - // @return Status The status code returned - Status operator()() override; - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - Status ObtainElementLength(int32_t *out_element_length, TensorRow element); - - Status PadAndBatchBucket(int32_t bucket_index, TensorRow *batched_bucket); - - Status ComputeColMap() override; - - std::vector length_dependent_columns_; - std::vector bucket_boundaries_; - std::vector bucket_batch_sizes_; - std::shared_ptr element_length_function_; - PadInfo pad_info_; - bool pad_to_bucket_boundary_; - bool drop_remainder_; - bool eoe_received_ = false; - - int32_t batch_count_; - std::unique_ptr child_iterator_; - std::vector> buckets_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUCKET_BATCH_BY_LENGTH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.cc deleted file mode 100644 index 41d0df7e3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.cc +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { -BuildSentencePieceVocabOp::BuildSentencePieceVocabOp(std::shared_ptr vocab, - const std::vector col_names, int32_t vocab_size, - float character_coverage, SentencePieceModel model_type, - const std::unordered_map ¶ms, - int32_t op_conn_size) - : PipelineOp(op_conn_size), - vocab_size_(vocab_size), - vocab_(vocab), - col_names_(col_names), - character_coverage_(character_coverage), - model_type_(model_type), - params_(params), - col_id_(0) { - sentence_queue_ = std::make_unique>(op_conn_size); - read_done_ = false; - ret_status_ = Status::OK(); -} - -Status BuildSentencePieceVocabOp::operator()() { - if (tree_ == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Pipeline init failed, Execution tree not set."); - } - RETURN_IF_NOT_OK(sentence_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "sentenceTask", std::bind(&BuildSentencePieceVocabOp::SentenceThread, this), nullptr, id())); - TaskManager::FindMe()->Post(); - child_iterator_ = std::make_unique(this, 0, 0); - TensorRow new_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - - bool eoe_warning = false; // give out warning if receive more than 1 eoe - while (child_iterator_->EofHandled() == false) { - while (new_row.empty() == false) { - RETURN_IF_NOT_OK(sentence_queue_->EmplaceBack(new_row)); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - CHECK_FAIL_RETURN_UNEXPECTED(!eoe_warning, "'build_sentencepiece_vocab' does not support 'repeat'.)"); - eoe_warning = true; - } - // add empty tensorRow for quit - TensorRow empty_row = {}; - RETURN_IF_NOT_OK(sentence_queue_->EmplaceBack(empty_row)); - return Status::OK(); -} - -Status BuildSentencePieceVocabOp::SentenceThread() { - TaskManager::FindMe()->Post(); - if (col_names_.empty() == true) { - auto itr = column_name_id_map_.find("text"); - CHECK_FAIL_RETURN_UNEXPECTED(itr != column_name_id_map_.end(), "Invalid data, 'text' column does not exist."); - col_id_ = itr->second; - } else { - auto itr = column_name_id_map_.find(col_names_[0]); - CHECK_FAIL_RETURN_UNEXPECTED(itr != column_name_id_map_.end(), "Invalid column, column name: " + col_names_[0] + - " does not exist, check existed " - "column with dataset API 'get_col_names'"); - col_id_ = itr->second; - } - std::unique_ptr sentence_iter = std::make_unique(this); - std::string model_proto; - sentencepiece::util::Status s_status = - sentencepiece::SentencePieceTrainer::Train(BuildParams(), sentence_iter.get(), &model_proto); - if (!s_status.ok()) { - RETURN_STATUS_UNEXPECTED(s_status.message()); - } else { - if (vocab_ == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] SentencePiece vocab should not be null."); - } - vocab_->set_model_proto(model_proto); - } - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - return Status::OK(); -} - -std::unordered_map BuildSentencePieceVocabOp::BuildParams() { - std::unordered_map ret_params; - ret_params["vocab_size"] = std::to_string(vocab_size_); - ret_params["character_coverage"] = std::to_string(character_coverage_); - if (model_type_ == SentencePieceModel::kBpe) { - ret_params["model_type"] = "BPE"; - } else if (model_type_ == SentencePieceModel::kChar) { - ret_params["model_type"] = "CHAR"; - } else if (model_type_ == SentencePieceModel::kWord) { - ret_params["model_type"] = "WORD"; - } else { - ret_params["model_type"] = "UNIGRAM"; - } - // filter some params that set by function param - // filter model_prefix that must be empty - for (auto param : params_) { - std::string key = param.first; - if (key == "input" || key == "vocab_size" || key == "model_prefix" || key == "character_coverage" || - key == "model_type") { - continue; - } - ret_params[key] = param.second; - } - - ret_params["model_prefix"] = ""; - ret_params["minloglevel"] = "1"; - return ret_params; -} - -bool BuildSentencePieceVocabOp::Done() { return read_done_; } - -void BuildSentencePieceVocabOp::Next(std::string *sentence) { - if (sentence == nullptr) { - MS_LOG(ERROR) << "[Internal ERROR] BuildSentencePieceVocab get nullptr element, please check data."; - return; - } - TensorRow new_row; - Status s = sentence_queue_->PopFront(&new_row); - - if (s.IsError()) { - read_done_ = true; - ret_status_ = s; - return; - } - if (new_row.empty() == true) { - read_done_ = true; - ret_status_ = Status::OK(); - return; - } - - if (new_row[col_id_]->type().IsNumeric() || new_row[col_id_]->Rank() > 1) { - ret_status_ = - STATUS_ERROR(StatusCode::kMDUnexpectedError, - "Invalid data, build_sentence_piece_vocab only supports string data with rank equal to 1, " - "but got type: " + - new_row[col_id_]->type().ToString() + ", rank: " + std::to_string(new_row[col_id_]->Rank())); - read_done_ = true; - return; - } - - std::string_view sentence_v; - ret_status_ = new_row[col_id_]->GetItemAt(&sentence_v, {}); - if (ret_status_.IsError()) { - read_done_ = true; - return; - } - - std::string st{sentence_v}; - *sentence = st; - ret_status_ = Status::OK(); -} - -BuildSentencePieceVocabOp::DatasetSentenceIterator::DatasetSentenceIterator(BuildSentencePieceVocabOp *s_p_vocab_ptr) - : s_p_vocab_ptr_(s_p_vocab_ptr) {} - -bool BuildSentencePieceVocabOp::DatasetSentenceIterator::done() const { - if (s_p_vocab_ptr_ == nullptr) { - return true; - } - return s_p_vocab_ptr_->Done(); -} - -void BuildSentencePieceVocabOp::DatasetSentenceIterator::Next() { - if (s_p_vocab_ptr_ == nullptr) { - return; - } - s_p_vocab_ptr_->Next(&value_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h deleted file mode 100644 index 951dfa4a8..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_DATASETOPS_BUILD_SENTENCE_VOCAB_OP_H_ -#define DATASET_ENGINE_DATASETOPS_BUILD_SENTENCE_VOCAB_OP_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/include/dataset/text.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "pybind11/pybind11.h" - -namespace mindspore { -namespace dataset { -namespace py = pybind11; - -class BuildSentencePieceVocabOp : public PipelineOp { - public: - class DatasetSentenceIterator : public sentencepiece::SentenceIterator { - public: - explicit DatasetSentenceIterator(BuildSentencePieceVocabOp *s_p_vocab_ptr); - ~DatasetSentenceIterator() {} - - bool done() const override; - void Next() override; - const std::string &value() const override { return value_; } - sentencepiece::util::Status status() const override { return sentencepiece::util::Status(); } - - private: - std::string value_; - BuildSentencePieceVocabOp *s_p_vocab_ptr_; - }; - - BuildSentencePieceVocabOp(std::shared_ptr vocab, - const std::vector col_names, int32_t vocab_size, float character_coverage, - SentencePieceModel model_type, const std::unordered_map ¶ms, - int32_t op_conn_size); - - ~BuildSentencePieceVocabOp() = default; - - // the thread for sentence train - Status SentenceThread(); - - Status EofReceived(int32_t) override { return Status::OK(); } - - Status EoeReceived(int32_t) override { return Status::OK(); } - - Status operator()() override; - - Status Reset() override { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Reset shouldn't be called in BuildSentencePieceVocabOp."); - } - - std::string Name() const override { return kBuildSentencePieceVocabOp; } - - // build the input params for sentence api - std::unordered_map BuildParams(); - - bool Done(); - void Next(std::string *sentence); - - private: - bool read_done_; - Status ret_status_; - int32_t vocab_size_; - float character_coverage_; - SentencePieceModel model_type_; - std::unordered_map params_; - std::shared_ptr vocab_; - std::vector col_names_; - uint32_t col_id_; - std::unique_ptr child_iterator_; // child iterator for fetching TensorRows 1 by 1 - std::unique_ptr> sentence_queue_; // master thread assigns each worker TensorRow via this -}; -} // namespace dataset -} // namespace mindspore -#endif // DATASET_ENGINE_DATASETOPS_BUILD_SENTENCE_VOCAB_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.cc deleted file mode 100644 index 2fd8a44e8..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.cc +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { -BuildVocabOp::BuildVocabOp(std::shared_ptr vocab, std::vector col_names, - std::pair freq_r, int64_t top_k, const std::vector &tokens, - bool prepend, int32_t num_workers, int32_t op_conn_size) - : ParallelOp(num_workers, op_conn_size), - interval_(op_conn_size * num_workers), - vocab_(vocab), - col_names_(col_names), - freq_range_(freq_r), - top_k_(top_k), - special_tokens_(tokens), - special_first_(prepend) { - // init two queues for thread sync - distributor_queue_ = std::make_unique>(num_workers * op_conn_size); - collector_queue_ = - std::make_unique>>>((num_workers * op_conn_size)); -} - -Status BuildVocabOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - TensorRow new_row; - RETURN_IF_NOT_OK(distributor_queue_->PopFront(&new_row)); - std::unique_ptr> wrkr_map = - std::make_unique>(); - int32_t row_cnt = 0; - while (!new_row.empty()) { - for (size_t col : col_ids_) { - CHECK_FAIL_RETURN_UNEXPECTED(!new_row[col]->type().IsNumeric(), - "Invalid datatype, 'build_vocab' only supports string type of input, but got " - "numeric type: " + - new_row[col]->type().ToString()); - for (auto itr = new_row[col]->begin(); itr != new_row[col]->end(); ++itr) { - (*wrkr_map)[std::string(*itr)] += 1; - } - } - row_cnt++; // row is processed by this point - if ((row_cnt % interval_ == 0) && ((row_cnt / interval_) % num_workers_ == worker_id) && (!wrkr_map->empty())) { - RETURN_IF_NOT_OK(collector_queue_->Add(std::move(wrkr_map))); - wrkr_map = std::make_unique>(); - } - RETURN_IF_NOT_OK(distributor_queue_->PopFront(&new_row)); - } - // clean up - if (!wrkr_map->empty()) { - RETURN_IF_NOT_OK(collector_queue_->Add(std::move(wrkr_map))); - } - // empty map as quit signal - RETURN_IF_NOT_OK(collector_queue_->Add(std::make_unique>())); - return Status::OK(); -} - -Status BuildVocabOp::operator()() { - // launch the collector thread - if (tree_ == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Pipeline init failed, Execution tree not set."); - } - RETURN_IF_NOT_OK(distributor_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(collector_queue_->Register(tree_->AllTasks())); - // launch worker threads and collector thread - RETURN_IF_NOT_OK(tree_->LaunchWorkers( - num_workers_, std::bind(&BuildVocabOp::WorkerEntry, this, std::placeholders::_1), Name() + "::WorkerEntry", id())); - RETURN_IF_NOT_OK( - tree_->AllTasks()->CreateAsyncTask("collector", std::bind(&BuildVocabOp::CollectorThread, this), nullptr, id())); - TaskManager::FindMe()->Post(); - child_iterator_ = std::make_unique(this, 0, 0); - TensorRow new_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - if (!col_names_.empty()) { - col_ids_.reserve(col_names_.size()); - for (std::string col : col_names_) { - auto itr = column_name_id_map_.find(col); - CHECK_FAIL_RETURN_UNEXPECTED(itr != column_name_id_map_.end(), "Invalid column name, column name: " + col + - " does not exist, check existed column " - "with dataset API 'get_col_names'"); - col_ids_.push_back(itr->second); - } - } else { - col_ids_.reserve(column_name_id_map_.size()); - for (const auto &p : column_name_id_map_) { - col_ids_.push_back(p.second); - } - } - bool eoe_warning = false; // give out warning if receive more than 1 eoe - while (child_iterator_->EofHandled() == false) { - while (new_row.empty() == false) { - RETURN_IF_NOT_OK(distributor_queue_->EmplaceBack(new_row)); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - CHECK_FAIL_RETURN_UNEXPECTED(!eoe_warning, - "Invalid repeat operator, BuildVocab does not support 'repeat' operator."); - eoe_warning = true; - } - - // tell all workers to quit - for (int32_t wrkr_id = 0; wrkr_id < num_workers_; wrkr_id++) { - RETURN_IF_NOT_OK(distributor_queue_->EmplaceBack(TensorRow())); - } - return Status::OK(); -} - -Status BuildVocabOp::CollectorThread() { - TaskManager::FindMe()->Post(); - int32_t num_quited_worker = 0; - std::unique_ptr> wrkr_map; - while (num_quited_worker != num_workers_) { - RETURN_IF_NOT_OK(collector_queue_->PopFront(&wrkr_map)); - RETURN_UNEXPECTED_IF_NULL(wrkr_map); - if (!wrkr_map->empty()) { - for (const auto &wd : *wrkr_map) { - word_cnt_[wd.first] += wd.second; - } - } else { - ++num_quited_worker; - } - } // all frequencies are obtained - CHECK_FAIL_RETURN_UNEXPECTED(!word_cnt_.empty(), - "Invalid data, BuildVocab load data failed that no words found in vocab, check vocab."); - std::vector words; - // make sure enough is reserved, this will become a partially sorted list eventually - words.reserve(wrkr_map->size()); - - for (auto it = word_cnt_.begin(); it != word_cnt_.end();) { - if (it->second >= freq_range_.first && it->second <= freq_range_.second) { - words.push_back(it->first); - it++; - } else { - it = word_cnt_.erase(it); - } - } - std::string err_msg; - - for (const std::string &sp_tk : special_tokens_) { - // if a special word exists in dataset, warn user about this - err_msg += (word_cnt_.find(sp_tk) != word_cnt_.end() ? sp_tk + "\t" : ""); - } - - CHECK_FAIL_RETURN_UNEXPECTED(err_msg.empty(), - "Invalid special words, these special words are already in the vocab: " + err_msg + "."); - - int64_t num_words = std::min(static_cast(words.size()), top_k_); - if (num_words == 0) { - MS_LOG(WARNING) << "No word falls in the frequency range: (" << freq_range_.first << "," << freq_range_.second - << ") vocab would be empty (except for special tokens)."; - } - - // this would take the top-k most frequent words - std::partial_sort(words.begin(), words.begin() + num_words, words.end(), - [this](const std::string &w1, const std::string &w2) { - int64_t f1 = word_cnt_[w1]; - int64_t f2 = word_cnt_[w2]; - return f1 == f2 ? w1 < w2 : f1 > f2; - }); - - if (special_first_) { - for (const std::string &sp_tk : special_tokens_) { - vocab_->AppendWord(sp_tk); - } - } - - for (int64_t i = 0; i < num_words; i++) { - vocab_->AppendWord(words[i]); - } - - if (!special_first_) { - for (const std::string &sp_tk : special_tokens_) { - vocab_->AppendWord(sp_tk); - } - } - - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - // then use std::nth_element to partial sort - return Status::OK(); -} - -// A print method typically used for debugging -void BuildVocabOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nCode is needed here to show more info about the op." - << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h deleted file mode 100644 index fd8e1cacc..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUILD_VOCAB_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUILD_VOCAB_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/include/dataset/text.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class BuildVocabOp : public ParallelOp { - public: - BuildVocabOp(std::shared_ptr vocab, std::vector col_names, - std::pair freq_range, int64_t top_k, const std::vector &tokens, - bool prepend, int32_t num_workers, int32_t op_connector_size); - - ~BuildVocabOp() = default; - - /// \brief A print method typically used for debugging - /// \param[out] out The output stream to write output to - /// \param[in] show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - std::string Name() const override { return kBuildVocabOp; } - - /// \briefStream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param[out] out Reference to the output stream being overloaded - /// \param[in] vop - reference to the BuildVocabOp to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const BuildVocabOp &vop) { - vop.Print(out, false); - return out; - } - - Status WorkerEntry(int32_t worker_id) override; - - /// collect the work product from each worker - Status CollectorThread(); - - Status EofReceived(int32_t) override { return Status::OK(); } - - Status EoeReceived(int32_t) override { return Status::OK(); } - - Status operator()() override; - - Status Reset() override { RETURN_STATUS_UNEXPECTED("[Internal ERROR] Reset shouldn't be called in BuildVocabOp"); } - - private: - const int32_t interval_; - bool special_first_; - std::shared_ptr vocab_; - std::vector col_names_; - std::vector col_ids_; - std::vector special_tokens_; - // pair = {min_f, max_f} - // make sure that 0<= min_f < max_f <= int32_max in the builder - std::pair freq_range_; - - int64_t top_k_; // every thing means top_k_ == int32_max - std::unique_ptr child_iterator_; // child iterator for fetching TensorRows 1 by 1 - std::unique_ptr> distributor_queue_; // master thread assigns each worker TensorRow via this - std::unique_ptr>>> collector_queue_; - std::unordered_map word_cnt_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_BUILD_VOCAB_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.cc deleted file mode 100644 index d78b6d764..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.cc +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h" - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -// A print method typically used for debugging -void CacheBase::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nCache client:\n" << *cache_client_ << "\n\n"; - } -} -// Overrides base class reset method. When an operator does a reset, it cleans up any state -// info from it's previous execution and then initializes itself so that it can be executed -// again. -Status CacheBase::Reset() { - if (sampler_ != nullptr) { - RETURN_IF_NOT_OK(sampler_->ResetSampler()); - } - // Wake up the workers to get them going again in a new epoch - MS_LOG(DEBUG) << Name() << " performing a self-reset."; - return Status::OK(); -} -CacheBase::CacheBase(int32_t num_workers, int32_t op_connector_size, std::shared_ptr cache_client, - std::shared_ptr sampler) - : ParallelOp(num_workers, op_connector_size, std::move(sampler)), - row_cnt_(0), - num_cache_miss_(0), - cache_client_(std::move(cache_client)), - prefetch_size_(1), - num_prefetchers_(num_workers_) { - // Adjust the prefetch size based on the number of workers. - auto prefetch_sz_per_thread = cache_client_->GetPrefetchSize() / num_prefetchers_; - if (prefetch_size_ < prefetch_sz_per_thread) { - prefetch_size_ = prefetch_sz_per_thread; - MS_LOG(DEBUG) << "Per worker prefetch size : " << prefetch_size_; - } - worker_in_queues_.Init(num_workers, op_connector_size); - prefetch_queues_.Init(num_prefetchers_, op_connector_size); - // We can cause deadlock if this internal Connector size is too small. - keys_miss_ = std::make_unique>>(num_prefetchers_, 1, connector_capacity_); -} -// Common function to fetch samples from the sampler and send them using the io_block_queues to -// the parallel workers -Status CacheBase::FetchSamplesToWorkers() { - int64_t buf_cnt = 0; - int64_t wait_cnt = 0; - int64_t prefetch_cnt = 0; - // Kick off several threads which will prefetch cache_prefetch_size_ rows in advance. - RETURN_UNEXPECTED_IF_NULL(tree_); - RETURN_IF_NOT_OK( - tree_->LaunchWorkers(num_prefetchers_, std::bind(&CacheBase::Prefetcher, this, std::placeholders::_1), Name())); - auto send_to_que = [](QueueList> &qList, int32_t worker_id, - std::vector &keys) -> Status { - auto blk = std::make_unique(IOBlock(keys, IOBlock::kFlagNone)); - RETURN_IF_NOT_OK(qList[worker_id]->Add(std::move(blk))); - return Status::OK(); - }; - // Instead of sending sampler id to WorkerEntry, we send them to the Prefetcher which will redirect them - // to the WorkerEntry. - do { - if (AllowCacheMiss() && wait_cnt > 0 && wait_cnt % GetOpNumRepeatsPerEpoch() == 0) { - MS_LOG(INFO) << "Epoch: " << op_current_epochs_ << " Cache Miss : " << num_cache_miss_ - << " Total number of rows : " << row_cnt_; - } - num_cache_miss_ = 0; - row_cnt_ = 0; - ++wait_cnt; - std::vector keys; - keys.reserve(1); - std::vector prefetch_keys; - prefetch_keys.reserve(prefetch_size_); - TensorRow sample_row; - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - while (!sample_row.eoe()) { - std::shared_ptr sample_ids = sample_row[0]; - for (auto itr = sample_ids->begin(); itr != sample_ids->end(); ++itr) { - ++row_cnt_; - prefetch_keys.push_back(*itr); - // Batch enough rows for performance reason. - if (row_cnt_ % prefetch_size_ == 0) { - RETURN_IF_NOT_OK(send_to_que(prefetch_queues_, prefetch_cnt++ % num_prefetchers_, prefetch_keys)); - // Now we tell the WorkerEntry to wait for them to come back. - for (auto row_id : prefetch_keys) { - keys.push_back(row_id); - RETURN_IF_NOT_OK(send_to_que(worker_in_queues_, static_cast(buf_cnt++ % num_workers_), keys)); - keys.clear(); - } - prefetch_keys.clear(); - } - } - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - } - // Deal with any partial keys left. - if (!prefetch_keys.empty()) { - RETURN_IF_NOT_OK(send_to_que(prefetch_queues_, prefetch_cnt++ % num_prefetchers_, prefetch_keys)); - for (auto row_id : prefetch_keys) { - keys.push_back(row_id); - RETURN_IF_NOT_OK(send_to_que(worker_in_queues_, static_cast(buf_cnt++ % num_workers_), keys)); - keys.clear(); - } - } - if (!keys.empty()) { - RETURN_IF_NOT_OK(send_to_que(worker_in_queues_, static_cast(buf_cnt++ % num_workers_), keys)); - } - // send the eoe - RETURN_IF_NOT_OK(worker_in_queues_[static_cast((buf_cnt++) % num_workers_)]->Add( - std::make_unique(IOBlock::kFlagEOE))); - RETURN_IF_NOT_OK( - prefetch_queues_[(prefetch_cnt++) % num_prefetchers_]->Add(std::make_unique(IOBlock::kFlagEOE))); - // If repeat but the not last repeat, wait for reset. - if (!IsLastIteration()) { - MS_LOG(DEBUG) << Name() << " Waiting for reset. Count " << wait_cnt << " Buffer sent " << buf_cnt; - } else { - // We can break out from the loop. - break; - } - if (epoch_sync_flag_) { - // If epoch_sync_flag_ is set, then master thread sleeps until all the worker threads have finished their job for - // the current epoch. - RETURN_IF_NOT_OK(WaitForWorkers()); - } - // If not the last repeat, self-reset and go to loop again. - if (!IsLastIteration()) { - RETURN_IF_NOT_OK(Reset()); - } - UpdateRepeatAndEpochCounter(); - } while (true); - // Flow the eof before exit - RETURN_IF_NOT_OK(worker_in_queues_[static_cast((buf_cnt++) % num_workers_)]->Add( - std::make_unique(IOBlock::kFlagEOF))); - // Shutdown threads - for (int32_t i = 0; i < num_workers_; i++) { - RETURN_IF_NOT_OK(worker_in_queues_[i]->Add(std::make_unique(std::vector(), IOBlock::kFlagNone))); - } - // Dump the last epoch result (approximately) without waiting for the worker threads to come back. - if (AllowCacheMiss()) { - MS_LOG(INFO) << "Epoch: " << wait_cnt / GetOpNumRepeatsPerEpoch() << " Cache Miss : " << num_cache_miss_ - << " Total number of rows : " << row_cnt_; - } - return Status::OK(); -} - -Status CacheBase::FetchFromCache(int32_t worker_id) { - std::unique_ptr blk; - do { - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&blk)); - if (blk->wait()) { - // Sync io_block is a signal that master thread wants us to pause and sync with other workers. - // The last guy who comes to this sync point should reset the counter and wake up the master thread. - if (++num_workers_paused_ == num_workers_) { - wait_for_workers_post_.Set(); - } - } else if (blk->eof()) { - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOF))); - } else if (blk->eoe()) { - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOE))); - } else { - std::vector keys; - RETURN_IF_NOT_OK(blk->GetKeys(&keys)); - if (keys.empty()) { - // empty key is a quit signal for workers - break; - } - for (auto row_id : keys) { - TensorRow row; - // Block until the row shows up in the pool. - RETURN_IF_NOT_OK(GetPrefetchRow(row_id, &row)); - if (row.empty()) { - if (AllowCacheMiss()) { - ++num_cache_miss_; - } else { - std::string errMsg = "[Internal ERROR] Row id " + std::to_string(row_id) + " not found."; - RETURN_STATUS_UNEXPECTED(errMsg); - } - } - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(row))); - } - } - } while (true); - return Status::OK(); -} - -Status CacheBase::RegisterResources() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(prefetch_queues_.Register(tree_->AllTasks())); - return Status::OK(); -} - -CacheBase::~CacheBase() = default; - -Status CacheBase::UpdateColumnMapFromCache() { - Status rc; - // Get the schema from the server. It may not be there yet. So tolerate the error. - if (column_name_id_map_.empty()) { - rc = cache_client_->FetchSchema(&column_name_id_map_); - if (rc == Status(StatusCode::kMDFileNotExist)) { - MS_LOG(DEBUG) << "Schema not in the server yet."; - rc = Status::OK(); - } - } - return rc; -} - -Status CacheBase::GetPrefetchRow(row_id_type row_id, TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - CHECK_FAIL_RETURN_UNEXPECTED(row_id >= 0, - "[Internal ERROR] Expect positive row id, but got:" + std::to_string(row_id)); - RETURN_IF_NOT_OK(prefetch_.PopFront(row_id, out)); - return Status::OK(); -} - -Status CacheBase::PrefetchRows(const std::vector &keys, std::vector *cache_miss) { - RETURN_UNEXPECTED_IF_NULL(cache_miss); - std::vector prefetch_keys; - prefetch_keys.reserve(keys.size()); - - // Filter out all those keys that unlikely we will find at the server - for (auto row_id : keys) { - if (cache_client_->KeyIsCacheMiss(row_id)) { - // Just put an empty row in the cache. - TensorRow row; - row.setId(row_id); - RETURN_IF_NOT_OK(prefetch_.Add(row_id, std::move(row))); - cache_miss->push_back(row_id); - } else { - prefetch_keys.push_back(row_id); - } - } - // Early exit if nothing to fetch - if (prefetch_keys.empty()) { - return Status::OK(); - } - // Get the rows from the server - TensorTable ttbl; - RETURN_IF_NOT_OK(cache_client_->GetRows(prefetch_keys, &ttbl)); - auto row_it = ttbl.begin(); - for (auto row_id : prefetch_keys) { - auto &row = *row_it; - if (row.empty()) { - cache_miss->push_back(row_id); - } - // Put the prefetch row into the pool and wake up any WorkerEntry to wait for the row - RETURN_IF_NOT_OK(prefetch_.Add(row_id, std::move(row))); - ++row_it; - } - return Status::OK(); -} - -Status CacheBase::Prefetcher(int32_t worker_id) { - TaskManager::FindMe()->Post(); - std::vector prefetch_keys; - prefetch_keys.reserve(prefetch_size_); - std::vector cache_miss; - cache_miss.reserve(prefetch_size_); - do { - prefetch_keys.clear(); - cache_miss.clear(); - std::unique_ptr blk; - RETURN_IF_NOT_OK(prefetch_queues_[worker_id]->PopFront(&blk)); - CHECK_FAIL_RETURN_UNEXPECTED(!blk->eof(), "[Internal ERROR] Expect eoe or a regular io block."); - if (!blk->eoe()) { - RETURN_IF_NOT_OK(blk->GetKeys(&prefetch_keys)); - Status rc; - const int32_t max_retries = 5; - int32_t retry_count = 0; - do { - rc = PrefetchRows(prefetch_keys, &cache_miss); - if (rc == StatusCode::kMDNetWorkError && retry_count < max_retries) { - // If we get some network error, we will attempt some retries - retry_count++; - } else if (rc.IsError() && rc.StatusCode() != StatusCode::kMDInterrupted) { - MS_LOG(WARNING) << rc.ToString(); - return rc; - } - } while (rc == StatusCode::kMDNetWorkError); - // In case any thread is waiting for the rows to come back and blocked on a semaphore, - // we will put an empty row in the local cache. - if (rc.IsError() && AllowCacheMiss()) { - for (auto row_id : prefetch_keys) { - TensorRow row; - row.setId(row_id); - RETURN_IF_NOT_OK(prefetch_.Add(row_id, std::move(row))); - cache_miss.push_back(row_id); - } - } - } else { - if (AllowCacheMiss()) { - // This code path is for CacheLookupOp acting as a sampler. If we get a eoe from - // a sampler, send a eoe to physical leaf op as well. - cache_miss.push_back(eoe_row_id); - } - } - if (AllowCacheMiss()) { - // Because of the way connector works, we push unconditionally even cache_miss can be empty. - RETURN_IF_NOT_OK(keys_miss_->Push(worker_id, cache_miss)); - } - } while (true); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h deleted file mode 100644 index 5f7fb8f4c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_BASE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_BASE_OP_H_ - -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/connector.h" -#include "mindspore-lite/minddata/dataset/engine/cache/cache_client.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/queue_map.h" -#include "mindspore-lite/minddata/dataset/util/semaphore.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -namespace mindspore { -namespace dataset { -/// \brief This is the base class for CacheOp and CacheLookupOp which share many similarities. -/// \see CacheOp -/// \see CacheLookupOp -class CacheBase : public ParallelOp, TensorRow> { - public: - /// \brief Base class constructor - /// \param num_workers Number of parallel workers - /// \param op_connector_size Connector size - /// \param cache_client CacheClient for communication to the CacheServer - /// \param sampler Sampler which is mandatory - CacheBase(int32_t num_workers, int32_t op_connector_size, std::shared_ptr cache_client, - std::shared_ptr sampler); - /// \brief Destructor - ~CacheBase(); - - /// \brief Overrides base class reset method. When an operator does a reset, it cleans up any state - /// info from it's previous execution and then initializes itself so that it can be executed - /// again. - /// \return Status The status code returned - Status Reset() override; - - /// \brief A print method typically used for debugging - /// \param out The output stream to write output to - /// \param show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Gives a name to the class, typically used for debugging - std::string Name() const override { return kCacheBase; } - - /// \brief << Stream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param out reference to the output stream being overloaded - /// \param mo reference to the CacheOp to display - /// \return the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const CacheBase &mo) { - mo.Print(out, false); - return out; - } - - /// \brief Getter for the cache client - /// \return shared ptr to the cache client - std::shared_ptr GetCacheClient() { return cache_client_; } - /// \brief Setter for the cache client - void SetCacheClient(std::shared_ptr cache_client) { cache_client_ = std::move(cache_client); } - /// \brief Derived class must implement this method if a cache miss is treated as error - virtual bool AllowCacheMiss() = 0; - - protected: - constexpr static int32_t eoe_row_id = -1; - int64_t row_cnt_; - std::atomic num_cache_miss_; - std::shared_ptr cache_client_; - std::unique_ptr>> keys_miss_; - - /// \brief Common function to register resources for interrupt - /// \note Derived should override this function for extra resources to be registered - virtual Status RegisterResources(); - /// \brief This function is called by main thread to send samples to the worker thread. - /// \note It is a non-virtual function - /// \return Status object - Status FetchSamplesToWorkers(); - /// \brief This function is called by each worker to fetch rows from the cache server for a given set of - /// sample row id's - /// \return Status object - Status FetchFromCache(int32_t worker_id); - /// \brief Get the column map from cache server - Status UpdateColumnMapFromCache(); - - private: - constexpr static int32_t connector_capacity_ = 1024; - int32_t prefetch_size_; - int32_t num_prefetchers_; - QueueList> prefetch_queues_; - QueueMap prefetch_; - - /// \brief Prefetcher. It prefetch the rows from cache server - /// \return Status object. - Status Prefetcher(int32_t worker_id); - /// \brief Functions used by prefetcher and WorkerEntry - Status PrefetchRows(const std::vector &keys, std::vector *cache_miss); - Status GetPrefetchRow(row_id_type row_id, TensorRow *out); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_BASE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.cc deleted file mode 100644 index ba9370a47..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.cc +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -Status CacheLookupOp::operator()() { - if (!sampler_) { - RETURN_STATUS_UNEXPECTED("Invalid sampler, Cache requires a sampler before it can be executed, but got nullptr."); - } - RETURN_IF_NOT_OK(RegisterResources()); - - // required task group sync after launching workers - TaskManager::FindMe()->Post(); - // We have to wait until the leaf op has handshake with us. - RETURN_IF_NOT_OK(leaf_op_wp_.Wait()); - RETURN_IF_NOT_OK(FetchSamplesToWorkers()); - return Status::OK(); -} -Status CacheLookupOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(FetchFromCache(worker_id)); - return Status::OK(); -} -Status CacheLookupOp::ResetSampler([[maybe_unused]] const bool failover_reset) { return Status::OK(); } -Status CacheLookupOp::HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count) { - RETURN_UNEXPECTED_IF_NULL(op); - // We act like a sampler and as a dataset op. During handshake with leaf op, - // We must wait until the leaf op has indexed everything. - RETURN_IF_NOT_OK(sampler_->HandshakeRandomAccessOp(op, reset_count)); - // Now we notify the main thread handshake has finished. - leaf_op_wp_.Set(); - return Status::OK(); -} -Status CacheLookupOp::InitSampler() { return SamplerRT::InitSampler(); } -void CacheLookupOp::Print(std::ostream &out, bool show_all) const { CacheBase::Print(out, show_all); } -void CacheLookupOp::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: CacheLookupOp"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} -Status CacheLookupOp::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - std::vector cache_miss; - RETURN_IF_NOT_OK(keys_miss_->Pop(0, &cache_miss)); - // Ignore the case we have no cache miss, we can't return empty samples. - while (cache_miss.empty()) { - RETURN_IF_NOT_OK(keys_miss_->Pop(0, &cache_miss)); - } - // Special code for eoe - if (cache_miss.at(0) == eoe_row_id) { - *out = std::move(TensorRow(TensorRow::kFlagEOE)); - } else { - std::shared_ptr sample_ts; - RETURN_IF_NOT_OK(CreateSamplerTensor(&sample_ts, cache_miss.size())); - auto idPtr = sample_ts->begin(); - for (size_t i = 0; i < cache_miss.size(); ++i) { - *idPtr = cache_miss.at(i); - ++idPtr; - } - *out = {sample_ts}; - } - return Status::OK(); -} -Status CacheLookupOp::RegisterResources() { - RETURN_IF_NOT_OK(CacheBase::RegisterResources()); - RETURN_IF_NOT_OK(leaf_op_wp_.Register(tree_->AllTasks())); - return Status::OK(); -} -Status CacheLookupOp::ComputeColMap() { - // We don't know the column map at this point unless we contact the cache server - // to fetch the schema but the cache server may not have it at this point either. - // So we will just return OK and let MergeOp (our parent) to handle it. - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h deleted file mode 100644 index a636d9b17..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_LOOKUP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_LOOKUP_OP_H_ - -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h" - -namespace mindspore { -namespace dataset { -/// \brief provides a memory/disk cache that acts as a save-point within a mappable dataset. -/// \note For non-mappable dataset, please see CacheOp -/// \see CacheOp -class CacheLookupOp : public CacheBase, public SamplerRT { - public: - /// \brief Constructor - /// \note It takes the same argument as the base class. - /// \see CacheBase - CacheLookupOp(int32_t num_workers, int32_t op_connector_size, std::shared_ptr cache_client, - std::shared_ptr sampler) - : CacheBase(num_workers, op_connector_size, cache_client, sampler), SamplerRT(*(sampler.get())) {} - ~CacheLookupOp() = default; - // As a parallel op, we override these two functions - Status operator()() override; - Status WorkerEntry(int32_t worker_id) override; - // As a sampler, we override the following functions - Status ResetSampler(const bool failover_reset) override; - Status HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count) override; - Status InitSampler() override; - Status GetNextSample(TensorRow *out) override; - void Print(std::ostream &out, bool show_all) const override; - void SamplerPrint(std::ostream &out, bool show_all) const override; - bool AllowCacheMiss() override { return true; } - std::string Name() const override { return kCacheLookupOp; } - - protected: - Status ComputeColMap() override; - - private: - WaitPost leaf_op_wp_; - - Status RegisterResources() override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_LOOKUP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.cc deleted file mode 100644 index 640050c56..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.cc +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/system_pool.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -CacheMergeOp::~CacheMergeOp() = default; -void CacheMergeOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\n\n"; - } -} - -CacheMergeOp::CacheMergeOp(int32_t numWorkers, int32_t opConnectorSize, int32_t numCleaners, - std::shared_ptr cache_client) - : ParallelOp(numWorkers, opConnectorSize), - num_cleaners_(numCleaners), - cache_client_(std::move(cache_client)), - cache_missing_rows_(true) {} - -Status CacheMergeOp::operator()() { - // A queue of row id to let cleaner send cache miss rows to the cache server - // We don't want a small queue as this will block the parallel op workers. - // A row id is 8 byte integer. So bigger size doesn't consume a lot of memory. - static const int32_t queue_sz = 512; - io_que_ = std::make_unique>(queue_sz); - RETURN_IF_NOT_OK(io_que_->Register(tree_->AllTasks())); - - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - - RETURN_IF_NOT_OK( - tree_->LaunchWorkers(1, std::bind(&CacheMergeOp::CacheMissMaster, this), Name() + "::CacheMissMaster", id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&CacheMergeOp::CacheMissWorkerEntry, this, std::placeholders::_1), - Name() + "::CacheMissWorkerEntry", id())); - - // One dedicated thread to move TensorRow from the pool to the cache server - for (auto i = 0; i < num_cleaners_; ++i) { - RETURN_IF_NOT_OK( - tree_->AllTasks()->CreateAsyncTask("Cleaner", std::bind(&CacheMergeOp::Cleaner, this), nullptr, id())); - } - TaskManager::FindMe()->Post(); - TensorRow new_row; - auto child_iterator = std::make_unique(this, 0, kCacheHitChildIdx); - int64_t ctr = 0; - do { - RETURN_IF_NOT_OK(child_iterator->FetchNextTensorRow(&new_row)); - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(ctr++ % num_workers_)]->EmplaceBack(std::move(new_row))); - } while (!new_row.eof()); - - return Status::OK(); -} - -// Each parallel worker will pop from the CacheHit stream. If there is a missing TensorRow, we will wait -// until it shows up in the pool. -Status CacheMergeOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - TensorRow new_row; - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&new_row)); - while (!new_row.eof()) { - if (new_row.eoe()) { - RETURN_IF_NOT_OK(EoeReceived(worker_id)); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&new_row)); - } else { - if (new_row.empty()) { - auto row_id = new_row.getId(); - // Block until the row shows up in the pool. - RETURN_IF_NOT_OK(cache_miss_.PopFront(row_id, &new_row)); - } - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(new_row))); - - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&new_row)); - } - } - RETURN_IF_NOT_OK(EofReceived(worker_id)); - return Status::OK(); -} - -Status CacheMergeOp::CacheMissMaster() { - missWorkers_in_queues_.Init(num_workers_, oc_queue_size_); - RETURN_IF_NOT_OK(missWorkers_in_queues_.Register(tree_->AllTasks())); - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(cache_client_->CacheSchema(column_name_id_map())); - TensorRow new_row; - auto child_iterator = std::make_unique(this, 0, kCacheMissChildIdx); - int64_t ctr = 0; - do { - RETURN_IF_NOT_OK(child_iterator->FetchNextTensorRow(&new_row)); - RETURN_IF_NOT_OK( - missWorkers_in_queues_[static_cast(ctr++ % num_workers_)]->EmplaceBack(std::move(new_row))); - } while (!new_row.eof()); - return Status::OK(); -} - -Status CacheMergeOp::CacheMissWorkerEntry(int32_t workerId) { - TaskManager::FindMe()->Post(); - // We will simply pop TensorRow from the stream and insert them into the pool and - // wake up any worker that is awaiting on the missing TensorRow. - // If we see an eoe, ignore it. For eof, we exit. - // Before we start, cache the schema at the server. Pick one of the workers - // do it. The schema should have been done at prepare time. - - TensorRow new_row; - RETURN_IF_NOT_OK(missWorkers_in_queues_[workerId]->PopFront(&new_row)); - while (!new_row.eof()) { - if (new_row.eoe()) { - // Ignore it. - MS_LOG(DEBUG) << "Ignore eoe"; - // However we need to flush any left over from the async write buffer. But any error - // we are getting will just to stop caching but the pipeline will continue - Status rc = cache_client_->FlushAsyncWriteBuffer(); - if (rc.IsError()) { - cache_missing_rows_ = false; - if (rc == StatusCode::kMDOutOfMemory || rc == kMDNoSpace) { - cache_client_->ServerRunningOutOfResources(); - } else { - MS_LOG(INFO) << "Async row flushing not successful: " << rc.ToString(); - } - } - } else { - row_id_type row_id = new_row.getId(); - if (row_id < 0) { - std::string errMsg = - "[Internal ERROR] row id should be greater than or equal to 0, but got: " + std::to_string(row_id); - RETURN_STATUS_UNEXPECTED(errMsg); - } - if (cache_missing_rows_) { - // Technically number of this row shows up in the cache miss stream is equal to the number - // of P() call. However the cleaner wants it too. So we need an extra copy. - TensorRowCacheRequest *rq; - RETURN_IF_NOT_OK(GetRq(row_id, &rq)); - if (rq->GetState() == TensorRowCacheRequest::State::kEmpty) { - // We will send the request async. But any error we most - // likely ignore and continue. - Status rc = rq->AsyncSendCacheRequest(cache_client_, new_row); - if (rc.IsOk()) { - RETURN_IF_NOT_OK(io_que_->EmplaceBack(row_id)); - } else if (rc == StatusCode::kMDOutOfMemory || rc == kMDNoSpace) { - cache_missing_rows_ = false; - cache_client_->ServerRunningOutOfResources(); - } - } - } - RETURN_IF_NOT_OK(cache_miss_.Add(row_id, std::move(new_row))); - } - RETURN_IF_NOT_OK(missWorkers_in_queues_[workerId]->PopFront(&new_row)); - } - return Status::OK(); -} - -Status CacheMergeOp::Cleaner() { - TaskManager::FindMe()->Post(); - while (true) { - row_id_type row_id; - RETURN_IF_NOT_OK(io_que_->PopFront(&row_id)); - if (row_id < 0) { - break; - } - // Locate the cache request - TensorRowCacheRequest *rq; - RETURN_IF_NOT_OK(GetRq(row_id, &rq)); - // If already flushed, move on to the next one. - if (rq->GetState() == TensorRowCacheRequest::State::kClean) { - continue; - } - Status rc = rq->CheckCacheResult(); - if (rc.IsError()) { - // If interrupt, time to quit. - if (rc == StatusCode::kMDInterrupted) { - return Status::OK(); - } else if (rc == StatusCode::kMDOutOfMemory || rc == kMDNoSpace) { - // The server is hitting some limit and we will turn off caching from now on. - cache_missing_rows_ = false; - cache_client_->ServerRunningOutOfResources(); - } else { - MS_LOG(INFO) << "Cache row not successful: " << rc.ToString(); - // Bad rc should not bring down the pipeline. We will simply continue and - // change the state back to empty. We don't need a CAS from CLEAN back to EMPTY. - rq->SetState(TensorRowCacheRequest::State::kEmpty); - } - } - } - return Status::OK(); -} - -Status CacheMergeOp::PrepareOperator() { // Run any common code from super class first before adding our own - // specific logic - CHECK_FAIL_RETURN_UNEXPECTED( - child_.size() == kNumChildren, - "[Internal ERROR] Incorrect number of children of CacheMergeOp, required num is 2, but got:" + - std::to_string(child_.size())); - RETURN_IF_NOT_OK(DatasetOp::PrepareOperator()); - // Get the computed check sum from all ops in the cache miss class - uint32_t cache_crc = DatasetOp::GenerateCRC(child_[kCacheMissChildIdx]); - // This is a mappable cache op so the id's need to be generated. - // Construct the cache - const bool generate_ids = false; - Status rc = cache_client_->CreateCache(cache_crc, generate_ids); - if (rc.StatusCode() == StatusCode::kMDDuplicateKey) { - // We are told the cache has been created already. - MS_LOG(INFO) << "Cache created already"; - rc = Status::OK(); - } - RETURN_IF_NOT_OK(rc); - return Status::OK(); -} - -Status CacheMergeOp::ComputeColMap() { - CHECK_FAIL_RETURN_UNEXPECTED(child_[kCacheMissChildIdx] != nullptr, "[Internal ERROR] cache miss stream is empty."); - if (column_name_id_map().empty()) { - column_name_id_map_ = child_[kCacheMissChildIdx]->column_name_id_map(); - } - CHECK_FAIL_RETURN_UNEXPECTED(!column_name_id_map().empty(), - "Invalid data, column_name_id_map of CacheMergeOp is empty."); - return Status::OK(); -} - -Status CacheMergeOp::EoeReceived(int32_t worker_id) { - // Send the eoe up. - MS_LOG(DEBUG) << "Cache merge sending eoe"; - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOE))); - return Status::OK(); -} - -// Base-class override for handling cases when an eof is received. -Status CacheMergeOp::EofReceived(int32_t worker_id) { - // Send the eof up. - MS_LOG(DEBUG) << "Cache merge sending eof"; - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOF))); - return Status::OK(); -} - -Status CacheMergeOp::GetRq(row_id_type row_id, CacheMergeOp::TensorRowCacheRequest **out) { - RETURN_UNEXPECTED_IF_NULL(out); - std::unique_lock lock(mux_); - auto it = io_request_.find(row_id); - if (it != io_request_.end()) { - *out = it->second.GetMutablePointer(); - } else { - // We will create a new one. - auto alloc = SystemPool::GetAllocator(); - auto r = io_request_.emplace(row_id, MemGuard>(alloc)); - if (r.second) { - auto &mem = r.first->second; - RETURN_IF_NOT_OK(mem.allocate(1)); - *out = mem.GetMutablePointer(); - } else { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] map insert fail."); - } - } - return Status::OK(); -} - -Status CacheMergeOp::TensorRowCacheRequest::AsyncSendCacheRequest(const std::shared_ptr &cc, - const TensorRow &row) { - auto expected = State::kEmpty; - if (st_.compare_exchange_strong(expected, State::kDirty)) { - // We will do a deep copy but write directly into CacheRequest protobuf or shared memory - Status rc = cc->AsyncWriteRow(row); - if (rc.StatusCode() == StatusCode::kMDNotImplementedYet) { - cleaner_copy_ = std::make_shared(cc.get()); - rc = cleaner_copy_->SerializeCacheRowRequest(cc.get(), row); - if (rc.IsOk()) { - // Send the request async. The cleaner will check the return code. - rc = cc->PushRequest(cleaner_copy_); - } - } else if (rc.IsOk()) { - // Set the state to clean even though it still sits in the cache client async buffer. - // The cleaner will then ignore it once the state is clean. - st_ = State::kClean; - } - if (rc.IsError()) { - // Clean up the shared pointer and reset the state back to empty - cleaner_copy_.reset(); - st_ = State::kEmpty; - } - return rc; - } - return Status::OK(); -} - -Status CacheMergeOp::TensorRowCacheRequest::CheckCacheResult() { - auto expected = State::kDirty; - if (st_.compare_exchange_strong(expected, State::kClean)) { - // Success or not, we will release the memory. - // We simply move it out of the structure and let it go out of scope. - auto cache_request = std::move(cleaner_copy_); - RETURN_IF_NOT_OK(cache_request->Wait()); - return Status::OK(); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h deleted file mode 100644 index 3ba8b0042..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_MERGE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_MERGE_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/engine/cache/cache_client.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/queue_map.h" -#include "mindspore-lite/minddata/dataset/util/semaphore.h" - -namespace mindspore { -namespace dataset { -/// \brief Provides method to merge two streams (one from CacheLookup and one from cache miss stream) into one single -/// stream -class CacheMergeOp : public ParallelOp { - public: - // Some handshake structures between CacheMissWorkerEntry and Cleaner - class TensorRowCacheRequest { - public: - enum class State : uint8_t { - kEmpty = 0, // Initial state. Row hasn't arrived from cache miss stream yet. - kDirty = 1, // Cleaner hasn't flushed it to the cache server yet. - kClean = 2 // The row has been flushed already. - }; - TensorRowCacheRequest() : st_(State::kEmpty) {} - ~TensorRowCacheRequest() = default; - /// Getter and Setter of the state - State GetState() const { return st_; } - void SetState(State newState) { st_ = newState; } - /// Take a tensor row and send rpc call to the server async - /// \param cc Cache client of the CacheMergeOp - /// \param row TensorRow to be sent to the server - /// \return Status object - /// \note Thread safe - Status AsyncSendCacheRequest(const std::shared_ptr &cc, const TensorRow &row); - - /// \brief We send the row to the server async so the CacheMissWorkerEntry can continue. - /// It is the cleaner that will check the result. - /// \return Status object - Status CheckCacheResult(); - - private: - std::atomic st_; - std::shared_ptr cleaner_copy_; - }; - - constexpr static int kNumChildren = 2; // CacheMergeOp has 2 children - constexpr static int kCacheHitChildIdx = 0; // Cache hit stream - constexpr static int kCacheMissChildIdx = 1; // Cache miss stream - - /// \brief Constructor - /// \param numWorkers Number of parallel workers as a derived class of ParallelOp - /// \param opConnector Size Connector size as a derived class of ParallelOp - /// \param numCleaners Number of cleaners to move cache miss rows into the cache server - /// \param cache_client CacheClient to communicate with the Cache server - CacheMergeOp(int32_t numWorkers, int32_t opConnectorSize, int32_t numCleaners, - std::shared_ptr cache_client); - ~CacheMergeOp(); - void Print(std::ostream &out, bool show_all) const override; - std::string Name() const override { return kCacheMergeOp; } - - friend std::ostream &operator<<(std::ostream &out, const CacheMergeOp &mo) { - mo.Print(out, false); - return out; - } - /// \brief Master thread responsible to spawn all the necessary worker threads for the two streams and - /// the threads for the cleaners. - /// \return - Status operator()() override; - - /// \brief Entry function for worker thread that fetch rows from CacheLookupOp - /// \param workerId - /// \return Status object - Status WorkerEntry(int32_t workerId) override; - - /// \brief Perform specific post-operations on CacheOp - /// \return Status The status code returned - Status PrepareOperator() override; - - /// \brief Main thread to fetch rows from the miss child and assign it to workers - /// \return Status The status code returned - Status CacheMissMaster(); - - /// \brief Entry function for worker thread that fetch rows from the cache miss stream - /// \param workerId - /// \return Status object - Status CacheMissWorkerEntry(int32_t workerId); - - /// \brief Base-class override for eoe handling - /// \param worker_id - /// \return Status object - Status EoeReceived(int32_t worker_id) override; - - /// \brief Base-class override for handling cases when an eof is received. - /// \param worker_id - The worker id - /// \return Status The status code returned - Status EofReceived(int32_t worker_id) override; - - protected: - Status ComputeColMap() override; - - private: - std::mutex mux_; - QueueMap cache_miss_; - std::map>> io_request_; - std::unique_ptr> io_que_; - int32_t num_cleaners_; - std::shared_ptr cache_client_; - std::atomic cache_missing_rows_; - - QueueList missWorkers_in_queues_; - - /// \brief Locate the cache request from the io_request_ map - /// \param row_id - /// \param out pointer to the cache request - /// \return Status object - Status GetRq(row_id_type row_id, TensorRowCacheRequest **out); - - /// \brief These are the entry functions for the cleaner threads. Each cleaner is responsible for - /// moving cache miss TensorRow into the CacheServer. - /// \return Status object - Status Cleaner(); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_MERGE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.cc deleted file mode 100644 index 8c433f968..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.cc +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -// Constructor of CacheOp -CacheOp::CacheOp(int32_t num_workers, int32_t op_connector_size, std::shared_ptr cache_client, - std::shared_ptr sampler) - : CacheBase(num_workers, op_connector_size, std::move(cache_client), std::move(sampler)), - num_guys_in_(0), - phase_(Phase::kBuildPhase) {} - -// Destructor -CacheOp::~CacheOp() = default; - -// This class functor will provide the master loop that drives the logic for performing the work -Status CacheOp::operator()() { - RETURN_UNEXPECTED_IF_NULL(tree_); - if (!sampler_) { - RETURN_STATUS_UNEXPECTED("Invalid sampler, CacheOp requires a sampler before it can be executed, but got nullptr."); - } - RETURN_IF_NOT_OK(RegisterResources()); - - // required task group sync after launching workers - TaskManager::FindMe()->Post(); - // Wait for the workers to finish caching the rows. - RETURN_IF_NOT_OK(WaitForCachingAllRows()); - // Current repeats and current epochs may have increased when caching all rows with DatasetOp::GetNextInput. - // But they shouldn't be increased because now cache op is starting to act as a leaf and its epoch hasn't started. - op_current_repeats_ = 0; - op_current_epochs_ = 0; - RETURN_IF_NOT_OK(FetchSamplesToWorkers()); - return Status::OK(); -} - -Status CacheOp::CacheAllRows(int32_t worker_id) { - // If the current phase is to fill the cache, do it then. - if (phase_ == Phase::kBuildPhase) { - MS_LOG(INFO) << "CacheOp first epoch SAVE mode started. Worker: " << worker_id; - // SAVE mode loop - TensorRow row; - RETURN_IF_NOT_OK(cache_workers_in_queue_[worker_id]->PopFront(&row)); - while (!row.eof()) { - if (!row.eoe()) { - Status rc; - // Do the Async write if we attach to the shared memory. - rc = cache_client_->AsyncWriteRow(row); - if (rc.StatusCode() == StatusCode::kMDNotImplementedYet) { - RETURN_IF_NOT_OK(cache_client_->WriteRow(row)); - } else if (rc.IsError()) { - return rc; - } - } else { - // In a repeat-over-cache scenario, any of the "real" leaf operators below us have been set up - // as non-repeating leaf ops. As such, they only do one epoch and then quit. Since we got the - // the eoe to indicate the end of the epoch, we should next expect to get the eof. - // Drain this eof so that we don't leave it sitting there on a connector that we'll never fetch - // from again. - RETURN_IF_NOT_OK(cache_workers_in_queue_[worker_id]->PopFront(&row)); - if (!row.eof()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Cache op expects to get an eof after eoe from child."); - } - break; - } - RETURN_IF_NOT_OK(cache_workers_in_queue_[worker_id]->PopFront(&row)); - } - } - // Let the main guy know we are done. - auto last_guy_in = num_guys_in_.fetch_add(1); - if ((last_guy_in + 1) == num_workers_) { - rows_cache_done_.Set(); - } else { - // Let's do a sync up here. - RETURN_IF_NOT_OK(rows_cache_done_.Wait()); - } - return Status::OK(); -} -Status CacheOp::WaitForCachingAllRows() { - // Fetch all rows and wait for workers to cache them - if (phase_ == Phase::kBuildPhase) { - // We will take the chance to cache the schema at the server. - RETURN_IF_NOT_OK(cache_client_->CacheSchema(column_name_id_map())); - // SAVE mode loop - TensorRow new_row; - auto child_iterator = std::make_unique(this, 0, 0); - int64_t ctr = 0; - do { - RETURN_IF_NOT_OK(child_iterator->FetchNextTensorRow(&new_row)); - RETURN_IF_NOT_OK(cache_workers_in_queue_[ctr++ % num_workers_]->EmplaceBack(std::move(new_row))); - } while (!new_row.eof()); - - for (int32_t i = 1; i < num_workers_; i++) { - RETURN_IF_NOT_OK(cache_workers_in_queue_[ctr++ % num_workers_]->EmplaceBack(TensorRow(TensorRow::kFlagEOF))); - } - } - - // Wait for the workers to finish caching the rows. - RETURN_IF_NOT_OK(rows_cache_done_.Wait()); - // Move from build phase to fetch phase if we are the one to fill the cache - if (phase_ == Phase::kBuildPhase) { - RETURN_IF_NOT_OK(cache_client_->FlushAsyncWriteBuffer()); // One more flush - RETURN_IF_NOT_OK(cache_client_->BuildPhaseDone()); - // Move to the next phase - phase_ = Phase::kFetchPhase; - } - // If we are not the one to create the cache, - // wait until the state changed from build phase to fetch base. - bool BuildPhaseDone = true; - do { - int8_t out; - RETURN_IF_NOT_OK(cache_client_->GetState(&out)); - auto state = static_cast(out); - switch (state) { - case CacheServiceState::kBuildPhase: - // Do nothing. Continue to wait. - BuildPhaseDone = false; - std::this_thread::sleep_for(std::chrono::milliseconds(kPhaseCheckIntervalInMilliSec)); - break; - case CacheServiceState::kFetchPhase: - BuildPhaseDone = true; - break; - case CacheServiceState::kOutOfMemory: - RETURN_STATUS_OOM("Out of memory."); - case CacheServiceState::kNoSpace: - RETURN_STATUS_ERROR(StatusCode::kMDNoSpace, - "Cache server is running of out spill storage, check memory usage."); - case CacheServiceState::kNone: - case CacheServiceState::kError: - default: - RETURN_STATUS_UNEXPECTED("Unexpected Cache server state: " + std::to_string(out)); - } - } while (!BuildPhaseDone); - // Get statistics from the server, and if we are not the one to create the cache, - // wait until the state changed from build phase to fetch base. - CacheServiceStat stat{}; - RETURN_IF_NOT_OK(cache_client_->GetStat(&stat)); - const row_id_type min_key = stat.min_row_id; - const row_id_type max_key = stat.max_row_id; - num_rows_ = max_key - min_key + 1; - MS_LOG(INFO) << "Number of rows cached: " << num_rows_; - MS_LOG(INFO) << "Number of rows cached in memory : " << stat.num_mem_cached; - MS_LOG(INFO) << "Number of rows spilled to disk : " << stat.num_disk_cached; - MS_LOG(INFO) << "Average cache size : " << stat.avg_cache_sz; - // Now all rows are cached and we have done a sync point check up. Next phase is - // is pick up fetch input from sampler and pass up to the caller. - RETURN_IF_NOT_OK(sampler_->HandshakeRandomAccessOp(this)); - return Status::OK(); -} -Status CacheOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(CacheAllRows(worker_id)); - RETURN_IF_NOT_OK(FetchFromCache(worker_id)); - return Status::OK(); -} -Status CacheOp::RegisterResources() { - RETURN_UNEXPECTED_IF_NULL(tree_); - cache_workers_in_queue_.Init(num_workers_, oc_queue_size_); - RETURN_IF_NOT_OK(cache_workers_in_queue_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(CacheBase::RegisterResources()); - RETURN_IF_NOT_OK(rows_cache_done_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(keys_miss_->Register(tree_->AllTasks())); - return Status::OK(); -} - -// Base-class override for special eoe handler. -// CacheOp must override this because it shall not perform default handling of eoe. Instead -// the CacheOp manages actions related to the end of the epoch. -Status CacheOp::EoeReceived(int32_t worker_id) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} -// Base-class override for handling cases when an eof is received. -Status CacheOp::EofReceived(int32_t worker_id) { - // eofReceived is overloaded because we want to manually handle this eof. - // Specifically, the default behavior is to pack it and flow it up to the next connection. - // In this case, we want a no-op behavior so that we can perform correct action. - return Status::OK(); -} - -Status CacheOp::PrepareOperator() { - // Run any common code from super class first before adding our own - RETURN_IF_NOT_OK(DatasetOp::PrepareOperator()); - // Get the computed check sum from all ops in our cache path below us and ask the cache op to create it's cache - uint32_t cache_crc = DatasetOp::GenerateCRC(shared_from_this()); - // This is a non-mappable cache op so the id's need to be generated. - // Construct the cache - const bool generate_ids = true; - Status rc = cache_client_->CreateCache(cache_crc, generate_ids); - if (rc.StatusCode() == StatusCode::kMDDuplicateKey) { - // We are told the cache has been created already. So we skip the build phase. - phase_ = Phase::kFetchPhase; - rc = Status::OK(); - } - RETURN_IF_NOT_OK(rc); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h deleted file mode 100644 index 169d0419f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_OP_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_base_op.h" - -namespace mindspore { -namespace dataset { -/// \brief CacheOp provides a memory/disk cache that acts as a save-point within a non-mappable dataset. -/// \note For mappable dataset, please see CacheLookupOp. -/// \see CacheLookupOp -class CacheOp : public CacheBase, public RandomAccessOp { - public: - // This CacheOp is for non-mappable case where it is divided into two phases. - // The first phase is we cache all the rows from the child (and let the cache server - // assigns row id). No read access in the first phase. Once the cache is fully built, - // we switch to second phase and fetch requests from the sampler. - enum class Phase : uint8_t { kBuildPhase = 0, kFetchPhase = 1 }; - constexpr static int32_t kPhaseCheckIntervalInMilliSec = 100; - - /// \brief Constructor of CacheOp - /// \note The builder class should be used to call it. - /// \param num_workers The number of worker threads. - /// \param op_connector_size The size of each queue in the connector. - CacheOp(int32_t num_workers, int32_t op_connector_size, std::shared_ptr cache_client, - std::shared_ptr sampler); - - // Destructor - ~CacheOp(); - - /// \brief Base-class override for special eoe handler. - /// \notes CacheOp must override this because it shall not perform default handling of eoe. Instead - /// the CacheOp manages actions related to the end of the epoch. - /// \return Status The status code returned - Status EoeReceived(int32_t worker_id) override; - - /// \brief Base-class override for handling cases when an eof is received. - /// \param worker_id - The worker id - /// \return Status The status code returned - Status EofReceived(int32_t worker_id) override; - - // \brief Class functor operator (). - /// \return Status The status code returned - Status operator()() override; - - /// \brief Entry function for worker thread that fetch rows from CacheLookupOp - /// \param workerId - /// \return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; - - /// \brief Base-class override for handling cases if we allow cache miss. - bool AllowCacheMiss() override { return false; } - - /// \brief Base-class override for the name of this operator. - std::string Name() const override { return kCacheOp; } - - /// \brief Perform specific post-operations on CacheOp - /// \return Status The status code returned - Status PrepareOperator() override; - - private: - WaitPost rows_cache_done_; - std::atomic num_guys_in_; - Phase phase_; - - QueueList cache_workers_in_queue_; - /// \brief The main thread will wait until all the rows are cached and will start the handshake with the sampler. - /// \return Status object - Status WaitForCachingAllRows(); - - Status CacheAllRowsMaster(); - - /// \brief For non-mappable dataset, there is a build phase where we cache all the rows. - /// \return Status object - Status CacheAllRows(int32_t worker_id); - Status RegisterResources() override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CACHE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.cc deleted file mode 100644 index c10d889fc..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.cc +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -// Constructor of the ConcatOp. -ConcatOp::ConcatOp(const std::shared_ptr &sampler, - const std::vector> &children_flag_and_nums, - const std::vector> &children_start_end_index, - const std::vector &children_sizes) - : ConcatOp() { - children_flag_and_nums_ = children_flag_and_nums; - children_start_end_index_ = children_start_end_index; - children_sizes_ = children_sizes; - children_sizes_ori_ = children_sizes; - std::shared_ptr random_sampler = std::dynamic_pointer_cast(sampler); - std::shared_ptr distribute_sampler = std::dynamic_pointer_cast(sampler); - - if (random_sampler != nullptr && distribute_sampler == nullptr) { - // global sample mode - global_shuffle_ = true; - discrete_random_ = std::make_unique>(children_sizes_.begin(), children_sizes_.end()); - rnd_.seed(seed_); - children_exhausted_ = std::vector(children_sizes_.size(), false); - } else if (random_sampler == nullptr && distribute_sampler != nullptr) { - // distributed sample mode - num_shard_ = static_cast(distribute_sampler->GetDeviceNum()); - shard_index_ = static_cast(distribute_sampler->GetDeviceID()); - } -} - -ConcatOp::ConcatOp() - : PipelineOp(0), - cur_child_(0), - verified_(false), - sample_number_(0), - num_shard_(1), - shard_index_(0), - discrete_random_(nullptr), - global_shuffle_(false), - seed_(GetSeed()) {} - -// A function that prints info about the Operator -void ConcatOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nDatasets: " << child_.size() << "\n\n"; - } -} - -// This definition is added to pass the cyclomatic complexity rule of <= 20 units -// The NOLINT directive is to disable cpplint check. -// Clang format and cpplint give conflicting recommendations on this line below. -#define f(fv, sv, shard_index) \ - (((fv) == -1 && (sv) == -1) || ((fv) < (sv) && (shard_index) >= (fv) && (shard_index) < (sv)) || \ - ((fv) > (sv) && ((shard_index) >= (fv) || (shard_index) < (sv)))) // NOLINT - -Status ConcatOp::Verify(int32_t id, const TensorRow &new_row) { - if (id == 0) { - // Obtain the data type and data rank in child[0] - for (auto item : new_row) { - data_type_.push_back(item->type()); - data_rank_.push_back(item->Rank()); - } - } else { - // Compare the data type and data rank with these in child[0] - int32_t index = 0; - for (auto item : new_row) { - if (item->type() != data_type_[index]) { - RETURN_STATUS_UNEXPECTED( - "Concat: the data types of the two datasets to be concatenated should be the same, but got: " + - data_type_[index].ToString() + " and " + item->type().ToString() + "."); - } - if (item->Rank() != data_rank_[index]) { - RETURN_STATUS_UNEXPECTED( - "Concat: the data tensor rank of the two datasets to be concatenated should be the same, but got: " + - std::to_string(data_rank_[index]) + " and " + std::to_string(item->Rank()) + "."); - } - index++; - } - } - verified_ = true; - return Status::OK(); -} - -// We need to overwrite the super class ComputeColMap here because the number of children is more than 1. -Status ConcatOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - // Obtain columns_name_id_map from child_[0] - column_name_id_map_ = child_[0]->column_name_id_map(); - if (column_name_id_map_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Child column name map cannot be empty!"); - } - // Verify all children have the same column name map - for (size_t i = 0; i < child_.size(); ++i) { - if (child_[i]->column_name_id_map() != column_name_id_map_) { - RETURN_STATUS_UNEXPECTED( - "Invalid columns, 'column name' or 'column order' of concat datasets should be the same."); - } - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Gets the number of classes -Status ConcatOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - int64_t max_num_classes = -1; - for (const auto &child : child_) { - // Choose a dataset which can get valid num_classes - int64_t tmp_num_classes = -1; - RETURN_IF_NOT_OK(child->GetNumClasses(&tmp_num_classes)); - if (tmp_num_classes > max_num_classes) { - max_num_classes = tmp_num_classes; - } - } - *num_classes = max_num_classes; - return Status::OK(); -} -Status ConcatOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] ConcatOp is an inlined operator."); } - -bool ConcatOp::IgnoreSample() { - bool is_not_mappable_or_second_ne_zero = true; - - if (!children_flag_and_nums_.empty()) { - const bool is_not_mappable = children_flag_and_nums_[cur_child_].first != 0 ? true : false; - const bool second_ne_zero = children_flag_and_nums_[cur_child_].second == 0 ? true : false; - is_not_mappable_or_second_ne_zero = is_not_mappable || second_ne_zero; - } - bool ret = true; - if (sample_number_ % num_shard_ == shard_index_ && is_not_mappable_or_second_ne_zero) { - ret = false; - } else if (!is_not_mappable_or_second_ne_zero) { - // if dataset is mappable or generator dataset which source is not yield, - // get the start and end subscripts of valid values - int fv = children_start_end_index_[cur_child_].first, sv = children_start_end_index_[cur_child_].second; - - // determine whether the data allocated to the current shard id is false data - if (f(fv, sv, shard_index_)) { - ret = false; - } - } - - if (is_not_mappable_or_second_ne_zero) { - sample_number_++; - } - return ret; -} - -Status ConcatOp::SampleInSequence(TensorRow *row, bool is_pipeline_mode) { - row->reset(); - bool is_not_mappable_or_second_ne_zero = true; - - if (!children_flag_and_nums_.empty()) { - const bool is_not_mappable = children_flag_and_nums_[cur_child_].first != 0 ? true : false; - const bool second_ne_zero = children_flag_and_nums_[cur_child_].second == 0 ? true : false; - // unmappable or iterable Generator - is_not_mappable_or_second_ne_zero = is_not_mappable || second_ne_zero; - } - - uint64_t start_time = GetSyscnt(); - Status s = is_pipeline_mode ? child_[cur_child_]->GetNextRow(row) : child_[cur_child_]->GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - - if (!row->eoe() && !row->eof()) { - if (!verified_) { - RETURN_IF_NOT_OK(Verify(static_cast(cur_child_), *row)); - } - - if (IgnoreSample()) { - s = is_pipeline_mode ? GetNextRow(row) : GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - } - } else if (row->eoe()) { - // if last child, send out eoe and reset epoch - if (cur_child_ == child_.size() - 1) { - // reset - cur_child_ = 0; - verified_ = false; - UpdateRepeatAndEpochCounter(); - } else { - // mappable and mappable Generator - if (!is_not_mappable_or_second_ne_zero) { - sample_number_ += children_flag_and_nums_[cur_child_].second; - } - cur_child_++; - verified_ = false; - s = is_pipeline_mode ? GetNextRow(row) : GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - } - } else if (row->eof()) { - CHECK_FAIL_RETURN_UNEXPECTED(cur_child_ == 0, "[Internal ERROR] Received an unexpected EOF."); - for (size_t i = cur_child_ + 1; i < child_.size(); i++) { - start_time = GetSyscnt(); - s = is_pipeline_mode ? child_[i]->GetNextRow(row) : child_[i]->GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - CHECK_FAIL_RETURN_UNEXPECTED(row->eof(), "[Internal ERROR] Row must be an EOF."); - } - } - return Status::OK(); -} - -Status ConcatOp::SampleInGlobal(TensorRow *row, bool is_pipeline_mode) { - row->reset(); - // select child id - auto child_id = (*discrete_random_)(rnd_); - MS_LOG(INFO) << "sample from child " << child_id; - - uint64_t start_time = GetSyscnt(); - Status s = is_pipeline_mode ? child_[child_id]->GetNextRow(row) : child_[child_id]->GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - - if (!row->eoe() && !row->eof()) { - // normal case, but reduce total sample numbers for without replacement sampling - children_sizes_[child_id] = std::max(--children_sizes_[child_id], static_cast(0)); - // change distribution - discrete_random_->param({children_sizes_.begin(), children_sizes_.end()}); - return Status::OK(); - } else if (row->eoe()) { - // if one child has drained, sample from other children - children_exhausted_[child_id] = true; - children_sizes_[child_id] = 0; - - // check all children have been exhausted - MS_LOG(INFO) << "child " << child_id << " node has been drained, check all children status (next row is eoe)."; - // always get eoe from child 0, since random {0, 0, 0} return 0, exhaust other children - for (auto c = 0; c < children_exhausted_.size(); c++) { - TensorRow eoe; - if (!children_exhausted_[c]) { - start_time = GetSyscnt(); - s = is_pipeline_mode ? child_[c]->GetNextRow(&eoe) : child_[c]->GetNextRowPullMode(&eoe); - RETURN_IF_NOT_OK(s); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", eoe.FlagName()}})); - // for those variable dataset size, we cannot support currently - CHECK_FAIL_RETURN_UNEXPECTED( - eoe.eoe(), - "The actual size of dataset " + std::to_string(c) + - " does not match its defined size, maybe the dataset size is variable or `__len__` is incorrect."); - children_exhausted_[c] = true; - } - } - - // reset distribution - MS_LOG(INFO) << "reset all children."; - children_sizes_ = children_sizes_ori_; - children_exhausted_ = std::vector(children_sizes_.size(), false); - discrete_random_->param({children_sizes_.begin(), children_sizes_.end()}); - UpdateRepeatAndEpochCounter(); - } else if (row->eof()) { - // check all children have been drained - MS_LOG(INFO) << "Get eof from child " << child_id << ", drain eof of other children"; - for (size_t i = 0; i < child_.size(); i++) { - if (i != child_id) { - start_time = GetSyscnt(); - s = is_pipeline_mode ? child_[i]->GetNextRow(row) : child_[i]->GetNextRowPullMode(row); - RETURN_IF_NOT_OK(s); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - CHECK_FAIL_RETURN_UNEXPECTED(row->eof(), "[Internal ERROR] Row must be an EOF."); - } - } - } - return Status::OK(); -} - -Status ConcatOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (global_shuffle_) { - RETURN_IF_NOT_OK(SampleInGlobal(row)); - } else { - RETURN_IF_NOT_OK(SampleInSequence(row)); - } - return Status::OK(); -} - -Status ConcatOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (global_shuffle_) { - RETURN_IF_NOT_OK(SampleInGlobal(row, false)); - } else { - RETURN_IF_NOT_OK(SampleInSequence(row, false)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h deleted file mode 100644 index c1a82ce33..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CONCAT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CONCAT_OP_H_ - -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h" - -namespace mindspore { -namespace dataset { -class ConcatOp : public PipelineOp { - public: - // Constructor of the ConcatOp. - // @note The builder class should be used to call it - // @param op_connector_size - connector size - ConcatOp(); - ConcatOp(const std::shared_ptr &sampler, const std::vector> &children_flag_and_nums, - const std::vector> &children_start_end_index, - const std::vector &children_sizes); - - // Destructor - ~ConcatOp() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param ro - reference to the ConcatOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ConcatOp &ro) { - ro.Print(out, false); - return out; - } - - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kConcatOp; } - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - /// \brief Gets the number of classes - /// \param[out] num_classes the number of classes - /// \return Status - The status code return - Status GetNumClasses(int64_t *num_classes) override; - - Status GetNextRow(TensorRow *row) override; - - Status GetNextRowPullMode(TensorRow *const row) override; - - Status SampleInSequence(TensorRow *row, bool is_pipeline_mode = true); - - Status SampleInGlobal(TensorRow *row, bool is_pipeline_mode = true); - - /// Check if the current sample will be taken or dropped - /// \return bool - bool IgnoreSample(); - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - Status Verify(int32_t id, const TensorRow &new_row); - - std::unordered_map column_name_id_; // Mapping between col index and col name - std::vector data_type_; - std::vector data_rank_; - std::vector> children_flag_and_nums_; - std::vector> children_start_end_index_; - std::vector children_sizes_; - std::vector children_sizes_ori_; - std::vector children_exhausted_; - - size_t cur_child_; - bool verified_; - int64_t sample_number_; - - int32_t num_shard_; - int32_t shard_index_; - - std::unique_ptr> discrete_random_; - bool global_shuffle_; - uint32_t seed_; - std::mt19937 rnd_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_CONCAT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.cc deleted file mode 100644 index aed4248e9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.cc +++ /dev/null @@ -1,1314 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/gpu_item_connector.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#ifdef WITH_BACKEND -#include "mindspore/ccsrc/include/runtime/hardware_abstract/data_queue/data_queue_mgr.h" -#include "include/backend/distributed/embedding_cache/embedding_cache_utils.h" -#include "include/backend/distributed/embedding_cache/data_queue_manager.h" -#include "utils/ms_context.h" -#endif -namespace mindspore { -namespace dataset { -#ifdef WITH_BACKEND -using distributed::DataQueueManager; -using distributed::IdDataInfo; -using distributed::IndexDataInfo; -#endif - -namespace { -std::vector ConvertTensorRowToDataQueueItem(const TensorRow &row) { - std::vector items; - for (auto &i : row) { - device::DataQueueItem data_item; - data_item.data_len = static_cast(i->SizeInBytes()); - data_item.shapes = i->shape().AsVector(); - data_item.data_ptr = const_cast(static_cast(i->GetBuffer())); - data_item.data_type = i->type().ToString(); - items.emplace_back(std::move(data_item)); - } - return items; -} - -#ifdef WITH_BACKEND -std::vector DeepCopyConvertTensorRowToDataQueueItem(const TensorRow &row) { - std::vector items; - for (auto &i : row) { - device::DataQueueItem data_item; - data_item.data_len = static_cast(i->SizeInBytes()); - data_item.shapes = i->shape().AsVector(); - data_item.data_ptr = malloc(data_item.data_len); - MS_EXCEPTION_IF_NULL(data_item.data_ptr); - auto ret = memcpy_s(data_item.data_ptr, data_item.data_len, i->GetBuffer(), data_item.data_len); - if (ret != EOK) { - MS_LOG(EXCEPTION) << "Memcpy for data queue item failed, errno[" << ret << "]"; - } - data_item.data_type = i->type().ToString(); - - items.emplace_back(std::move(data_item)); - } - return items; -} -#endif - -void PushEpochEndToQueue(const std::string &channel_name) { -#ifdef WITH_BACKEND - const auto &id_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name).first; - MS_EXCEPTION_IF_NULL(id_data_queue); - IdDataInfo *data = new IdDataInfo(); - MS_EXCEPTION_IF_NULL(data); - data->end_of_epoch_ = true; - id_data_queue->Push(data); -#endif -} - -void PushFileEndToQueue(const std::string &channel_name) { -#ifdef WITH_BACKEND - const auto &id_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name).first; - MS_EXCEPTION_IF_NULL(id_data_queue); - IdDataInfo *data = new IdDataInfo(); - MS_EXCEPTION_IF_NULL(data); - data->end_of_file_ = true; - id_data_queue->Push(data); -#endif -} -} // namespace -DataQueueOp::DataQueueOp(const std::string channel_name, DeviceType device_type, int32_t device_id, bool send_epoch_end, - int32_t total_batch, bool create_data_info_queue) - : PipelineOp(1), - ascend_keep_waiting_(true), - num_workers_(kDeviceQueGpuNumThreads), - queue_capacity_(kDeviceQueGpuQueueCapacity), - channel_name_(channel_name), - device_type_(device_type), - device_id_(device_id), - send_epoch_end_(send_epoch_end), - stop_send_(false), - send_finished_(false), - total_batch_(total_batch), - create_data_info_queue_(create_data_info_queue), - data_info_queue_ptr_(nullptr), - first_fetch_flag_(false), - first_push_flag_(false), - send_summary_({}) { - std::shared_ptr cfg = GlobalContext::config_manager(); - dynamic_shape_ = cfg->dynamic_shape(); - - // Be careful when try to modified these num_workers_ and queue_capacity_, - // and we suggest num_workers_ * queue_capacity_ not greater than 16, because - // one worker one circular_pool with 1G pin memory, so num_workers_ * queue_capacity_ - // must limit to avoid memory overload -#ifdef WITH_BACKEND - MS_EXCEPTION_IF_NULL(MsContext::GetInstance()); - if (MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET) == kAscendDevice) { - ascend_data_queue_ = - device::DataQueueMgr::GetInstance().CreateDataQueue(kAscendDevice, channel_name, dynamic_shape_, 0, {}); - } - enable_prefetch_cache_pipeline_ = distributed::EmbeddingCacheTableManager::GetInstance().enable_pipeline(); -#endif -#ifdef ENABLE_DUMP_IR - md_channel_info_ = std::make_shared(channel_name_); -#endif -} - -DataQueueOp::~DataQueueOp() { -#ifdef ENABLE_DUMP_IR - // BFS iter execution tree to get send epoch from EpochControl Op - std::vector> child_node = this->Children(); - size_t node_index = 0; - int32_t num_epochs = 0; - while (child_node.size() != 0 && node_index < child_node.size()) { - auto node = child_node[node_index]; - if (node->Name() == kEpochCtrlOp) { - EpochCtrlOp *op = dynamic_cast(node.get()); - if (op != nullptr) { - num_epochs = op->NumEpochs(); - break; - } - } - auto child_child_node = node->Children(); - if (!child_child_node.empty()) { - (void)std::copy(child_child_node.begin(), child_child_node.end(), std::back_inserter(child_node)); - } - ++node_index; - } - - // won't print rdr if call stop_send manually or send infinite epoch - std::string rdr_msg = md_channel_info_->ToFormatString(); - if (!send_finished_ && !rdr_msg.empty() && num_epochs != -1) { - MS_LOG(WARNING) << rdr_msg; - } - MS_LOG(INFO) << "Release the DataQueueOp, channel name: " << channel_name_; -#endif -} - -void DataQueueOp::ReleaseData(void *addr, int32_t worker_id) { - if (addr != nullptr && worker_id >= 0 && worker_id < pool_.size()) { - pool_[worker_id]->Deallocate(addr); - } -} - -Status DataQueueOp::EoeReceived(int32_t) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -Status DataQueueOp::FilterMetadata(TensorRow *row) const { - std::unordered_map current_name_id_map = child_[0]->column_name_id_map(); - TensorRow output; - TensorRow tmp = *row; - std::vector to_keep_indices; - for (auto column : current_name_id_map) { - std::string column_name = column.first; - // Need to filter meta column start with kDftMetaColumnPrefix - size_t pos = column_name.find(kDftMetaColumnPrefix); - if (pos != std::string::npos && pos == 0) { - continue; - } - to_keep_indices.push_back(column.second); - } - if (to_keep_indices.size() == 0) { - std::string err_msg = "No effective column found, maybe all columns are meta column and will be filtered. "; - err_msg += "If you want to output meta column please rename column name to a new one which is not start with "; - err_msg += "\"" + std::string(kDftMetaColumnPrefix) + "\""; - RETURN_STATUS_UNEXPECTED(err_msg); - } - std::sort(to_keep_indices.begin(), to_keep_indices.end()); - (void)std::transform(to_keep_indices.begin(), to_keep_indices.end(), std::back_inserter(output), - [&tmp](const auto &it) { return std::move(tmp[it]); }); - row->CopyTimerTo(&output); - *row = std::move(output); - return Status::OK(); -} - -Status DataQueueOp::CheckExceptions(const TensorRow &row) const { - // this method checks if the row meets the conditions to be sent to TDT - for (const auto &item : row) { - CHECK_FAIL_RETURN_UNEXPECTED(item->type().IsNumeric(), - "Invalid datatype, cannot send string, or Python dict to device."); - } - return Status::OK(); -} - -Status DataQueueOp::operator()() { - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask("Detect first batch", - std::bind(&DataQueueOp::DetectFirstBatch, this), nullptr, id())); - TaskManager::FindMe()->Post(); - child_iterator_ = std::make_unique(this, 0, 0); - -#ifdef ENABLE_DUMP_IR - if (md_channel_info_ == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] RDR module init failed."); - } -#endif - if (device_type_ == DeviceType::Ascend) { -#ifdef WITH_BACKEND - if (create_data_info_queue_) { - // This place has a race condition with GetDataInfo, so the first one - // arrive here will do the initialize work. - { - std::unique_lock lock(data_info_mutex_); - if (data_info_queue_ptr_ == nullptr) { - data_info_queue_ptr_ = std::make_unique(kDataInfoQueueCapacity); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Register(tree_->AllTasks())); - } - } - } - RETURN_IF_NOT_OK(SendDataToAscend()); - -#endif - } else if (device_type_ == DeviceType::GPU) { -#ifdef WITH_BACKEND - if (create_data_info_queue_) { - // This place has a race condition with GetDataInfo, so the first one - // arrive here will do the initialize work. - { - std::unique_lock lock(data_info_mutex_); - if (data_info_queue_ptr_ == nullptr) { - data_info_queue_ptr_ = std::make_unique(kDataInfoQueueCapacity); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Register(tree_->AllTasks())); - } - } - } - RETURN_IF_NOT_OK(SendDataToGPU()); -#endif - } else if (device_type_ == DeviceType::CPU) { - RETURN_IF_NOT_OK(SendDataToCPU()); - } - - return Status::OK(); -} - -Status DataQueueOp::PushPrefetchDataToGPU() { -#ifdef WITH_BACKEND - TaskManager::FindMe()->Post(); - const auto &index_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name_).second; - MS_EXCEPTION_IF_NULL(index_data_queue); - bool eof_flag = false; - int64_t send_batch = 0; - - bool is_profiling_enable = GlobalContext::profiling_manager()->IsProfilingEnable(tree_); - uint64_t push_cost; - - while (!eof_flag && !DataQueueManager::GetInstance().IsClosed() && !device::DataQueueMgr::GetInstance().IsClosed() && - !TaskManager::FindMe()->Interrupted()) { - IndexDataInfo *data = index_data_queue->Pop(); - if (DataQueueManager::GetInstance().IsClosed() || device::DataQueueMgr::GetInstance().IsClosed()) { - // Terminate abnormally. - break; - } - MS_EXCEPTION_IF_NULL(data); - - eof_flag = data->end_of_file_; - if (data->data_ != nullptr) { - MS_EXCEPTION_IF_NULL(data->items_); - - auto status = RetryPushData(*(data->items_), is_profiling_enable, &push_cost); - delete data->items_; - delete data; - - RETURN_IF_NOT_OK(status); - - ++send_batch; - if (total_batch_ > 0 && send_batch >= total_batch_) { - break; - } - } else { - delete data; - } - } -#endif - return Status::OK(); -} - -Status DataQueueOp::PushDataToGPUCacheQueue(std::vector &&data_items) { -#ifdef WITH_BACKEND - std::vector *items = new std::vector(std::move(data_items)); - MS_EXCEPTION_IF_NULL(items); - if (items->empty()) { - MS_LOG(EXCEPTION) << "The data queue item is empty."; - } - const auto &id_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name_).first; - MS_EXCEPTION_IF_NULL(id_data_queue); - IdDataInfo *data = new IdDataInfo(items->at(0).data_ptr, items->at(0).data_len, items, false, false); - - MS_EXCEPTION_IF_NULL(data); - id_data_queue->Push(data); -#endif - - return Status::OK(); -} - -Status DataQueueOp::PushPrefetchDataToAscend() { -#ifdef WITH_BACKEND - TaskManager::FindMe()->Post(); - const auto &index_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name_).second; - MS_EXCEPTION_IF_NULL(index_data_queue); - MS_EXCEPTION_IF_NULL(ascend_data_queue_); - bool eof_flag = false; - int64_t send_batch = 0; - while (!eof_flag && !DataQueueManager::GetInstance().IsClosed() && ascend_data_queue_->IsOpen()) { - IndexDataInfo *data = index_data_queue->Pop(); - if (DataQueueManager::GetInstance().IsClosed() || !ascend_data_queue_->IsOpen()) { - // Terminate abnormally. - break; - } - MS_EXCEPTION_IF_NULL(data); - device::DataQueueStatus status; - - bool eoe_flag = data->end_of_epoch_; - eof_flag = data->end_of_file_; - if (data->data_ != nullptr) { - MS_EXCEPTION_IF_NULL(data->items_); - - if (ascend_data_queue_->QueueType() == "Ascend_MBUF") { - size_t batch_data_len = - std::accumulate(data->items_->begin(), data->items_->end(), static_cast(0), - [](size_t acc, const DataQueueItem &item) { return acc + item.data_len; }); - RETURN_IF_NOT_OK(WaitForAscendQueue(batch_data_len)); - } - - status = ascend_data_queue_->Push(*(data->items_)); - for (auto &item : *(data->items_)) { - MS_EXCEPTION_IF_NULL(item.data_ptr); - free(item.data_ptr); - } - delete data->items_; - delete data; - if (status != device::DataQueueStatus::SUCCESS) { - if (stop_send_) { - MS_LOG(INFO) << "stop_send received"; - return Status::OK(); - } - RETURN_STATUS_ERROR( - StatusCode::kMDTDTPushFailure, - "TDT Push data into device Failed, check the first error or TraceBack first, more checking advises are: " - "1) if training is not ready, error might raised by network computing operator or environment configuration. " - "2) other cases, checking info level log or search this error in mindspore's FAQ for detail solution."); - } - ++send_batch; - if (total_batch_ > 0 && send_batch >= total_batch_) { - break; - } - } else { - delete data; - if (send_epoch_end_ && eoe_flag) { - // empty data - status = ascend_data_queue_->Push({}); - bool break_loop = false; - RETURN_IF_NOT_OK(CheckPushStatus(status, stop_send_, &send_finished_, &break_loop)); - if (break_loop) { - break; - } - } - } - } -#endif - - return Status::OK(); -} - -Status DataQueueOp::PushDataToAscendCacheQueue(const TensorRow &curr_row) { -#ifdef WITH_BACKEND - std::vector *items = - new std::vector(DeepCopyConvertTensorRowToDataQueueItem(curr_row)); - MS_EXCEPTION_IF_NULL(items); - if (items->empty()) { - MS_LOG(EXCEPTION) << "The data queue item is empty."; - } - const auto &id_data_queue = DataQueueManager::GetInstance().GetDataQueue(channel_name_).first; - MS_EXCEPTION_IF_NULL(id_data_queue); - IdDataInfo *data = - new IdDataInfo(items->at(0).data_ptr, items->at(0).data_len, items, curr_row.eoe(), curr_row.eof()); - - MS_EXCEPTION_IF_NULL(data); - id_data_queue->Push(data); - - if (create_data_info_queue_) { - DATA_INFO data_info; - (void)std::transform(curr_row.begin(), curr_row.end(), std::back_inserter(data_info), - [](const std::shared_ptr &ts) { return std::make_pair(ts->type(), ts->shape()); }); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Add(data_info)); - } -#endif - return Status::OK(); -} - -Status DataQueueOp::WaitForAscendQueue(size_t batch_data_len) { - // Queue control logic for mbuf in host, to prevent from hang/exit abnormally - // case 1: If mbuf queue memory + next row memory < 2G then continue send, else suspend; - // case 2: Based on case 1, if element nums in mbuf < max_queue_size then continue send, else suspend; - // case 3: If row memory >= 1G, can only send 1 row each time, queue_size will always in [0, 1]; - // note: - // why need queue control: acltdtSendTensor will hang when queue is full, need to break this thread by ourselves - // how about dynamic shape: yes, memory_per_batch_ collect memory of rows in different shapes. - // how about row too large(>2G): we can promise the first row will be sent and hang in this while, but we dont - // know if the device will out of memory. If not oom, send next row, otherwise device returns error. - - // Calculate the memory of next row before sending - RETURN_UNEXPECTED_IF_NULL(ascend_data_queue_); - - size_t queue_size = ascend_data_queue_->QueryQueueSize(); - double row_memory = batch_data_len / 1024. / 1024. / 1024.; - memory_per_batch_.push_back(row_memory); - - const double max_queue_memory = 2.; - const size_t max_queue_size = 100; - const int64_t send_interval = 1000; - uint64_t start_time = GetSyscnt(); - while ((row_memory + CalMbufQueueMemory(queue_size) >= max_queue_memory || queue_size >= max_queue_size) && - queue_size != 0) { - RETURN_IF_INTERRUPTED(); - MS_LOG(DEBUG) << "Mbuf queue size: " << queue_size << ", max queue limit: " << max_queue_size << ". " - << "Next row memory: " << row_memory << ", Mbuf memory: " << CalMbufQueueMemory(queue_size); - - queue_size = ascend_data_queue_->QueryQueueSize(); - std::this_thread::sleep_for(std::chrono::microseconds(send_interval)); - } - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WaitForAscend", start_time)); - return Status::OK(); -} - -Status DataQueueOp::SendDataToAscend() { - MS_LOG(INFO) << "Device queue, sending data to Ascend."; - if (enable_prefetch_cache_pipeline_) { - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "Push prefetch data to ascend queue", std::bind(&DataQueueOp::PushPrefetchDataToAscend, this), nullptr, id())); - } - uint64_t batch_start_time = 0; - uint64_t end_time = 0; - uint64_t batch_record_start = 0; - uint64_t batch_record_end = 0; - int64_t send_batch = 0; - int32_t tdt_cost = 0; - int32_t connector_size = 0; - int32_t connector_capacity = 0; - bool is_break_loop = false; - double row_timer_start = 0; - - std::shared_ptr cfg = GlobalContext::config_manager(); - int64_t sending_num = cfg->sending_batches(); // Get the current sending_num - - std::shared_ptr profiling_node; - bool is_profiling_enable = GlobalContext::profiling_manager()->IsProfilingEnable(tree_); - if (is_profiling_enable) { - std::shared_ptr node; - RETURN_IF_NOT_OK(GlobalContext::profiling_manager()->GetTracingNode(kDeviceQueueTracingName, &node)); - profiling_node = std::dynamic_pointer_cast(node); - batch_start_time = ProfilingTime::GetCurMilliSecond(); - connector_capacity = ChildOpConnectorCapacity(); - } -#ifdef ENABLE_DUMP_IR - RETURN_IF_NOT_OK(md_channel_info_->RecordBatchQueue(ChildOpConnectorSize())); - RETURN_IF_NOT_OK(md_channel_info_->RecordPreprocessBatch(0)); -#endif - batch_record_start = ProfilingTime::GetCurMilliSecond(); - TensorRow curr_row; - row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - curr_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - first_fetch_flag_ = true; - - MS_LOG(INFO) << "Begin to send data to device, channel name: " << channel_name_; - RETURN_UNEXPECTED_IF_NULL(ascend_data_queue_); - - while (!curr_row.eof() && !is_break_loop) { - SendInfo send_info_per_epoch; - send_info_per_epoch.epoch = op_current_epochs_ + 1; - send_summary_.push_back(send_info_per_epoch); - while (!curr_row.eoe() && !is_break_loop) { - RETURN_IF_NOT_OK(FilterMetadata(&curr_row)); - RETURN_IF_NOT_OK(CheckExceptions(curr_row)); - WaitContinueSignal(); -#ifdef ENABLE_DUMP_IR - RETURN_IF_NOT_OK(md_channel_info_->RecordBatchQueue(ChildOpConnectorSize())); - RETURN_IF_NOT_OK(md_channel_info_->RecordPreprocessBatch(send_batch)); - RETURN_IF_NOT_OK(md_channel_info_->RecordPushStartTime()); - if (send_batch == 0) { - // record the push start time for first batch - RETURN_IF_NOT_OK(md_channel_info_->RecordPushFirstStartTime()); - } -#endif - DetectPerBatchTime(&batch_record_start, &batch_record_end); - PrintBeginInfoWhenFirstBatch(first_push_flag_); - // when training stopped, handle might have been destroyed immediately - if (ascend_data_queue_ != nullptr && !ascend_data_queue_->IsOpen()) { - MS_LOG(WARNING) << "Thread has already been terminated."; - is_break_loop = true; - continue; - } - uint64_t start_time = GetSyscnt(); - if (!enable_prefetch_cache_pipeline_) { -#ifdef ENABLE_DUMP_IR - if (ascend_data_queue_->QueueType() == "Ascend_MBUF") { - RETURN_IF_NOT_OK(md_channel_info_->RecordDeviceQueue(ascend_data_queue_->QueryQueueSize())); - } - MS_LOG(INFO) << md_channel_info_->ToFormatString(); -#endif - RETURN_IF_NOT_OK(SendRowToTdt(&curr_row, is_profiling_enable, &tdt_cost)); - } else { - RETURN_IF_NOT_OK(PushDataToAscendCacheQueue(curr_row)); - } -#ifdef ENABLE_DUMP_IR - if (send_batch == 0) { - // record the push end time for first batch - RETURN_IF_NOT_OK(md_channel_info_->RecordPushFirstEndTime()); - } -#endif - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "PushToAscend", start_time, {{"TensorRowFlags", curr_row.FlagName()}})); - if (curr_row.Timer()->Enabled()) { - // VL_MD is 10900 - VLOG_MD(curr_row.Timer()->Summary()); - } - PrintEndInfoWhenFirstBatch(&first_push_flag_); - ProfilingRecorder(is_profiling_enable, profiling_node, send_batch, tdt_cost, &batch_start_time, &end_time, - connector_capacity, connector_size); - batch_record_start = ProfilingTime::GetCurMilliSecond(); - send_batch++; - MS_LOG(INFO) << "Have sent " << send_batch << " batch(es) to device, channel name: " << channel_name_; -#ifdef ENABLE_DUMP_IR - RETURN_IF_NOT_OK(md_channel_info_->RecordBatchQueue(ChildOpConnectorSize())); - RETURN_IF_NOT_OK(md_channel_info_->RecordPreprocessBatch(send_batch)); - RETURN_IF_NOT_OK(md_channel_info_->RecordPushEndTime()); -#endif - - if (total_batch_ > 0 && send_batch >= total_batch_) { - is_break_loop = true; - break; - } - - // wait when sending num is not 0, and sending num no larger than already sending batch - LimitSendingBatches(send_batch, &sending_num, cfg); - - RecordProfilingData(is_profiling_enable, false, &connector_size, &connector_capacity, &send_batch); - row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - curr_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - uint64_t batch_fetch_end = ProfilingTime::GetCurMilliSecond(); - if (ascend_data_queue_->QueueType() == "Ascend_MBUF") { - if (!enable_prefetch_cache_pipeline_) { - RETURN_IF_NOT_OK(WaitForAscendQueue(static_cast(curr_row.SizeInBytes()))); - } - } - uint64_t queue_wait_end = ProfilingTime::GetCurMilliSecond(); - // Skip the time looping in the mbuf queue control, FetchNextTensorRow time is what we need - batch_record_start = batch_record_start + (queue_wait_end - batch_fetch_end); - } - - uint64_t start_time = GetSyscnt(); - // send epoch end flag: ACL_TENSOR_DATA_END_OF_SEQUENCE to tdt - RETURN_IF_NOT_OK(SendEpochEndToAscend(curr_row, is_profiling_enable, &tdt_cost, &is_break_loop)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "PushToAscend", start_time, - {{"TensorRowFlags", TensorRow(TensorRow::kFlagEOE).FlagName()}})); - UpdateRepeatAndEpochCounter(); - RecordProfilingData(is_profiling_enable, true, &connector_size, &connector_capacity, &send_batch); - row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - curr_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - } - - if (enable_prefetch_cache_pipeline_) { - PushFileEndToQueue(channel_name_); - } - - // now we use this flag to judge whether exception raised. - if (stop_send_ || !TaskManager::FindMe()->Interrupted()) { - send_finished_ = true; - } - tree_->SetFinished(); - MS_LOG(INFO) << "ExecutionTree finished. Device queue sent number of batches: " << send_batch; - - return Status::OK(); -} - -void DataQueueOp::RecordProfilingData(bool is_profiling_enable, bool end_of_epoch, int32_t *connector_size, - int32_t *connector_capacity, const int64_t *send_batch) const { - if (is_profiling_enable) { - *connector_size = ChildOpConnectorSize(); - *connector_capacity = ChildOpConnectorCapacity(); - } - if (end_of_epoch) { - tree_->SetEpochEnd(); - GlobalContext::profiling_manager()->RecordEndOfEpoch(*send_batch); - } -} - -double DataQueueOp::CalMbufQueueMemory(size_t realtime_queue_size) { - while (memory_per_batch_.size() > realtime_queue_size) { - memory_per_batch_.pop_front(); - } - return std::accumulate(memory_per_batch_.begin(), memory_per_batch_.end(), 0.); -} - -Status DataQueueOp::SendEpochEndToAscend(const TensorRow &curr_row, const bool &is_profiling_enable, int32_t *tdt_cost, - bool *is_break_loop) { - RETURN_UNEXPECTED_IF_NULL(tdt_cost); - RETURN_UNEXPECTED_IF_NULL(is_break_loop); - RETURN_UNEXPECTED_IF_NULL(ascend_data_queue_); - if (curr_row.eoe() && send_epoch_end_ && ascend_data_queue_->IsOpen()) { - TensorRow dummy_row; - if (!enable_prefetch_cache_pipeline_) { - double start_time = 0; - if (is_profiling_enable) { - start_time = ProfilingTime::GetCurMilliSecond(); - } - auto status = ascend_data_queue_->Push({}); - if (is_profiling_enable) { - double end_time = ProfilingTime::GetCurMilliSecond(); - RETURN_UNEXPECTED_IF_NULL(tdt_cost); - *tdt_cost = static_cast(end_time - start_time); - } - - RETURN_IF_NOT_OK(CheckPushStatus(status, stop_send_, &send_finished_, is_break_loop)); - MS_LOG(INFO) << "an epoch has already sent, now stop send data."; - } else { - PushEpochEndToQueue(channel_name_); - } - stop_send_ = true; - } - return Status::OK(); -} - -void DataQueueOp::WaitContinueSignal() const { - while (stop_send_ && ascend_keep_waiting_) { - MS_LOG(DEBUG) << "stop_send flag is set, waiting for continue signal..."; - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } -} - -void DataQueueOp::LimitSendingBatches(int64_t send_batch, int64_t *sending_num, - const std::shared_ptr &cfg) const { - while (send_batch >= *sending_num) { - *sending_num = cfg->sending_batches(); - if (*sending_num == 0) { - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - MS_LOG(INFO) << "Wait for 10 milliseconds, as needed send batch is: " << *sending_num - << ", and current sending batch is:" << send_batch; - } -} - -Status DataQueueOp::SendRowToTdt(TensorRow *curr_row, bool is_profiling_enable, int32_t *tdt_cost) { - std::vector items = ConvertTensorRowToDataQueueItem(*curr_row); - double start_time = 0; - if (is_profiling_enable) { - start_time = ProfilingTime::GetCurMilliSecond(); - } - double row_timer_start = GetMilliTimeStamp(); - auto status = ascend_data_queue_->Push(items); - curr_row->TimerRecord(NameWithID(), RowTimer::kPushToDeviceTime, {GetMilliTimeStamp() - row_timer_start}); - if (is_profiling_enable) { - double end_time = ProfilingTime::GetCurMilliSecond(); - RETURN_UNEXPECTED_IF_NULL(tdt_cost); - *tdt_cost = static_cast(end_time - start_time); - } - if (status != device::DataQueueStatus::SUCCESS) { - if (stop_send_) { - MS_LOG(INFO) << "stop_send received"; - return Status::OK(); - } - RETURN_STATUS_ERROR( - StatusCode::kMDTDTPushFailure, - "TDT Push data into device Failed, check the first error or TraceBack first, more checking advises are: " - "1) if training is not ready, error might raised by network computing operator or environment configuration. " - "2) other cases, checking info level log or search this error in mindspore's FAQ for detail solution."); - } - if (create_data_info_queue_) { - DATA_INFO data_info; - (void)std::transform(curr_row->begin(), curr_row->end(), std::back_inserter(data_info), - [](const std::shared_ptr &ts) { return std::make_pair(ts->type(), ts->shape()); }); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Add(std::move(data_info))); - } - return Status::OK(); -} - -Status DataQueueOp::CheckPushStatus(DataQueueStatus status, bool stop_send, bool *send_finished, bool *is_break_loop) { - if (status != DataQueueStatus::SUCCESS) { - if (stop_send) { - *send_finished = true; - MS_LOG(INFO) << "stop_send received"; - return Status::OK(); - } - // when training stopped, handle might have been destroyed immediately - if (!ascend_data_queue_->IsOpen()) { - *is_break_loop = true; - MS_LOG(WARNING) << "Thread has already been terminated."; - return Status::OK(); - } - RETURN_STATUS_ERROR( - StatusCode::kMDTDTPushFailure, - "TDT Push data into device Failed, check the first error or TraceBack first, more checking advises are: " - "1) if training is not ready, error might raised by network computing operator or environment configuration. " - "2) other cases, checking info level log or search this error in mindspore's FAQ for detail solution."); - } - return Status::OK(); -} - -Status DataQueueOp::GetDataInfo(DATA_INFO *data_info) { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(MsContext::GetInstance()); - if (!create_data_info_queue_) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] DataInfo queue is not created."); - } - // This place has a race condition with operator(), so the first one - // arrive here will do the initialize work. - { - std::unique_lock lock(data_info_mutex_); - if (data_info_queue_ptr_ == nullptr) { - data_info_queue_ptr_ = std::make_unique(kDataInfoQueueCapacity); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Register(tree_->AllTasks())); - } - } - RETURN_IF_NOT_OK(data_info_queue_ptr_->PopFront(data_info)); -#endif - return Status::OK(); -} - -Status DataQueueOp::GetMbufQueueSize(size_t *queue_size) { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(queue_size); - if (TaskManager::FindMe()->Interrupted()) { - RETURN_STATUS_ERROR(StatusCode::kMDTDTPushFailure, - "DataQueueOp thread had been interrupted, please check for other error messages."); - } - if (device_type_ == DeviceType::Ascend && ascend_data_queue_->QueueType() == "Ascend_MBUF") { - *queue_size = ascend_data_queue_->QueryQueueSize(); - } else { - *queue_size = 1; - } -#endif - return Status::OK(); -} - -std::vector> DataQueueOp::GetSendInfo() { - std::vector> send_info_per_epoch; - (void)std::transform(send_summary_.begin(), send_summary_.end(), std::back_inserter(send_info_per_epoch), - [](const SendInfo &send_info) -> std::vector { - return std::vector{send_info.epoch, send_info.fetch_data_num, - send_info.first_data_time, send_info.fetch_data_time}; - }); - // history message control - constexpr auto kMaxInfoSize = 5; - while (send_info_per_epoch.size() > kMaxInfoSize) { - send_info_per_epoch.erase(send_info_per_epoch.begin()); - } - return send_info_per_epoch; -} - -Status DataQueueOp::SetThreadDevice() { -#ifdef WITH_BACKEND - (void)device::DataQueueMgr::GetInstance().SetThreadDevice(channel_name_); -#endif - return Status::OK(); -} - -Status DataQueueOp::CreateDynamicDataQueue() { -#ifdef WITH_BACKEND - if (dynamic_shape_) { - auto ret = device::DataQueueMgr::GetInstance().CreateDynamicBufQueue(channel_name_, kDynamicHostQueueCapacity); - if (ret != DataQueueStatus::SUCCESS && ret != DataQueueStatus::QUEUE_EXIST) { - RETURN_STATUS_ERROR(StatusCode::kMEFailed, "Create dynamic data queue failed"); - } - } -#endif - return Status::OK(); -} - -Status DataQueueOp::LaunchParallelCopyThread() { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(tree_); - // Every thread use cuda api should SetThreadDevice - RETURN_IF_NOT_OK(SetThreadDevice()); - // CircularPool may not safe under multi-threads scenario, so one worker with one pool - for (int i = 0; i < num_workers_; i++) { - std::shared_ptr pool; - RETURN_UNEXPECTED_IF_NULL(MsContext::GetInstance()); - RETURN_IF_NOT_OK(CircularPool::CreateCircularPool( - &pool, -1, kDeviceQueGpuThreadMemory, false, - MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET) == kGPUDevice)); - pool_.push_back(pool); - } - gpu_connector_ = std::make_unique(num_workers_, 1, queue_capacity_); - receive_queues_.Init(num_workers_, queue_capacity_); - RETURN_IF_NOT_OK(receive_queues_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(static_cast(num_workers_), - std::bind(&DataQueueOp::WorkerEntry, this, std::placeholders::_1), - Name() + "::WorkerEntry", id())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask("Push data to GPU queue", - std::bind(&DataQueueOp::PushDataToGPU, this), nullptr, id())); - if (enable_prefetch_cache_pipeline_) { - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "Push prefetch data to GPU queue", std::bind(&DataQueueOp::PushPrefetchDataToGPU, this), nullptr, id())); - } - -#endif - return Status::OK(); -} - -bool DataQueueOp::NoExceptionRaised() const { -#ifdef WITH_BACKEND - return !TaskManager::FindMe()->Interrupted() && !device::DataQueueMgr::GetInstance().IsClosed(); -#else - return !TaskManager::FindMe()->Interrupted(); -#endif -} - -Status DataQueueOp::PushDataToGPU() { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(tree_); - // Every thread use cuda api should SetThreadDevice - RETURN_IF_NOT_OK(SetThreadDevice()); - TaskManager::FindMe()->Post(); - uint64_t batch_start_time = 0; - uint64_t end_time = 0; - uint64_t push_cost = 0; - std::shared_ptr profiling_node; - bool is_profiling_enable = GlobalContext::profiling_manager()->IsProfilingEnable(tree_); - if (is_profiling_enable) { - std::shared_ptr node; - RETURN_IF_NOT_OK(GlobalContext::profiling_manager()->GetTracingNode(kDeviceQueueTracingName, &node)); - profiling_node = std::dynamic_pointer_cast(node); - batch_start_time = ProfilingTime::GetCurMilliSecond(); - } -#ifdef ENABLE_DUMP_IR - md_channel_info_->RecordBatchQueue(gpu_connector_->size()); - md_channel_info_->RecordPreprocessBatch(0); -#endif - GpuConnectorItem item; - RETURN_IF_NOT_OK(gpu_connector_->Pop(0, &item)); - auto items = std::move(item.data_item); - bool eoe_flag = item.eoe_flag; - int64_t send_batch = 0; - auto release_function = std::bind(&DataQueueOp::ReleaseData, this, std::placeholders::_1, std::placeholders::_2); - auto ret = device::DataQueueMgr::GetInstance().Open(channel_name_, release_function); - if (ret != DataQueueStatus::SUCCESS) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Failed to open channel for sending data."); - } - - while (!(items.empty() && !eoe_flag) && !device::DataQueueMgr::GetInstance().IsClosed()) { - if (!eoe_flag) { -#ifdef ENABLE_DUMP_IR - md_channel_info_->RecordBatchQueue(gpu_connector_->size()); - md_channel_info_->RecordPreprocessBatch(send_batch); - md_channel_info_->RecordPushStartTime(); -#endif - // Data prefetch only when PS mode enables cache. - uint64_t start_time = GetSyscnt(); - if (!enable_prefetch_cache_pipeline_) { - RETURN_IF_NOT_OK(RetryPushData(items, is_profiling_enable, &push_cost)); - } else { - PushDataToGPUCacheQueue(std::move(items)); - } - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "PushToGPU", start_time, {{"TensorRowFlags", "Data"}})); - ProfilingRecorder(is_profiling_enable, profiling_node, send_batch, push_cost, &batch_start_time, &end_time, - gpu_connector_->capacity(), gpu_connector_->size()); - send_batch++; - MS_LOG(INFO) << "Have sent " << send_batch << " batch(es) to device, channel name: " << channel_name_; -#ifdef ENABLE_DUMP_IR - md_channel_info_->RecordBatchQueue(gpu_connector_->size()); - md_channel_info_->RecordPreprocessBatch(send_batch); - md_channel_info_->RecordPushEndTime(); -#endif - if (total_batch_ > 0 && send_batch >= total_batch_) { - break; - } - } else { - if (is_profiling_enable) { - tree_->SetEpochEnd(); - GlobalContext::profiling_manager()->RecordEndOfEpoch(send_batch); - } - } - if (NoExceptionRaised()) { - auto rc = gpu_connector_->Pop(0, &item); - items = std::move(item.data_item); - eoe_flag = item.eoe_flag; - // If the batches send by dataset are more than gpu calculate, gpu will core for no signal notify. - if (rc.IsError()) { - device::DataQueueMgr::GetInstance().Close(channel_name_); - device::DataQueueMgr::GetInstance().CloseConfirm(); - return rc; - } - } else { - break; - } - } - - if (enable_prefetch_cache_pipeline_) { - // Send eof to cache queue - PushFileEndToQueue(channel_name_); - } - - // now we use this flag to judge whether exception raised. - if (NoExceptionRaised()) { - send_finished_ = true; - } - tree_->SetFinished(); - MS_LOG(INFO) << "ExecutionTree finished. Device queue sent number of batches: " << send_batch; - - device::DataQueueMgr::GetInstance().Close(channel_name_); - device::DataQueueMgr::GetInstance().CloseConfirm(); - return Status::OK(); -} - -// WorkEntry of DataQueueOp just do multi_threads memcpy for performance optimization. -Status DataQueueOp::WorkerEntry(int32_t worker_id) { - // Every thread use cuda api should SetThreadDevice - RETURN_IF_NOT_OK(SetThreadDevice()); - TaskManager::FindMe()->Post(); - TensorRow current_row; - uint32_t batch_num = 0; - RETURN_IF_NOT_OK(receive_queues_[worker_id]->PopFront(¤t_row)); - while (!current_row.quit() && !device::DataQueueMgr::GetInstance().IsClosed()) { - GpuConnectorItem connector_item = {{}, current_row.eoe()}; - if (!connector_item.eoe_flag) { - std::vector items; - for (auto &i : current_row) { - device::DataQueueItem data_item; - data_item.data_len = static_cast(i->SizeInBytes()); - data_item.shapes = i->shape().AsVector(); - data_item.data_ptr = nullptr; - data_item.worker_id = worker_id; - items.push_back(data_item); - } - - RETURN_IF_NOT_OK(MallocForGPUData(&items, current_row, worker_id)); - connector_item.data_item = std::move(items); - batch_num++; - } else { - MS_LOG(INFO) << "EOE Detected"; - } - RETURN_IF_NOT_OK(gpu_connector_->Add(worker_id, std::move(connector_item))); - RETURN_IF_NOT_OK(receive_queues_[worker_id]->PopFront(¤t_row)); - } - - MS_LOG(INFO) << "Device queue worker id " << worker_id << " processed number of batches: " << batch_num; - // Add empty data_item vector with eoe_flag=false as quit flag. - GpuConnectorItem connector_item = {{}, false}; - RETURN_IF_NOT_OK(gpu_connector_->Add(worker_id, std::move(connector_item))); -#endif - return Status::OK(); -} - -Status DataQueueOp::SendDataToGPU() { -#ifdef WITH_BACKEND - RETURN_IF_NOT_OK(LaunchParallelCopyThread()); - MS_LOG(INFO) << "Device queue, sending data to GPU."; - uint64_t batch_record_start; - uint64_t batch_record_end; - batch_record_start = ProfilingTime::GetCurMilliSecond(); - double row_timer_start = GetMilliTimeStamp(); - TensorRow current_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - current_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - first_fetch_flag_ = true; - int64_t num_buf = 0; - bool is_break_loop = false; - - MS_LOG(INFO) << "Begin to send data to device, channel name: " << channel_name_; - - while (!current_row.eof() && !is_break_loop && !device::DataQueueMgr::GetInstance().IsClosed()) { - SendInfo send_info_per_epoch; - send_info_per_epoch.epoch = op_current_epochs_ + 1; - send_summary_.push_back(send_info_per_epoch); - while (!current_row.eoe() && !is_break_loop && !device::DataQueueMgr::GetInstance().IsClosed()) { - RETURN_IF_NOT_OK(FilterMetadata(¤t_row)); - RETURN_IF_NOT_OK(CheckExceptions(current_row)); - DetectPerBatchTime(&batch_record_start, &batch_record_end); - - if (create_data_info_queue_) { - DATA_INFO data_info; - (void)std::transform(current_row.begin(), current_row.end(), std::back_inserter(data_info), - [](const std::shared_ptr &ts) { return std::make_pair(ts->type(), ts->shape()); }); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Add(data_info)); - } - - PrintBeginInfoWhenFirstBatch(first_push_flag_); - if (current_row.Timer()->Enabled()) { - // VL_MD is 10900 - VLOG_MD(current_row.Timer()->Summary()); - } - RETURN_IF_NOT_OK(receive_queues_[num_buf++ % num_workers_]->Add(std::move(current_row))); - PrintEndInfoWhenFirstBatch(&first_push_flag_); - batch_record_start = ProfilingTime::GetCurMilliSecond(); - if (NoExceptionRaised()) { - row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - current_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - } else { - is_break_loop = true; - } - } - UpdateRepeatAndEpochCounter(); - if (current_row.eoe()) { - MS_LOG(INFO) << "EOE Detected"; - TensorRow eoe_flag(TensorRow::kFlagEOE); - RETURN_IF_NOT_OK(receive_queues_[num_buf++ % num_workers_]->Add(std::move(eoe_flag))); - } - - if (NoExceptionRaised()) { - row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(¤t_row)); - current_row.TimerRecord(NameWithID(), RowTimer::kThroughputTime, {GetMilliTimeStamp() - row_timer_start}); - } else { - is_break_loop = true; - } - } - - for (uint32_t index = 0; index < num_workers_; index++) { - MS_LOG(INFO) << "Adding quit flag to Workers"; - TensorRow quit_flag(TensorRow::kFlagQuit); - RETURN_IF_NOT_OK(receive_queues_[num_buf++ % num_workers_]->Add(std::move(quit_flag))); - } - - MS_LOG(INFO) << "Device queue received number of batches and EOEs: " << (num_buf - num_workers_); -#else - MS_LOG(WARNING) << "Gpu queue is not supported in ut tests."; -#endif - return Status::OK(); -} - -Status DataQueueOp::MallocForGPUData(std::vector *items, const TensorRow &curr_row, - const int32_t &worker_id) { - size_t i = 0; - for (auto &sub_item : *items) { - auto rc = pool_[static_cast(worker_id)]->Allocate(sub_item.data_len, &sub_item.data_ptr); - if (rc.IsError() || sub_item.data_ptr == nullptr) { - RETURN_STATUS_OOM("Memory malloc failed, check memory usage."); - } - if (curr_row[i] == nullptr) { - MS_LOG(ERROR) << "[Internal ERROR] The pointer curr_row[" << i << "] is null"; - RETURN_STATUS_UNEXPECTED("[Internal ERROR] TensorRow 'curr_row' contains nullptr."); - } - sub_item.data_type = curr_row[i]->type().ToString(); - const unsigned char *column_data = curr_row[i]->GetBuffer(); - if (memcpy_s(sub_item.data_ptr, sub_item.data_len, column_data, - static_cast(curr_row[i++]->SizeInBytes())) != 0) { - MS_LOG(ERROR) << "[Internal ERROR] memcpy_s failed."; - RETURN_STATUS_UNEXPECTED("[Internal ERROR] memcpy_s failed."); - } - } - - return Status::OK(); -} - -Status DataQueueOp::ClearDevice() { -#ifdef WITH_BACKEND - MS_LOG(INFO) << "Clearing the data in GPU device: " << device_id_ << " channel: " << channel_name_; - auto release_function = std::bind(&DataQueueOp::ReleaseData, this, std::placeholders::_1, std::placeholders::_2); - auto ret = device::DataQueueMgr::GetInstance().Open(channel_name_, release_function); - if (ret != DataQueueStatus::SUCCESS) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Failed to open channel for clearing the device."); - } - - ret = device::DataQueueMgr::GetInstance().Clear(channel_name_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == DataQueueStatus::SUCCESS, "Failed to clear the device."); - device::DataQueueMgr::GetInstance().Close(channel_name_); - device::DataQueueMgr::GetInstance().CloseConfirm(); -#endif - return Status::OK(); -} - -Status DataQueueOp::SendDataToCPU() { - MS_LOG(INFO) << "Device queue, sending data to CPU."; - int64_t total_batch = 0; - - while (!(child_iterator_->EofHandled())) { - TensorRow curr_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - - if (!first_fetch_flag_) { - first_fetch_flag_ = true; - } - if (!curr_row.empty()) { - for (auto &tensor : curr_row) { - MS_LOG(DEBUG) << "Feature size is " << tensor->SizeInBytes() << "."; - } - total_batch++; - if (stop_send_) { - break; - } - } - } - - MS_LOG(INFO) << "Device queue total batch is " << total_batch << "."; - - return Status::OK(); -} - -void DataQueueOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nChannel name: " << channel_name_ << "\n\n"; - } -} - -void DataQueueOp::ProfilingRecorder(bool is_profiling_enable, const std::shared_ptr &profiling_node, - int64_t send_batch, int32_t tdt_cost, uint64_t *batch_start_time, - uint64_t *end_time, int32_t connector_capacity, int32_t connector_size) const { - // Record the pipeline profiling info - if (is_profiling_enable) { - *end_time = ProfilingTime::GetCurMilliSecond(); - // record push tdt time - profiling_node->Record(TIME, TDT_PUSH_TIME, send_batch + 1, tdt_cost, *end_time); - int32_t batch_cost = (int32_t)(*end_time - *batch_start_time); - // record batch time - profiling_node->Record(TIME, BATCH_TIME, send_batch + 1, batch_cost, *end_time); - // record pipeline time - profiling_node->Record(TIME, PIPELINE_TIME, send_batch + 1, batch_cost - tdt_cost, *end_time); - *batch_start_time = *end_time; - // record connector depth - profiling_node->Record(CONNECTOR_DEPTH, connector_capacity, send_batch + 1, connector_size, *end_time); - } -} - -Status DataQueueOp::DetectFirstBatch() { - TaskManager::FindMe()->Post(); - uint8_t count_num = 0; - uint64_t temp_start_time = ProfilingTime::GetCurMilliSecond(); - constexpr int check_interval = 200; - while (true) { - RETURN_IF_INTERRUPTED(); - std::this_thread::sleep_for(std::chrono::milliseconds(check_interval)); - uint64_t temp_end_time = ProfilingTime::GetCurMilliSecond(); - // if fetch first batch, or detect 3 or more times and unable fetch first batch, exist with already printed Warning - if (first_fetch_flag_ == true || count_num > 2) { - break; - } else if (temp_end_time - temp_start_time > kTimeOutMilliSeconds) { - count_num++; - MS_LOG(WARNING) << "Bad performance attention, it waits more than " + - std::to_string(kTimeOutMilliSeconds / 1000) + - " seconds and unable to fetch first Batch of " - "data from dataset pipeline, which might result `GetNext` timeout problem. You may test " - "dataset processing performance (with creating dataset iterator) and optimize it. Notes: " - "shuffle operation is turn on for loading Dataset in default, which may effect first batch " - "loading time."; - } - } - return Status::OK(); -} - -void DataQueueOp::DetectPerBatchTime(const uint64_t *start_time, uint64_t *end_time) { - *end_time = ProfilingTime::GetCurMilliSecond(); - auto interval = *end_time - *start_time; - constexpr auto kTimeMilliSeconds = 1000.; - send_summary_.back().record_data(interval / kTimeMilliSeconds); - if (interval > kTimeOutMilliSeconds) { - MS_LOG(WARNING) << "Bad performance attention, it takes more than " + std::to_string(kTimeOutMilliSeconds / 1000) + - " seconds to fetch a batch of data from dataset " - "pipeline, which might result `GetNext` timeout problem. You may test dataset processing" - " performance(with creating dataset iterator) and optimize it."; - } -} - -void DataQueueOp::PrintBeginInfoWhenFirstBatch(const bool &first_push_flag) const { - if (first_push_flag != true) { - VLOG_FLOW("Loading dataset and begin to push first batch into device ..."); - MS_LOG(INFO) << "Loading dataset and begin to push first batch into device ..."; - } -} - -void DataQueueOp::PrintEndInfoWhenFirstBatch(bool *first_push_flag) const { - if (!first_push_flag) { - MS_LOG(WARNING) << "First batch flag: first_push_flag is nullptr"; - return; - } - if (*first_push_flag != true) { - VLOG_FLOW("Loading dataset and push first batch into device successful."); - MS_LOG(INFO) << "Loading dataset and push first batch into device successful."; - *first_push_flag = true; - } -} - -Status DataQueueOp::RetryPushData(const std::vector &items, const bool profiling, uint64_t *push_time) { -#ifdef WITH_BACKEND - bool flag_log = false; - uint64_t start_time = 0; - if (profiling) { - start_time = ProfilingTime::GetCurMilliSecond(); - } - while (!device::DataQueueMgr::GetInstance().IsClosed() && !TaskManager::FindMe()->Interrupted()) { - DataQueueStatus ret = device::DataQueueMgr::GetInstance().Push(channel_name_, items, WAIT_TIME); - if (ret != DataQueueStatus::SUCCESS) { - if (ret == DataQueueStatus::ERROR_INPUT) { - device::DataQueueMgr::GetInstance().Close(channel_name_); - device::DataQueueMgr::GetInstance().CloseConfirm(); - return Status( - StatusCode::kMDUnexpectedError, __LINE__, __FILE__, - "Invalid data, the types or shapes of current row is different with previous row(i.e. do batch operation but " - "drop_reminder is False, or without resize image into the same size, these will cause shapes differs)."); - } else { - if (!stop_send_) { - if (!flag_log) { - MS_LOG(DEBUG) << "Retry pushing data..."; - flag_log = true; - } - continue; - } - break; - } - } else { - break; - } - } - if (profiling) { - *push_time = ProfilingTime::GetCurMilliSecond() - start_time; - } -#endif - return Status::OK(); -} - -Status DataQueueOp::SendDataToAscendDynamic() { -#ifdef WITH_BACKEND - MS_LOG(DEBUG) << "Dynamic Data queue, sending data to Ascend."; - int64_t send_batch = 0; - uint64_t data_queue_cost = 0; - bool is_break_loop = false; - - RETURN_IF_NOT_OK(CreateDynamicDataQueue()); - std::function release_function([](void *, int32_t) { return; }); - auto ret = device::DataQueueMgr::GetInstance().Open(channel_name_, release_function); - if (ret != DataQueueStatus::SUCCESS) { - return Status(StatusCode::kMDUnexpectedError, __LINE__, __FILE__, - "[Internal ERROR] Failed to open channel for sending data."); - } - - TensorRow curr_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - first_fetch_flag_ = true; - - while (!curr_row.eof() && !is_break_loop) { - while (!curr_row.eoe() && !is_break_loop) { - RETURN_IF_NOT_OK(FilterMetadata(&curr_row)); - RETURN_IF_NOT_OK(CheckExceptions(curr_row)); - std::vector items = ConvertTensorRowToDataQueueItem(curr_row); - RETURN_IF_NOT_OK(RetryPushData(items, false, &data_queue_cost)); - if (create_data_info_queue_) { - DATA_INFO data_info; - (void)std::transform(curr_row.begin(), curr_row.end(), std::back_inserter(data_info), - [](const std::shared_ptr &ts) { return std::make_pair(ts->type(), ts->shape()); }); - RETURN_IF_NOT_OK(data_info_queue_ptr_->Add(data_info)); - } - send_batch++; - if (total_batch_ > 0 && send_batch >= total_batch_) { - is_break_loop = true; - break; - } - - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - } - - if (curr_row.eoe() && send_epoch_end_) { - MS_LOG(INFO) << "an epoch has already sent, now stop send data."; - stop_send_ = true; - } - - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&curr_row)); - } - - // now we use this flag to judge whether exception raised. - if (stop_send_ || !TaskManager::FindMe()->Interrupted()) { - send_finished_ = true; - } - tree_->SetFinished(); - MS_LOG(INFO) << "ExecutionTree finished. Device queue sent number of batches: " << send_batch; - - device::DataQueueMgr::GetInstance().Close(channel_name_); - device::DataQueueMgr::GetInstance().CloseConfirm(); -#endif - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h deleted file mode 100644 index 21719c0df..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h +++ /dev/null @@ -1,230 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATA_QUEUE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATA_QUEUE_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" - -#include "mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#ifdef ENABLE_DUMP_IR -#include "mindspore-lite/minddata/dataset/util/rdr.h" -#endif -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/circular_pool.h" -#include "mindspore/ccsrc/include/runtime/hardware_abstract/data_queue/data_queue.h" - -namespace mindspore { -namespace dataset { -class GpuConnector; -using DATA_INFO = std::vector>; -using DATA_INFO_QUEUE = Queue; -using mindspore::device::DataQueueItem; -using mindspore::device::DataQueueStatus; -constexpr int32_t kTimeOutMilliSeconds = 60000; -const int kDataInfoQueueCapacity = 128; - -class DataQueueOp : public PipelineOp { - public: - static const uint32_t INVALID_HANDLE = 0xffffffffUL; - const uint32_t WAIT_TIME = 5; - - enum class DeviceType { Ascend = 0, GPU = 1, CPU = 2 }; - - // Name: constructor - // Description - DataQueueOp(const std::string channel_name, DeviceType device_type, int32_t device_id, bool send_epoch_end, - int32_t total_batch, bool create_data_info_queue); - - // Name: destructor - // Description - ~DataQueueOp(); - - /// \brief Getter function - /// \return connector size of current op - int32_t ConnectorSize() const { return ChildOpConnectorSize(); } - - Status EoeReceived(int32_t worker_id) override; - - void StopSend() { - MS_LOG(INFO) << "Received signal to stop sending data to device."; - stop_send_ = true; - send_finished_ = true; - } - - void ContinueSend() { - MS_LOG(INFO) << "continue send at the beginning of the epoch"; - stop_send_ = false; - } - - void StopWaiting() { ascend_keep_waiting_ = false; } - - Status ClearDevice(); - - Status GetDataInfo(DATA_INFO *data_info); - - Status GetMbufQueueSize(size_t *queue_size); - - std::vector> GetSendInfo(); - - // Name: Print() - // Description: A function that prints info about the node - void Print(std::ostream &out, // In: The output stream to print to - bool show_all) const override; // In: T/F if it should print everything - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const DataQueueOp &to) { - to.Print(out, false); - return out; - } - - Status operator()() override; - // Record the pipeline profiling info - void ProfilingRecorder(bool is_profiling_enable, const std::shared_ptr &profiling_node, - int64_t send_batch, int32_t tdt_cost, uint64_t *batch_start_time, uint64_t *end_time, - int32_t connector_capacity, int32_t connector_size) const; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kDeviceQueueOp; } - - private: - // Name: FilterMetadata(TensorRow *); - // Description: Auto filter metadata column before sending to device. - Status FilterMetadata(TensorRow *row) const; - - // Name: CheckExceptions(TensorRow); - // Description: Check whether the TensorRow meets the condition for performing DataQueueOp - Status CheckExceptions(const TensorRow &row) const; - - // Name: PrintBeginInfoWhenFirstBatch(bool) - // Description: Print info when first batch begin to send in sink_mode - void PrintBeginInfoWhenFirstBatch(const bool &first_push_flag) const; - - // Name: PrintEndInfoWhenFirstBatch(bool) - // Description: Print info when first batch send successful in sink_mode - void PrintEndInfoWhenFirstBatch(bool *first_push_flag) const; - Status RetryPushData(const std::vector &data, bool profiling, uint64_t *push_time); - bool NoExceptionRaised() const; - Status SendDataToAscendDynamic(); - - void WaitContinueSignal() const; - Status SendDataToAscend(); - Status SendEpochEndToAscend(const TensorRow &curr_row, const bool &is_profiling_enable, int32_t *tdt_cost, - bool *is_break_loop); - // Queue control logic for mbuf in host, to prevent from hang/exit abnormally - Status WaitForAscendQueue(size_t batch_data_len); - - // After multi-stage pipeline cache prefetch is enabled, data is not directly pushed to the device queue but to the - // cache queue first, and then pushed to the device queue after the cache analysis is complete. Push prefetch cache - // data to Ascend device queue. - Status PushPrefetchDataToAscend(); - // Push origin data to Ascend cache queue. - Status PushDataToAscendCacheQueue(const TensorRow &curr_row); - // Push prefetch cache data to GPU device queue. - Status PushPrefetchDataToGPU(); - // Push origin data to GPU cache queue. - Status PushDataToGPUCacheQueue(std::vector &&data_items); - - void LimitSendingBatches(int64_t send_batch, int64_t *sending_num, const std::shared_ptr &cfg) const; - Status SendRowToTdt(TensorRow *curr_row, bool is_profiling_enable, int32_t *tdt_cost); - // check status that push data into device - Status CheckPushStatus(DataQueueStatus status, bool stop_send, bool *send_finished, bool *is_break_loop); - bool ascend_keep_waiting_; - - Status SendDataToGPU(); - Status MallocForGPUData(std::vector *items, const TensorRow &curr_row, - const int32_t &worker_id); - void ReleaseData(void *addr, int32_t worker_id); - Status LaunchParallelCopyThread(); - Status PushDataToGPU(); - Status WorkerEntry(int32_t worker_id); - Status SetThreadDevice(); - Status CreateDynamicDataQueue(); - double CalMbufQueueMemory(size_t realtime_queue_size); - void RecordProfilingData(bool is_profiling_enable, bool end_of_epoch, int32_t *connector_size, - int32_t *connector_capacity, const int64_t *send_batch) const; - - QueueList receive_queues_; - std::vector> pool_; - std::unique_ptr gpu_connector_; - const uint32_t kDeviceQueGpuNumThreads = 2; - const uint32_t kDeviceQueGpuQueueCapacity = 8; - const int32_t kDeviceQueGpuThreadMemory = 1024; - const uint32_t kDynamicHostQueueCapacity = 2; - uint32_t num_workers_; - uint32_t queue_capacity_; - - Status SendDataToCPU(); - // Create async thread to detect whether it takes too long and unable to fetch first batch - Status DetectFirstBatch(); - - // Detect the cost time of each batch, present alarm message if cost too long - void DetectPerBatchTime(const uint64_t *start_time, uint64_t *end_time); - - // Send information in sink mode - struct SendInfo { - double epoch = 0; - double fetch_data_num = 0; - double fetch_data_time = 0; - double first_data_time = 0; - bool init = false; - void record_data(double t) { - if (!init) { - first_data_time = t; - init = true; - } - fetch_data_time += t; - fetch_data_num += 1; - } - }; - std::vector send_summary_; - - std::unique_ptr child_iterator_; - std::string channel_name_; - DeviceType device_type_; - const int32_t device_id_; - const bool send_epoch_end_; - bool stop_send_; - bool send_finished_; - int32_t total_batch_; - bool create_data_info_queue_; - std::unique_ptr data_info_queue_ptr_; - std::atomic first_fetch_flag_; - std::mutex data_info_mutex_; - bool first_push_flag_; // default: false, when first push, it will be true - bool dynamic_shape_{false}; - std::deque memory_per_batch_; - std::shared_ptr ascend_data_queue_; - -#ifdef ENABLE_DUMP_IR - std::shared_ptr md_channel_info_; -#endif - - // Whether to enable the cache prefetch multilevel pipeline, used in ps cache mode. - bool enable_prefetch_cache_pipeline_{false}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATA_QUEUE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.cc deleted file mode 100644 index 78f395b74..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.cc +++ /dev/null @@ -1,513 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -#include "mindspore-lite/minddata/dataset/engine/operator_connector.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/sig_handler.h" -#ifndef ENABLE_ANDROID -#include "utils/system/crc32c.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -DatasetOp::DatasetOp(int32_t op_connector_size, std::shared_ptr sampler) - : oc_queue_size_(op_connector_size), - sampler_(sampler), - operator_id_(kInvalidOperatorId), - tree_(nullptr), - state_(OpState::kDeOpIdle), - op_total_repeats_(kInfiniteRepeat), - op_num_repeats_per_epoch_(kInfiniteRepeat), - op_current_repeats_(0), - op_current_epochs_(0), - out_connector_(nullptr), - dataset_size_(-1), - num_classes_(-1) { - // The operator starts out with an invalid operator id. The only way to - // get it out of invalid state is to assign the operator to an execution tree. -} - -// Adds a operator to become our child. -Status DatasetOp::AddChild(std::shared_ptr child) { - if (std::dynamic_pointer_cast(child) != nullptr) { - std::string err_msg( - "Unsupported scenario, \'send\' operator can only be after \'device_queue\' operation, but got " + Name()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (operator_id_ == kInvalidOperatorId) { - std::string err_msg( - "[Internal ERROR] Cannot add child node. Tree node connections can only " - "be made if the node belongs to a tree."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // disallow relationships with other trees - if (tree_ != child->tree_) { - std::string err_msg( - "Invalid operator structure, the relationship of operators should be one by one, but got too many branches."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - child_.push_back(child); - child->AddParent(this); - return Status::OK(); -} - -Status DatasetOp::RemoveChild(std::shared_ptr child) { - if (operator_id_ == kInvalidOperatorId) { - std::string err_msg( - "[Internal ERROR] Cannot remove child node. Tree node connections can only " - "be made if the node belongs to a tree."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // disallow relationships with other trees - if (tree_ != child->tree_) { - std::string err_msg( - "Invalid operator structure, the relationship of operators should be one by one, but got too many branches."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - child_.erase(std::remove(child_.begin(), child_.end(), child), child_.end()); - child->RemoveParent(this); - return Status::OK(); -} - -Status DatasetOp::InsertAsParent(std::shared_ptr to_add) { - RETURN_UNEXPECTED_IF_NULL(to_add); - for (auto &prev_parent : this->parent_) { - RETURN_IF_NOT_OK(prev_parent->RemoveChild(shared_from_this())); - RETURN_IF_NOT_OK(prev_parent->AddChild(to_add)); - } - RETURN_IF_NOT_OK(to_add->AddChild(shared_from_this())); - if (tree_->root()->id() == this->id()) { - RETURN_IF_NOT_OK(tree_->AssignRoot(to_add)); - } - return Status::OK(); -} -// Removes child operator in this operator. -Status DatasetOp::RemoveChildren() { - for (const auto &child : child_) { - child->RemoveParent(this); - } - child_.clear(); - - return Status::OK(); -} - -// Adds a parent operator to this operator -void DatasetOp::AddParent(DatasetOp *parent) { parent_.push_back(parent); } - -// Removes a parent operator from this operator -void DatasetOp::RemoveParent(const DatasetOp *parent) { - parent_.erase(std::remove(parent_.begin(), parent_.end(), parent), parent_.end()); -} - -// Removes this node from the tree and connects it's parent/child together -Status DatasetOp::Remove() { - if (parent_.size() > 1) { - std::string err_msg( - "Invalid operator structure, the relationship between operators should be one-to-one, but encountered more than " - "one parent, namely: " + - std::to_string(parent_.size())); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (child_.size() > 1) { - std::string err_msg( - "Invalid operator structure, the relationship of operators should be one by one, but got too many branches."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Scenario's when removing node B: - // A -> B -> C - // A -> B - // B -> C - // - // If we remove B, then first take our child A and update it's parent to be C - // It's possible the parent is null if we are the root node being removed. - if (!child_.empty()) { - // If we have a parent, then assign child's parent to point to our parent. - if (!parent_.empty()) { - CHECK_FAIL_RETURN_UNEXPECTED(parent_[0]->Children().size() == 1, - "Invalid operator structure, the relationship of operators should be one by one, " - "but got too many branches."); - child_[0]->parent_[0] = parent_[0]; - } else { - // We don't have a parent, so we are the root node being removed. - // clear the parent list of our child so that it becomes the new root. - child_[0]->parent_.clear(); - RETURN_IF_NOT_OK(tree_->AssignRoot(child_[0])); - } - } - - // Next, if we had a parent, then set it's child to be our child. - if (!parent_.empty()) { - // if we have a child, then set our parent to point to it - if (!child_.empty()) { - parent_[0]->child_[0] = child_[0]; - } else { - // We don't have a child, so clear the child list of the current - // parent because it will be empty once we are removed. - parent_[0]->child_.clear(); - } - } - - // Finally, clear "this" op's parent and child pointers since we have just - // disconnected it from the tree and invalidate it's fields. - child_.clear(); - parent_.clear(); - operator_id_ = kInvalidOperatorId; - tree_ = nullptr; - - return Status::OK(); -} - -// Getter function to get a shared pointer to our child -std::shared_ptr DatasetOp::child(int32_t child_index) const { - std::shared_ptr return_op = nullptr; - if (child_.empty()) { - return return_op; - } - MS_ASSERT(child_index < static_cast(child_.size())); - // Return a shared pointer - return child_[child_index]; -} - -// Getter function to get the parent pointer -void DatasetOp::Parent(DatasetOp **parent, int32_t parent_index) const { - if (parent_.empty()) { - // common case if this is a root node - *parent = nullptr; - } else { - MS_ASSERT(parent_index < static_cast(parent_.size())); - *parent = parent_[parent_index]; - } -} - -// Getter function to get all of our parents. -std::vector DatasetOp::parents() const { return parent_; } - -// Creates the connector within this operator -void DatasetOp::CreateConnector() { - MS_LOG(DEBUG) << "Creating connector in tree operator: " << operator_id_ << "."; - if (oc_queue_size_ > 0) { - out_connector_ = std::make_unique(oc_queue_size_); - } else { - // Some op's may choose not to have an output connector - MS_LOG(DEBUG) << "Bypassed connector creation for tree operator: " << operator_id_ << "."; - out_connector_ = nullptr; - } -} - -// A print method typically used for debugging. showAll of true will recursively descend to child prints -void DatasetOp::Print(std::ostream &out, bool show_all) const { - // When show_all is false, we display a 1 liner piece of text for the op. - // When show_all is true, we display more detailed output for the op. - // Derived printers should show their own header info, then call base class printer, followed by - // derived-specific items. - - // Always show the id and name as first line regardless if this summary or detailed print - out << "(" << std::setw(2) << operator_id_ << ") <" << Name() << ">:"; - - if (show_all) { - // The detailed display will show common base class info of the op. Allow the derived class to print - // it's own id and name though as the first line. - out << "\nNumber of children : " << child_.size(); - for (size_t i = 0; i < child_.size(); i++) { - out << "\n Child[" << i << "] id: " << child_[i]->id(); - } - out << "\nNumber of parents : " << parent_.size(); - for (size_t i = 0; i < parent_.size(); i++) { - out << "\n Parent[" << i << "] id: " << parent_[i]->id(); - } - out << "\nConnector queue size : " << oc_queue_size_ << "\nTotal repeats : " << op_total_repeats_ - << "\nNumber repeats per epoch : " << op_num_repeats_per_epoch_; - if (sampler_) { - out << "\nSampler:\n"; - sampler_->SamplerPrint(out, show_all); - } - } -} - -Status DatasetOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (child_.empty()) { - MS_LOG(DEBUG) << "No child for operator [" << Name() << "]."; - return Status::OK(); - } - RETURN_UNEXPECTED_IF_NULL(child_[0]); - return child_[0]->GetNextRowPullMode(row); -} - -// Gets the next row from the given child -Status DatasetOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - // pop is a blocked call and will throw an interruption if the whole group shuts down. - RETURN_IF_NOT_OK(out_connector_->PopFront(row)); - return Status::OK(); -} - -// Gets the number of classes -Status DatasetOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (child_.size() == 1) { - return child_[0]->GetNumClasses(num_classes); - } else if (child_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetNumClasses shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in front of the child_ structure, so we get num classes from the last child. - return child_[child_.size() - 1]->GetNumClasses(num_classes); - } else { - // when num classes isn't found, the default behavior is to return -1 - MS_LOG(WARNING) << "Num classes not defined for : " << Name(); - *num_classes = -1; - return Status::OK(); - } -} - -Status DatasetOp::GetClassIndexing(std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - if (child_.size() == 1) { - return child_[0]->GetClassIndexing(output_class_indexing); - } else if (child_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetClassIndexing shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in the front of the child_ structure, so we get data from the last child. - return child_[child_.size() - 1]->GetClassIndexing(output_class_indexing); - } else { - *output_class_indexing = {}; - RETURN_STATUS_UNEXPECTED("Unsupported scenario, GetClassIndexing failed for " + Name() + - " doesn't support GetClassIndexing yet."); - } -} - -// Performs handling for when an eoe message is received. -// The base class implementation simply flows the eoe message to output. Derived classes -// may override if they need to perform special eoe handling. -Status DatasetOp::EoeReceived(int32_t worker_id) { return out_connector_->SendEOE(); } - -// Performs handling for when an eof message is received. -// The base class implementation simply flows the eof message to output. Derived classes -// may override if they need to perform special eof handling. -Status DatasetOp::EofReceived(int32_t worker_id) { return out_connector_->SendEOF(); } - -// During tree prepare phase, operators may have specific post-operations to perform depending on their role. -Status DatasetOp::PrepareOperator() { - // Creating Connector object for each op. - this->CreateConnector(); - if (out_connector_) { - RETURN_IF_NOT_OK(out_connector_->Register(tree_->AllTasks())); - } - RETURN_IF_NOT_OK(this->RegisterWorkerConnectors()); - - // Generate the column name map for the current op. - RETURN_IF_NOT_OK(this->ComputeColMap()); - - return Status::OK(); -} - -// During tree prepare phase, operators may have specific post-operations to perform depending on their role. -Status DatasetOp::PrepareOperatorPullBased() { - // Generate the column name map for the current op. - RETURN_IF_NOT_OK(this->ComputeColMap()); - - // check if operators are implemented in pull mode - std::string message = ""; - ImplementedPullMode isImplemented = PullModeImplementationStatus(); - if (isImplemented == ImplementedPullMode::NotImplemented) { - message = Name() + " is not implemented yet in pull mode."; - if (IsLeaf()) { - message = "Leaf node " + message; - if (GlobalContext::config_manager()->get_debug_mode()) { - RETURN_STATUS_UNEXPECTED(message); - } - } - } else if (isImplemented == ImplementedPullMode::DisabledDebugMode) { - message = "In debug mode, " + Name() + " is disabled for debugging purposes."; - } - if (message.size() > 0) { - MS_LOG(WARNING) << message; - } - return Status::OK(); -} - -// Derived classes may implement the reset function if the operator is stateful and needs -// specific reset handling that is not contained in this common code version of the reset. -Status DatasetOp::Reset() { - state_ = OpState::kDeOpRunning; - return Status::OK(); -} - -// gives a string output for the column map for handy debug printing -std::string DatasetOp::ColumnNameMapAsString() const { - std::string outStr = "Column name id map: "; - for (auto &it : column_name_id_map_) { - outStr += (" " + it.first + ":" + std::to_string(it.second)); - } - return outStr; -} - -// Computing the assignment of the column name map. -// This just inherits the column map from its first child, can only be used if the number of children is 1. -// Operations changing the column map must overwrite this function. -Status DatasetOp::ComputeColMap() { - if (child_.size() > 1) { - RETURN_STATUS_UNEXPECTED( - "Invalid operator structure, the relationship of operators should be one by one, but got too many branches."); - } - if (column_name_id_map_.empty()) { - column_name_id_map_ = child_[0]->column_name_id_map(); - if (column_name_id_map_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid column list, the column list of " + child_[0]->Name() + - " should have one column at least, but got empty."); - } - MS_LOG(DEBUG) << "Setting column map:\n" << DatasetOp::ColumnNameMapAsString(); - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Getter for the sampler, and it also removes the sampler from the op -Status DatasetOp::FetchRemoveSampler(std::shared_ptr *sampler) { - RETURN_UNEXPECTED_IF_NULL(sampler); - *sampler = sampler_; // It's okay if it sampler_ points to nullptr - sampler_.reset(); // clear our member-copy of this pointer. We no longer have this sampler - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -uint32_t DatasetOp::GenerateCRC(const std::shared_ptr &op) { - std::stringstream ss; - op->tree_->Print(ss, op); - std::string ss_str = ss.str(); - - // Filter out the Num workers field when generating the check sum - ss_str = std::regex_replace(ss_str, std::regex("Number of ShardReader workers.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Num workers.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("\\[workers.*?\\]"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Connector queue size.*\n"), ""); - - // Filter out tcp/ip information - ss_str = std::regex_replace(ss_str, std::regex("Hostname.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Port.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Number of rpc workers.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Prefetch size.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Local client support.*\n"), ""); - - // Filter out Number of rows when generating the check sum - ss_str = std::regex_replace(ss_str, std::regex("Number of rows.*\n"), ""); - - // Filter out the Operator control flags field when generating the check sum - ss_str = std::regex_replace(ss_str, std::regex("Operator control flags.*\n"), ""); - - // Filter out the Device id field to allow cache sharing for a distributed run of the same pipeline - ss_str = std::regex_replace(ss_str, std::regex("Device id.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("device_id.*\n"), ""); - - // Filter out the operator id field - ss_str = std::regex_replace(ss_str, std::regex(" *Parent.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex(" *Child.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex(R"(\(\s*\d+?\))"), ""); - - // Doesn't matter whether there is any parent node above CacheOp or not. - ss_str = std::regex_replace(ss_str, std::regex("Number of parents.*\n"), ""); - - // Filter out shuffle seed from ShuffleOp - ss_str = std::regex_replace(ss_str, std::regex("Shuffle seed.*\n"), ""); - - // Filter out the total repeats and number repeats per epoch field - ss_str = std::regex_replace(ss_str, std::regex("Total repeats.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Number repeats per epoch.*\n"), ""); - - // The Cache crc and Server cache id field is different when creating new cache_client and re-using the same - // cache_client later. So we filter out these two fields to allow cache sharing. - ss_str = std::regex_replace(ss_str, std::regex("Cache crc.*\n"), ""); - ss_str = std::regex_replace(ss_str, std::regex("Server cache id.*\n"), ""); - - MS_LOG(DEBUG) << "Printing the tree for generating crc:\n" << ss_str; - - uint32_t cache_crc = system::Crc32c::GetMaskCrc32cValue(ss_str.c_str(), ss_str.length()); - return cache_crc; -} -#endif - -void DatasetOp::UpdateRepeatAndEpochCounter() { - op_current_repeats_++; - if (op_current_repeats_ % op_num_repeats_per_epoch_ == 0) { - op_current_epochs_++; - } - MS_LOG(DEBUG) << Name() << " current repeats: " << op_current_repeats_ << ", current epochs: " << op_current_epochs_; -} - -Status DatasetOp::SetEpoch(const int64_t epoch) { - CHECK_FAIL_RETURN_UNEXPECTED(epoch >= 0, - "New epoch value must be greater than or equal to 0, got: " + std::to_string(epoch)); - while (op_current_epochs_ < epoch) { - UpdateRepeatAndEpochCounter(); - } - return Status::OK(); -} - -int64_t DatasetOp::GetTreeBatchSize() { - if (child_.size() == 1) { - return child_[0]->GetTreeBatchSize(); - } else if (child_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetBatchSize shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in front of the child_ structure, so we get data from the last child. - return child_[child_.size() - 1]->GetTreeBatchSize(); - } else { - return 1; - } -} - -int64_t DatasetOp::GetTreeRepeatCount() { - if (child_.size() == 1) { - return child_[0]->GetTreeRepeatCount(); - } else if (child_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetRepeatCount shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in front of the child_ structure, so we get data from the last child. - return child_[child_.size() - 1]->GetTreeRepeatCount(); - } else { - return 1; - } -} -std::vector DatasetOp::GetMPWorkerPIDs() const { return std::vector(); } - -Status DatasetOp::Launch() { -#ifndef ENABLE_ANDROID - RegisterMainHandlers(); -#endif - return Status::OK(); -} - -Status DatasetOp::Terminate() { return Status::OK(); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h deleted file mode 100644 index 8d69a0f1d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h +++ /dev/null @@ -1,434 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATASET_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATASET_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/callback_manager.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/engine/operator_connector.h" -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -constexpr char kBarrierOp[] = "BarrierOp"; -constexpr char kBatchOp[] = "BatchOp"; -constexpr char kBucketBatchByLengthOp[] = "BucketBatchByLengthOp"; -constexpr char kBuildSentencePieceVocabOp[] = "BuildSentencePieceVocabOp"; -constexpr char kBuildVocabOp[] = "BuildVocabOp"; -constexpr char kCacheBase[] = "CacheBase"; -constexpr char kCacheLookupOp[] = "CacheLookupOp"; -constexpr char kCacheMergeOp[] = "CacheMergeOp"; -constexpr char kCacheOp[] = "CacheOp"; -constexpr char kConcatOp[] = "ConcatOp"; -constexpr char kDatasetOp[] = "DatasetOp"; -constexpr char kDeviceQueueOp[] = "DataQueueOp"; -constexpr char kEpochCtrlOp[] = "EpochCtrlOp"; -constexpr char kFilterOp[] = "FilterOp"; -constexpr char kMapOp[] = "MapOp"; -constexpr char kParallelOp[] = "ParallelOp"; -constexpr char kPipelineOp[] = "PipelineOp"; -constexpr char kProjectOp[] = "ProjectOp"; -constexpr char kRenameOp[] = "RenameOp"; -constexpr char kRepeatOp[] = "RepeatOp"; -constexpr char kShuffleOp[] = "ShuffleOp"; -constexpr char kSkipOp[] = "SkipOp"; -constexpr char kTakeOp[] = "TakeOp"; -constexpr char kZipOp[] = "ZipOp"; -constexpr char kSendBridgeOp[] = "SendBridgeOp"; -constexpr char kReceiveBridgeOp[] = "ReceiveBridgeOp"; - -// Forward declare -class ExecutionTree; - -class NodePass; - -class SamplerRT; - -// \brief The base class DatasetOp is the main tree node. It is an abstract class, so -// the actual implementation of the operators will be derived from here. -class DatasetOp : public std::enable_shared_from_this { - // Allow execution tree to access internal members - friend class ExecutionTree; - - public: - static constexpr int32_t kInvalidOperatorId = -1; - static constexpr int32_t kInfiniteRepeat = -1; - - // Flags that control operator runtime behaviors - enum OpState { kDeOpRunning = 0, kDeOpIdle = 1, kDeOpTerminated }; - - // \brief Constructor - // \param op_connector_size - The size for the output connector of this operator. - // \param sampler - The sampler for the op - DatasetOp(int32_t op_connector_size, std::shared_ptr sampler); - - // \brief Destructor - virtual ~DatasetOp() { tree_ = nullptr; } - - // \brief Adds a operator to become our child. - // \param child - shared pointer to the child to add. - Status AddChild(std::shared_ptr child); - - // \brief Remove a operator from our children. - // \param child - shared pointer to the child to remove. - Status RemoveChild(std::shared_ptr child); - - // \brief Removes this node from the tree and connects it's parent/child together - // \return Status eerror code returned - Status Remove(); - - // Removes child operator in this operator. - Status RemoveChildren(); - - // \brief Getter function to get a shared pointer to our child - // \param[in] child_index An operator can have n children. Indicates which child to return. - // \return The shared pointer to the child. If there are no children, it returns null regardless of the given index - std::shared_ptr child(int32_t child_index) const; - - // \brief Getter function to get the pointer to our parent - // If there are no parents, it returns null regardless of the given index - // \param[in] parent_index An operator can have n parents. Indicates which parent to return. - void Parent(DatasetOp **parent, int32_t parent_index) const; - - // Getter function to get all of our parents. - std::vector parents() const; - - virtual Status AddNewWorkers(int32_t num_new_workers = 1) { - return Status(StatusCode::kMDUnexpectedError, "Add new workers is not supported for non-ParallelOps"); - } - - virtual Status RemoveWorkers(int32_t num_workers = 1) { - return Status(StatusCode::kMDUnexpectedError, "Remove workers is not supported for non-ParallelOps"); - } - - // \brief Inserts a operator as the parent current op. - // \notes Inserted op will become the sole parent of the current op. - // The existing parent of the current op will be transferred to the inserted op. - Status InsertAsParent(std::shared_ptr to_add); - - // \brief Creates the connector within this operator - void CreateConnector(); - - // \brief A print method typically used for debugging - // \param out - The output stream to write output to - // \param show_all - A bool to control if you want to show all info or just a summary - virtual void Print(std::ostream &out, bool show_all) const; - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - virtual Status GetNextRowPullMode(TensorRow *const row); - - /// \brief << Stream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param out - reference to the output stream being overloaded - /// \param dO - reference to the DatasetOp to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const DatasetOp &dO) { - dO.Print(out, false); - return out; - } - - // \brief Class functor operator (). - // \notes DatasetOps operate by launching a thread (see ExecutionTree). - // This pure virtual version makes the requirement that derived classes must provide a functor - // that will execute their main runtime loop code. - // \return Status The status code returned - virtual Status operator()() = 0; - - /// \brief Gets the next row from the given child - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - virtual Status GetNextRow(TensorRow *row); - - // \brief Gets the batch size - // \return Status - The status code return - virtual int64_t GetTreeBatchSize(); - - // \brief Gets the repeat count - // \return Status - The status code return - virtual int64_t GetTreeRepeatCount(); - - // \brief Gets the number of classes - // \return Status - The status code return - virtual Status GetNumClasses(int64_t *num_classes); - - // \brief Gets the class indexing - // \return Status - The status code return - virtual Status GetClassIndexing(std::vector>> *output_class_indexing); - - // \brief Performs handling for when an eoe message is received. - // The base class implementation simply flows the eoe message to output. Derived classes - // may override if they need to perform special eoe handling. - // \param worker_id - The worker id - // \return Status The status code returned - virtual Status EoeReceived(int32_t worker_id); - - // \brief Performs handling for when an eof message is received. - // The base class implementation simply flows the eof message to output. Derived classes - // may override if they need to perform special eof handling. - // \param worker_id - The worker id - // \return Status The status code returned - virtual Status EofReceived(int32_t worker_id); - - // \brief Derived classes may implement the reset function if the operator is stateful and needs - // specific reset handling that is not contained in this common code version of the reset - // \return Status The status code returned - virtual Status Reset(); - - // \brief During tree prepare phase, operators may have specific post-operations to perform depending on - // their role. - // \notes Derived versions of this function should always call their superclass version first - // before providing their own implementations. - virtual Status PrepareOperator(); - - // \brief During tree prepare phase, operators may have specific post-operations to perform depending on - // their role. - // \notes Derived versions of this function should always call its superclass version first - // before providing their own implementations. - virtual Status PrepareOperatorPullBased(); - - // \brief Getter function - // \return The operator id - int32_t id() const { return operator_id_; } - - // \brief Getter function - // \return The number of workers in this op - virtual int32_t NumWorkers() const = 0; - - // \brief Getter function - // \return T/F if this is an inlined operator - bool inlined() const { return (oc_queue_size_ == 0); } - - // \brief Set the epoch number for op manually. This is only used in reset mode. - // \param[in] epoch The new epoch number to restart the pipeline from - // \return - Status - Status SetEpoch(const int64_t epoch); - - // \brief Setter function, set the number of total repeats for the operator - void SetTotalRepeats(int32_t total_repeats) { op_total_repeats_ = total_repeats; } - - // \brief Setter function, set the number of repeats per epoch for the operator - void SetNumRepeatsPerEpoch(int32_t num_repeats_per_epoch) { op_num_repeats_per_epoch_ = num_repeats_per_epoch; } - - // \brief Getter function - // \return The number of required repeats for the operator - int32_t GetOpTotalRepeats() { return op_total_repeats_; } - - // \brief Getter function - // \return The number of repeats per epoch for the operator - int32_t GetOpNumRepeatsPerEpoch() const { return op_num_repeats_per_epoch_; } - - // \brief Register the internal worker connectors. No op unless it is a parallel op - // \return Status - virtual Status RegisterWorkerConnectors() { return Status::OK(); } - - // \brief Getter for the column name mapping - // \return The returned map - std::unordered_map column_name_id_map() const { return column_name_id_map_; } - - // \brief Checks if the column name map has been set up yet for this op - // \return - T/F if the operator has the map set up - bool HasColumnNameMap() const { return (column_name_id_map_.empty()); } - - // \brief gives a string output for the column map for handy debug printing - // \return - the column name map as a string - std::string ColumnNameMapAsString() const; - - OperatorConnector *OutputConnector() const { return out_connector_.get(); } - - // \brief Getter function - // \return connector size of current op - int32_t ConnectorSize() const { - if (!inlined()) { - return out_connector_->size(); - } - // Return child connector size for inlined op - return ChildOpConnectorSize(); - } - - /// \brief Counting number of rows sent out by a connector - int64_t ConnectorOutRowsCount() const { - return out_connector_ == nullptr ? int64_t(-1) : static_cast(out_connector_->out_rows_count()); - } - - // \brief Getter function - // \return connector size of current op - int32_t ConnectorCapacity() const { - if (!inlined()) { - return out_connector_->capacity(); - } - // Return child connector capacity for inlined op - return ChildOpConnectorCapacity(); - } - - // \brief Getter function - // \return connector size of child op - int32_t ChildOpConnectorSize(int32_t child_index = 0) const { return child_[child_index]->ConnectorSize(); } - - // \brief Getter function - // \return connector capacity of child op - int32_t ChildOpConnectorCapacity(int32_t child_index = 0) const { return child_[child_index]->ConnectorCapacity(); } - - // \brief Children Getter - // \return Vector of Children - std::vector> Children() const { return child_; } - - // Op name getter - // \return Name of the current Op - virtual std::string Name() const = 0; - - // Op name and ID getter - // \return Name and ID of the current Op - std::string NameWithID() const { return Name() + "(ID:" + std::to_string(id()) + ")"; } - - // Execution Tree getter - // \return Pointer to the ExecutionTree the current op belongs to, no ownership - ExecutionTree *Tree() { return tree_; } - - // Getter for the sampler - // \return Shared pointer to the sampler (may return nullptr) - std::shared_ptr sampler() { return sampler_; } - - // \brief Getter for the sampler, and it also removes the sampler from the op - // \param[out] sampler A pointer to the output sampler that was removed - // \return Status error code - Status FetchRemoveSampler(std::shared_ptr *sampler); - -#ifndef ENABLE_ANDROID - // Computes a CRC value for the operator - static uint32_t GenerateCRC(const std::shared_ptr &op); -#endif - - // \brief A helper templated function for casting "this" pointer to shared_ptr - // Similar to shared_from_this, except this one will give you the derived class as shared_ptr - // \return A shared_ptr casted to the derived class - template - std::shared_ptr shared_from_base() { - return std::static_pointer_cast(shared_from_this()); - } - - // \brief Setter for the sampler. Allows you to overwrite a previous sampler with a new one. - void SetSampler(std::shared_ptr sampler) { sampler_ = sampler; } - - // \brief Checks if this is a leaf node (0 children) - // \return boolean returns true if it's a leaf - bool IsLeaf() { return (child_.empty()); } - - // Checks if an operator has reached its last iteration - // \return boolean returns true if it's last iteration - bool IsLastIteration() { return op_total_repeats_ == op_current_repeats_ + 1; } - - // This function is only intended to be called by CallbackManager within the master thread of ParallelOp - // The expected behavior is this, when this function is invoked, this function will block until all the workers - // have finished their remaining work and go to sleep. Since all ParallelOps use a QueueList to sync with master. - // They would automatically wait on the QueueList when they are done. - // \return Status - virtual Status WaitForWorkers() { return Status::OK(); } - - virtual Status PostForWorkers() { return Status::OK(); } - - virtual int32_t NumWorkers() { return 0; } - - virtual Status SendQuitFlagToWorker(int32_t worker_id) { return Status::OK(); } - - virtual Status SendWaitFlagToWorker(int32_t worker_id) { return Status::OK(); } - - // \brief Add callback to DatasetOp, only MapOp supports Callback at the moment - void AddCallbacks(std::vector> callbacks) { callback_manager_.AddCallbacks(callbacks); } - - // \brief Remove all callbacks from DatasetOp - void ClearCallbacks() { callback_manager_.ClearCallbacks(); } - - virtual bool IsPython() const { return false; } - - virtual std::vector GetMPWorkerPIDs() const; - - // Terminate the Op which contains subprocess - virtual Status Terminate(); - - protected: - // \brief Removes a parent operator from this operator - // \notes External callers do not have access to this function - // \param[in] parent The parent node to remove - void RemoveParent(const DatasetOp *parent); - - // \brief Adds a parent operator to this operator - // \notes External callers do not have access to this function - // \param[in] parent The parent node to add - void AddParent(DatasetOp *parent); - - // Compute the current op's column map using its child's column map. - // Get called during the tree post-prepare phase in PrepareOperator. - // This base implementation just inherits the map from child 0, and can only be used if the number of children is 1. - // Operations changing the column map it inherits from the child must overwrite this function. - // \return - Status - virtual Status ComputeColMap(); - - // Increase op_current_repeats_ by 1 when one repeat finished. - // If this repeat happen to be the last repeat in the current epoch, also increase op_current_epochs_ by 1. - void UpdateRepeatAndEpochCounter(); - - // Launch the Op which contains subprocess - virtual Status Launch(); - - enum ImplementedPullMode { NotImplemented = 0, Implemented, DisabledDebugMode }; - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - virtual ImplementedPullMode PullModeImplementationStatus() const { return ImplementedPullMode::NotImplemented; } - - std::vector> child_; // Child nodes - std::vector parent_; // Parent nodes. No ownership - std::shared_ptr sampler_; // Some leaf ops might have a sampler - int32_t oc_queue_size_; // Capacity for each out_connector_ - int32_t operator_id_; // Generated id for the node - ExecutionTree *tree_; // Back pointer to our tree. - OpState state_; // The state of the operator, Running, Idle, Terminated - int32_t op_total_repeats_; // Required number of repeats for the operator - int32_t op_num_repeats_per_epoch_; // Total number of repeats per epoch for the operator - int32_t op_current_repeats_; // Current number of repeats the operator has handled - int32_t op_current_epochs_; // Current number of epochs the operator has handled - std::unique_ptr out_connector_; // Output Connector - std::unordered_map column_name_id_map_; // Mapping between col index and col name - std::mutex column_name_map_mutex_; // For protecting shared access to the column map - CallbackManager callback_manager_; // Manages callbacks associated with a DatasetOp - int64_t dataset_size_; // Size of the dataset - int64_t num_classes_; // Number of classes - - private: - // Sets the operator id. - // \notes No public interface. Only the class itself, or it's friend the execution tree can set - // this - // \param op_id - the Id value to set into the operator - void SetId(int32_t op_id) { operator_id_ = op_id; } - - // Sets the tree into the op so that the operator has a back pointer to the tree. - // \param tree - the tree to assign to the op. - void set_tree(ExecutionTree *tree) { tree_ = tree; } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_DATASET_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.cc deleted file mode 100644 index ea6c212aa..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.cc +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h" -#include -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// Constructor -EpochCtrlOp::EpochCtrlOp(int32_t num_epoch) : RepeatOp(num_epoch) { MS_LOG(INFO) << "Welcome to Epoch Ctrl Op."; } - -// Destructor -EpochCtrlOp::~EpochCtrlOp() {} - -// A print method typically used for debugging -void EpochCtrlOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [epochs: " << num_repeats_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - RepeatOp::Print(out, show_all); - } -} - -Status EpochCtrlOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - if (child_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] EpochCtrlOp can't be the leaf node(first operator) of pipeline."); - } - - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - - if (row->eob()) { - MS_LOG(WARNING) << "EpochCtrlOp got EOB, skip it."; - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - } - - // Only intercept EOE for EoeReceived processing, after that the EOE is forwarded to next op. - // Other TensorRows containing data or EOF will simply be forwarded. - // EOF can simply be forwarded because this op does not spawn any thread, thus does not require clean up. - if (row->eoe()) { - RETURN_IF_NOT_OK(EoeReceived(0)); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -Status EpochCtrlOp::EoeReceived(int32_t worker_id) { - UpdateRepeatAndEpochCounter(); - repeat_count_++; - if ((num_repeats_ != kInfiniteRepeat) && (repeat_count_ > num_repeats_)) { - std::string err_msg = "Epoch Control operator at EoeReceived: Epoch count: " + std::to_string(repeat_count_) + - " exceeds maximum epochs: " + std::to_string(num_repeats_); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - MS_LOG(DEBUG) << "Epoch Control operator received end of epoch. Epoch count is now: " << repeat_count_ - << ". Max epochs: " << num_repeats_; - - // This will allow GetNextInput in DatasetOp class to pass EOE row instead of eating it. - state_ = OpState::kDeOpIdle; - - if (repeat_count_ != num_repeats_) { - for (auto &eoe_op : eoe_ops_) { - MS_LOG(DEBUG) << "Epoch Control driving reset to op: " << eoe_op->id(); - RETURN_IF_NOT_OK(eoe_op->Reset()); - } - } - - return Status::OK(); -} - -int64_t EpochCtrlOp::GetTreeRepeatCount() { return child_[0]->GetTreeRepeatCount(); } - -Status EpochCtrlOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (child_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] EpochCtrlOp can't be the leaf node(first operator) of pipeline."); - } - - // Reset TensorRow (both vector and flags) - row->reset(); - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - - // Only intercept EOE for EoeReceived processing, after that the EOE is forwarded to next op. - // Other TensorRows containing data or EOF will simply be forwarded. - // EOF can simply be forwarded because this op does not spawn any thread, thus does not require clean up. - if (row->eoe()) { - RETURN_IF_NOT_OK(EoeReceived(0)); - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h deleted file mode 100644 index f57174d0b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_EPOCH_CTRL_OP_H_ -#define DATASET_ENGINE_DATASETOPS_EPOCH_CTRL_OP_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" - -namespace mindspore { -namespace dataset { -class EpochCtrlOp : public RepeatOp { - public: - // Constructor - explicit EpochCtrlOp(int32_t num_epoch); - - // Destructor - ~EpochCtrlOp(); - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - std::string Name() const override { return kEpochCtrlOp; } - - // This function returns the row that is at the top of our output connector. The caller is - // typically our parent node, when the parent is asking us to provide the next row of data. - // Since EpochCtrlOp is derived from RepeatOp which is an inlined op, getting a row from us - // will simply bounce you to get a row from our child. - // Epoch Control Op does not eat the EOE, it will pass the EOE to the next op. - Status GetNextRow(TensorRow *row) override; - - // Base-class override for handling cases when an eoe is received. - // @param worker_id - The worker id - Status EoeReceived(int32_t worker_id) override; - - int64_t GetTreeRepeatCount() override; - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - int32_t NumEpochs() const { return num_repeats_; } - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_DATASETOPS_EPOCH_CTRL_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.cc deleted file mode 100644 index 2fb0329cf..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.cc +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -FilterOp::FilterOp(const std::vector &in_col_names, int32_t num_workers, int32_t op_queue_size, - std::shared_ptr predicate_func) - : ParallelOp(num_workers, op_queue_size), predicate_func_(std::move(predicate_func)), in_columns_(in_col_names) { - worker_in_queues_.Init(num_workers, op_queue_size); -} - -Status FilterOp::operator()() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - // Synchronize with TaskManager. - TaskManager::FindMe()->Post(); - - child_iterator_ = std::make_unique(this, 0, 0); - TensorRow new_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - int64_t cnt = 0; - while (child_iterator_->EofHandled() == false) { - while (new_row.empty() == false) { - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(cnt % num_workers_)]->EmplaceBack(new_row)); - cnt++; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(cnt++ % num_workers_)]->EmplaceBack( - std::move(TensorRow(TensorRow::kFlagEOE)))); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(cnt++ % num_workers_)]->EmplaceBack( - std::move(TensorRow(TensorRow::kFlagEOF)))); - // EOF received, send quit signal to all workers - for (int32_t ind = 0; ind < num_workers_; ind++) { - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(cnt++ % num_workers_)]->EmplaceBack( - std::move(TensorRow(TensorRow::kFlagQuit)))); - } - - return Status::OK(); -} - -Status FilterOp::EofReceived(int32_t) { return Status::OK(); } - -Status FilterOp::EoeReceived(int32_t) { return Status::OK(); } - -// Validating if each of the input_columns exists in the column_name_id_map_. -Status FilterOp::ValidateInColumns(const std::vector &input_columns) const { - for (const auto &inCol : input_columns) { - bool found = column_name_id_map_.find(inCol) != column_name_id_map_.end() ? true : false; - if (!found) { - std::string err_msg = "Invalid parameter, column name: " + inCol + " does not exist in the dataset columns."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} - -// A print method typically used for debugging. -void FilterOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nInput column names:"; - for (size_t i = 0; i < in_columns_.size(); i++) { - out << " " << in_columns_[i]; - } - out << "\n\n"; - } -} - -Status FilterOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - TensorRow new_row; - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&new_row)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - start_time = GetSyscnt(); - - while (!new_row.quit()) { - // Getting a TensorRow to work on. - if (new_row.eoe()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(new_row)); - } else if (new_row.eof()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(new_row)); - } else { - RETURN_IF_NOT_OK(ValidateInColumns(in_columns_)); - - bool result = false; - RETURN_IF_NOT_OK(WorkerCompute(new_row, &result)); - - if (result) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(new_row)); - } else { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, - {{"TensorRowFlags", TensorRow(TensorRow::kFlagSkip).FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagSkip))); - } - } - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&new_row)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - start_time = GetSyscnt(); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", new_row.FlagName()}})); - return Status::OK(); -} - -Status FilterOp::WorkerCompute(const TensorRow &in_row, bool *out_predicate) { - TensorRow to_process; - if (in_columns_.empty() == true) { - MS_LOG(INFO) << "Input columns in filter operator is empty, will apply to the all column in the current table."; - to_process = in_row; - } else { - (void)std::transform( - in_columns_.begin(), in_columns_.end(), std::back_inserter(to_process), - [&in_row, this](const auto &it) -> std::shared_ptr { return in_row[column_name_id_map_[it]]; }); - } - RETURN_IF_NOT_OK(InvokePredicateFunc(to_process, out_predicate)); - return Status::OK(); -} - -Status FilterOp::CheckInput(const TensorRow &input) const { - for (auto &item : input) { - if (item == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] input tensor is null."); - } - } - return Status::OK(); -} - -Status FilterOp::InvokePredicateFunc(const TensorRow &input, bool *out_predicate) { - RETURN_UNEXPECTED_IF_NULL(out_predicate); - RETURN_IF_NOT_OK(CheckInput(input)); - - TensorRow output; - RETURN_IF_NOT_OK(predicate_func_->Compute(input, &output)); - RETURN_IF_NOT_OK(output.at(0)->GetItemAt(out_predicate, {})); - - return Status(StatusCode::kSuccess, "FilterOp predicate func call succeed"); -} - -Status FilterOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - row->clear(); - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - - if (row->eoe()) { - RETURN_IF_NOT_OK(EoeReceived(0)); - } else if (row->eof()) { - RETURN_IF_NOT_OK(EofReceived(0)); - } else { - RETURN_IF_NOT_OK(ValidateInColumns(in_columns_)); - bool result = false; - RETURN_IF_NOT_OK(WorkerCompute(*row, &result)); - if (result) { - return Status::OK(); - } else { - RETURN_IF_NOT_OK(GetNextRowPullMode(row)); - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h deleted file mode 100644 index 15f554e6f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_FILTER_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_FILTER_OP_H_ - -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" - -namespace mindspore { -namespace dataset { - -enum filterCtrl : int8_t { kFilterEmpty = 0, kFilterPartial = 1, kFilterFull = 2, kFilterEoe = 3, kFilterEof = 4 }; - -class FilterOp : public ParallelOp { - public: - // Constructor of FilterOp - // @note The builder class should be used to call it. - // @param in_col_names A list of input column names,when it is empty the predicate will be - // applied all columns in the dataset. - // @param num_workers The number of worker threads. - // @param op_connector_size The size of each queue in the connector. - // @param predicate_func python callable which returns a boolean value. - FilterOp(const std::vector &in_col_names, int32_t num_workers, int32_t op_queue_size, - std::shared_ptr predicate_func); - - // Destructor - ~FilterOp() = default; - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree),This class functor will - // provide the master loop that drives the logic for performing the work. - // @return Status The status code returned - Status operator()() override; - - // @param int32_t workerId. - // @return Status The status code returned. - Status EofReceived(int32_t) override; - - // @param int32_t workerId. - // @return Status The status code returned. - Status EoeReceived(int32_t) override; - - // A print method typically used for debugging. - // @param out The output stream to write output to. - // @param show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kFilterOp; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - // predicate_func python callable which returns a boolean value. - std::shared_ptr predicate_func_; - - // Variable to store the column name that will feed to predicate function. - std::vector in_columns_; - - std::unique_ptr child_iterator_; - - // Private function for worker/thread to loop continuously. It comprises the main - // logic of FilterOp, getting the data from previous Op, validating user specified column names, - // applying predicate to each of the data, filter the data when predicate result is false. - // @param worker_id The id assigned to this thread/worker upon creation. - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; // In: workerId assigned by tree_ - - // Filter the data by predicate function . - // @param in_row input row. - // @param out_predicate result boolean to filter or not. - // @return Status The status code returned - Status WorkerCompute(const TensorRow &in_row, bool *out_predicate); - - // @param input tensor vector. - // @return Status The status code returned. - Status CheckInput(const TensorRow &input) const; - - // Invoke python func. - // @param input tensor vector. - // @param the result of predicate. - // @return Status The status code returned. - Status InvokePredicateFunc(const TensorRow &input, bool *out_predicate); - - // Private function for validating if each of the user specified input column names - // exist in column_name_id_map_. - // @param input_columns The vector of input column names used in the current thread. - // @return Status The status code returned - Status ValidateInColumns(const std::vector &input_columns) const; -}; - -} // namespace dataset -} // namespace mindspore -#endif diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.cc b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.cc deleted file mode 100644 index 8715e0139..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.cc +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h" - -namespace mindspore { -namespace dataset { - -// Constructor -CpuMapJob::CpuMapJob() = default; - -// Constructor -CpuMapJob::CpuMapJob(std::vector> operations) : MapJob(std::move(operations)) {} - -// Destructor -CpuMapJob::~CpuMapJob() = default; - -// A function to execute a cpu map job -Status CpuMapJob::Run(std::vector in, std::vector *out) { - RETURN_UNEXPECTED_IF_NULL(out); - int32_t num_rows = in.size(); - for (int32_t row = 0; row < num_rows; row++) { - TensorRow input_row = in[row]; - TensorRow result_row; - for (size_t i = 0; i < ops_.size(); i++) { - // Call compute function for cpu - Status rc = ops_[i]->Compute(input_row, &result_row); - if (rc.IsError()) { - std::string op_name = ops_[i]->Name(); - RETURN_IF_NOT_OK(util::RebuildMapErrorMsg(input_row, op_name, &rc)); - } - - // Assign result_row to to_process for the next TensorOp processing, except for the last TensorOp in the list. - if (i + 1 < ops_.size()) { - input_row = std::move(result_row); - } - } - out->push_back(std::move(result_row)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h deleted file mode 100644 index 3814a9e97..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_MAP_OP_CPU_MAP_JOB_H_ -#define DATASET_ENGINE_DATASETOPS_MAP_OP_CPU_MAP_JOB_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h" - -namespace mindspore { -namespace dataset { -class CpuMapJob : public MapJob { - public: - // Constructor - CpuMapJob(); - - // Constructor - explicit CpuMapJob(std::vector> operations); - - // Destructor - ~CpuMapJob(); - - // A pure virtual run function to execute a cpu map job - Status Run(std::vector in, std::vector *out) override; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // A pure virtual run function to execute a npu map job for Ascend910B DVPP - Status Run(std::vector in, std::vector *out, mindspore::device::DeviceContext *device_context, - const size_t &stream_id) override { - RETURN_STATUS_UNEXPECTED("The run operation is not implemneted in CPU platform."); - } -#endif - - MapTargetDevice Type() override { return MapTargetDevice::kCpu; } -}; - -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_DATASETOPS_MAP_OP_CPU_MAP_JOB_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.cc b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.cc deleted file mode 100644 index 2bcd3e72c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.h" - -namespace mindspore { -namespace dataset { - -// Constructor -GpuMapJob::GpuMapJob(std::vector> operations) : MapJob(operations) {} - -// Destructor -GpuMapJob::~GpuMapJob() = default; -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.h b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.h deleted file mode 100644 index 93262fb82..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/gpu_map_job.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_MAP_OP_GPU_MAP_JOB_H_ -#define DATASET_ENGINE_DATASETOPS_MAP_OP_GPU_MAP_JOB_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h" - -namespace mindspore { -namespace dataset { -class GpuMapJob : public MapJob { - public: - // Constructor - explicit GpuMapJob(std::vector> operations); - - // Destructor - ~GpuMapJob(); - - // A pure virtual run function to execute a cpu map job - Status Run(std::vector in, std::vector *out) override { - RETURN_STATUS_UNEXPECTED("The run operation is not implemneted in GPU platform."); - } - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // A pure virtual run function to execute a npu map job for Ascend910B DVPP - Status Run(std::vector in, std::vector *out, mindspore::device::DeviceContext *device_context, - const size_t &stream_id) override { - RETURN_STATUS_UNEXPECTED("The run operation is not implemneted in GPU platform."); - } -#endif -}; - -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_DATASETOPS_MAP_OP_GPU_MAP_JOB_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h deleted file mode 100644 index 92d989624..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_MAP_OP_MAP_JOB_H_ -#define DATASET_ENGINE_DATASETOPS_MAP_OP_MAP_JOB_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -namespace util { -static inline Status RebuildMapErrorMsg(const TensorRow &input_row, const std::string &op_name, Status *rc) { - std::string err_msg = ""; - // Need to remove the suffix "Op" which length is 2 - std::string abbr_op_name = op_name.substr(0, op_name.length() - 2); - err_msg += "map operation: [" + abbr_op_name + "] failed. "; - if (input_row.getPath().size() > 0 && !input_row.getPath()[0].empty()) { - err_msg += "The corresponding data file is: " + input_row.getPath()[0]; - if (input_row.getPath().size() > 1) { - std::set path_set; - (void)path_set.insert(input_row.getPath()[0]); - for (size_t j = 1; j < input_row.getPath().size(); j++) { - if (!input_row.getPath()[j].empty() && path_set.find(input_row.getPath()[j]) == path_set.end()) { - err_msg += ", " + input_row.getPath()[j]; - (void)path_set.insert(input_row.getPath()[j]); - } - } - } - err_msg += ". "; - } - std::string tensor_err_msg = rc->GetErrDescription(); - if (rc->GetLineOfCode() < 0) { - err_msg += "Error description:\n"; - } - err_msg += tensor_err_msg; - if (abbr_op_name == "PyFunc") { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, err_msg); - } - (void)rc->SetErrDescription(err_msg); - return *rc; -} -} // namespace util -class MapJob { - public: - // Constructor - explicit MapJob(std::vector> operations) : ops_(operations) {} - - // Constructor - MapJob() = default; - - // Destructor - virtual ~MapJob() = default; - - Status AddOperation(std::shared_ptr operation) { - ops_.push_back(operation); - return Status::OK(); - } - - // A pure virtual run function to execute a particular map job - virtual Status Run(std::vector in, std::vector *out) = 0; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // A pure virtual run function to execute a particular map job for Ascend910B DVPP - virtual Status Run(std::vector in, std::vector *out, - mindspore::device::DeviceContext *device_context, const size_t &stream_id) = 0; -#endif - - virtual MapTargetDevice Type() = 0; - - protected: - std::vector> ops_; -}; - -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_DATASETOPS_MAP_OP_MAP_JOB_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.cc deleted file mode 100644 index 3cddbb895..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.cc +++ /dev/null @@ -1,923 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/callback_param.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/cpu_map_job.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/core/device_tensor_ascend910b.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#endif - -namespace mindspore { -namespace dataset { -using TensorOpVector = std::vector>; - -// Constructor of MapOp -MapOp::MapOp(const std::vector &in_col_names, const std::vector &out_col_names, - std::vector> tensor_operations, int32_t num_workers, - int32_t op_connector_size) - : ParallelOp(num_workers, op_connector_size), - tensor_operations_(tensor_operations), - tfuncs_(std::vector(num_workers, TensorOpVector())), - in_columns_(in_col_names), - out_columns_(out_col_names), - python_multiprocessing_runtime_(nullptr) { - // Set connector size via config. - // If caller didn't specify the out_col_names, assume they are same as the in_columns. - - // Build TensorOp from TensorOperation vector - // This is to ensure each iterator holds its own copy of the TensorOp objects. - auto base_seed = GetSeed(); - for (int32_t worker_index = 0; worker_index < num_workers; ++worker_index) { - (void)std::transform( - tensor_operations_.begin(), tensor_operations_.end(), std::back_inserter(tfuncs_[worker_index]), - [base_seed, worker_index](const std::shared_ptr &operation) -> std::shared_ptr { - auto op = operation->Build(); - op->SetSeed(base_seed + worker_index); - return op; - }); - } - - if (out_columns_.empty() || out_columns_[0].empty()) { - out_columns_ = in_columns_; - } -} - -// A print method typically used for debugging -void MapOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nInput column names:"; - for (size_t i = 0; i < in_columns_.size(); i++) { - out << " " << in_columns_[i]; - } - for (size_t i = 0; i < tfuncs_.size(); i++) { - out << "\n TensorOps with worker_id " << i << ":"; - for (size_t j = 0; j < tfuncs_[i].size(); j++) { - out << " " << *(tfuncs_[i][j].get()); - } - } - out << "\n\n"; - } -} - -// A helper function that fetch worker map job from local queues and extract the data and map job list -Status MapOp::FetchNextWork(int32_t worker_id, TensorRow *row, std::vector> *job_list) { - std::unique_ptr worker_job; - // Fetch the next worker job and TensorRow - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(worker_id)]->PopFront(&worker_job)); - // Extract the TensorRow and job list from the map worker job. - *row = std::move(worker_job->tensor_row); - *job_list = std::move(worker_job->jobs); - - return Status::OK(); -} - -Status MapOp::GenerateWorkerJob(const std::unique_ptr *worker_job, int32_t worker_id) { - // create the map_job by first op - std::shared_ptr map_job = nullptr; - MapTargetDevice prev_target = MapTargetDevice::kCpu; - CHECK_FAIL_RETURN_UNEXPECTED(tfuncs_[worker_id].size() > 0, "[Internal ERROR] Map's operations is null."); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - if (tfuncs_[worker_id][0]->IsDvppOp()) { - prev_target = MapTargetDevice::kAscend910B; - map_job = std::make_shared(); - } else { -#endif - map_job = std::make_shared(); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } -#endif - RETURN_IF_NOT_OK(map_job->AddOperation(tfuncs_[worker_id][0])); - - // continue create job from the second op - for (size_t j = 1; j < tfuncs_[worker_id].size(); j++) { - MapTargetDevice target_device = MapTargetDevice::kCpu; - if (tfuncs_[worker_id][j]->IsDvppOp()) { - target_device = MapTargetDevice::kAscend910B; - } - - if (target_device != prev_target) { - (*worker_job)->jobs.push_back(std::move(map_job)); - // create a new map_job for different target device operation -#if !defined(BUILD_LITE) && defined(ENABLE_D) - if (target_device == MapTargetDevice::kAscend910B) { - map_job = std::make_shared(); - } else { -#endif - map_job = std::make_shared(); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } -#endif - RETURN_IF_NOT_OK(map_job->AddOperation(tfuncs_[worker_id][j])); - } else { - RETURN_IF_NOT_OK(map_job->AddOperation(tfuncs_[worker_id][j])); - } - - prev_target = target_device; - } - - if (map_job != nullptr) { - (*worker_job)->jobs.push_back(std::move(map_job)); - } - - return Status::OK(); -} - -// This class functor will provide the master loop that drives the logic for performing the work -Status MapOp::operator()() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - // init callback - RETURN_IF_NOT_OK(callback_manager_.Init(this)); - - // Synchronize with TaskManager - TaskManager::FindMe()->Post(); - - int64_t ep_step = 0, total_step = 0; - - RETURN_IF_NOT_OK(callback_manager_.Begin(CallbackParam(0, ep_step, total_step))); - - child_iterator_ = std::make_unique(this, 0, 0); - TensorRow new_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - - while (!new_row.eof()) { - if (op_current_repeats_ % GetOpNumRepeatsPerEpoch() == 0) { - ep_step = 0; - RETURN_IF_NOT_OK(callback_manager_.EpochBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - } - while (!new_row.eoe()) { - ep_step++; - total_step++; - // Create an empty map worker job to be populated by a TensorRow and map jobs - - RETURN_IF_NOT_OK(callback_manager_.StepBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - - std::unique_ptr worker_job = std::make_unique(std::move(new_row)); - int32_t cur_worker_id = NextWorkerID(); - - // Populate map worker job for a worker to execute - RETURN_IF_NOT_OK(GenerateWorkerJob(&worker_job, cur_worker_id)); - - // Push map worker job to the corresponding worker's queue - RETURN_IF_NOT_OK(worker_in_queues_[cur_worker_id]->Add(std::move(worker_job))); - - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - - // Propagate the eoe row to worker - std::unique_ptr worker_job = std::make_unique(std::move(new_row)); - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(worker_job))); - UpdateRepeatAndEpochCounter(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - // End() is commented out because it might never be called due to the lack of EOF when EpochCtrl is -1 - // Handle eof logic, this code might never be reached if epoch_ctrl = -1. - std::unique_ptr worker_job = std::make_unique(std::move(new_row)); - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(worker_job))); - - // Quit all workers, this code might never be reached if EpochCtrl is -1. - for (int32_t wkr_id = 0; wkr_id < num_workers_; wkr_id++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - - return Status::OK(); -} - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -// init Ascend910B resource -Status MapOp::InitResource(const std::vector>> &tfuncs, - device::DeviceContext **device_context, size_t *stream_id) { - RETURN_UNEXPECTED_IF_NULL(device_context); - RETURN_UNEXPECTED_IF_NULL(stream_id); - bool dvpp_flag = false; - for (auto &op : tfuncs[0]) { - if (op->IsDvppOp()) { - dvpp_flag = true; - break; - } - } - - if (dvpp_flag) { - MS_LOG(INFO) << "Init resource for Ascend910B."; - { - std::unique_lock lock(device_context_mutex_); - auto ms_context = MsContext::GetInstance(); - if (ms_context == nullptr) { - RETURN_STATUS_UNEXPECTED("Get ms context failed by MsContext::GetInstance()"); - } - *device_context = device::DeviceContextManager::GetInstance().GetOrCreateDeviceContext( - {ms_context->get_param(MS_CTX_DEVICE_TARGET), ms_context->get_param(MS_CTX_DEVICE_ID)}); - if ((*device_context) == nullptr) { - RETURN_STATUS_UNEXPECTED("Get device context failed by ms context"); - } - (*device_context)->Initialize(); - if ((*device_context)->device_res_manager_ == nullptr) { - RETURN_STATUS_UNEXPECTED("The device resource manager is null."); - } - - std::string soc_version; - auto ret = AclAdapter::GetInstance().GetSocName(&soc_version); - if (ret != APP_ERR_OK) { - RETURN_STATUS_UNEXPECTED("Get Soc Version failed."); - } - if (soc_version.find("Ascend910B") == std::string::npos && - soc_version.find("Ascend910_93") == std::string::npos) { - std::string err_msg = "The SoC: " + soc_version + " is not Ascend910B / Ascend910_93"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - try { - if ((*device_context)->device_res_manager_->CreateStream(stream_id) != true) { - RETURN_STATUS_UNEXPECTED("Create new stream failed on Ascend910B platform."); - } - MS_LOG(INFO) << "Create new stream id: " << std::to_string(*stream_id); - } catch (const std::exception &e) { - std::string str_err = e.what(); - if (str_err.find("driver error:out of memory") != std::string::npos) { - RETURN_STATUS_UNEXPECTED( - "Cannot reset NPU device in forked subprocess.\n " - "Note: the following sevral scenarios are not supported yet.\n" - " 1. GeneratorDataset with num_parallel_workers>1 and " - "python_multiprocessing=True.\n 2. Independent dataset mode (export " - "MS_INDEPENDENT_DATASET=True):\n 1) Use the eager mode of dvpp " - "in the main process, and then start the dataset independent process. " - "GeneratorDataset / map / batch performs dvpp operations in thread mode.\n" - " 2) Initialize the device in the main process, and then start the " - "dataset independent process. GeneratorDataset / map / batch executes the " - "dvpp operation in thread mode.\n " - "Suggestion: except for the scenes above to use NPU with multiprocessing, " - "you can set ds.config.set_multiprocessing_start_method('spawn') in your " - "script and rerun."); - } - MS_LOG(EXCEPTION) << e.what(); - } - } - return Status::OK(); -} - -// Apply transforms on tensor -Status MapOp::ComputeIsDvpp(const std::shared_ptr tfunc, TensorRow *i_row, TensorRow *o_row, - device::DeviceContext *device_context, size_t stream_id) { - RETURN_UNEXPECTED_IF_NULL(i_row); - RETURN_UNEXPECTED_IF_NULL(o_row); - RETURN_UNEXPECTED_IF_NULL(device_context); - std::vector> device_in((*i_row).size()); - auto i = 0; - for (auto &tensor : *i_row) { - // if the first op is Decode, confirm that it is in jpeg format. - if (tfunc->Name() == kDvppDecodeOp) { - CHECK_FAIL_RETURN_UNEXPECTED(tensor->shape().Rank() == 1, - "[DvppDecode] Invalid data shape. Currently only support 1D. Its rank is: " + - std::to_string(tensor->shape().Rank())); - CHECK_FAIL_RETURN_UNEXPECTED( - IsNonEmptyJPEG(tensor) == true, - "[DvppDecode] Invalid image type. Currently only support JPG. Its shape is: " + tensor->shape().ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - tensor->type() == DataType::DE_UINT8, - "[DvppDecode] Invalid data type. Currently only support uint8. Its type is: " + tensor->type().ToString()); - } - - // create device tensor for input - if (tfunc->Name() == "DvppConvertColorOp") { - std::vector channels = {1, 3, 4}; - RETURN_IF_NOT_OK(DeviceTensorAscend910B::CreateDeviceTensor(tensor, device_context, stream_id, &device_in[i], - tfunc->IsHWC(), channels)); - } else { - RETURN_IF_NOT_OK( - DeviceTensorAscend910B::CreateDeviceTensor(tensor, device_context, stream_id, &device_in[i], tfunc->IsHWC())); - } - i++; - } - std::vector> device_out((*i_row).size()); - if (tfunc->Name() == kDvppDecodeOp) { - // if the op is Decode, we should get the height and width form JPEG header and create the output tensor first - int img_width = 0; - int img_height = 0; - for (int32_t k = 0; k < (*i_row).size(); k++) { - RETURN_IF_NOT_OK(GetJpegImageInfo((*i_row)[k], &img_width, &img_height)); - TensorShape shape{1, img_height, img_width, 3}; - DataType type(DataType::DE_UINT8); - std::shared_ptr device_tensor = nullptr; - RETURN_IF_NOT_OK( - DeviceTensorAscend910B::CreateDeviceTensor(shape, type, device_context, stream_id, &device_out[k])); - } - } - Status rc = tfunc->Compute(device_in, &device_out); - if (rc.IsError()) { - std::string op_name = tfunc->Name(); - RETURN_IF_NOT_OK(util::RebuildMapErrorMsg(*i_row, op_name, &rc)); - } - // Because we do ToHostTensor, we should sync first - if (!device_context->device_res_manager_->SyncStream(stream_id)) { - std::string err_msg = "SyncStream stream id: " + std::to_string(stream_id) + " failed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // copy the data from device to host - for (auto &tensor_row : device_out) { - std::shared_ptr host_out; - CHECK_FAIL_RETURN_UNEXPECTED(tensor_row->ToHostTensor(&host_out), "Copy tensor from device to host failed."); - (*o_row).push_back(std::move(host_out)); - } - - // release all the device memory - for (auto &item : device_in) { - if (!item->ReleaseDeviceMemory()) { - std::string err_msg = "Release the device memory failed after the dvpp ops executed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - for (auto &item : device_out) { - if (!item->ReleaseDeviceMemory()) { - std::string err_msg = "Release the device memory failed after the dvpp ops executed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} -#endif - -// Private function for worker/thread to loop continuously. It comprises the main -// logic of MapOp: getting the data from previous Op, validating user specified column names, -// applying a list of TensorOps to each of the data, process the results and then -// pushing them back to MapOp's output Connector to be fetched by the next Op. -Status MapOp::WorkerEntry(int32_t worker_id) { - // Handshake with TaskManager that thread creation is successful. - TaskManager::FindMe()->Post(); - // let Python layer know the worker id of this thread - if (python_multiprocessing_runtime_ != nullptr) { - python_multiprocessing_runtime_->set_thread_to_worker(worker_id); - } - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // init Ascend910B resource - device::DeviceContext *device_context = nullptr; - size_t stream_id = 0; - RETURN_IF_NOT_OK(InitResource(tfuncs_, &device_context, &stream_id)); -#endif - - TensorRow in_row; - std::vector> job_list; - - uint64_t start_time = GetSyscnt(); - double row_timer_start = 0; - // Fetch next data row and map job list - RETURN_IF_NOT_OK(FetchNextWork(worker_id, &in_row, &job_list)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - - // Now that init work is done, drop into the main fetching loop. - // Map op does not use child iterator, and it needs to manually handle eoe and eof's itself - // rather than use the base-class defaults. - while (true) { - // Handle special logic where row carries a ctrl flag. - if (in_row.Flags() != TensorRow::kFlagNone) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - if (in_row.quit()) { - break; - } - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(in_row))); - if (in_row.wait()) { - RETURN_IF_NOT_OK(TaskManager::FindMe()->Wait()); // wait for auto tune update workers successful - TaskManager::FindMe()->Clear(); - } - } else { - CHECK_FAIL_RETURN_UNEXPECTED(in_row.size() != 0, "[Internal ERROR] MapOp got an empty TensorRow."); - TensorRow out_row; - // Perform the compute function of TensorOp(s) and store the result in new_tensor_table. -#if !defined(BUILD_LITE) && defined(ENABLE_D) - RETURN_IF_NOT_OK(WorkerCompute(in_row, &out_row, job_list, device_context, stream_id)); -#else - RETURN_IF_NOT_OK(WorkerCompute(in_row, &out_row, job_list)); -#endif - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - out_row.TimerRecord(NameWithID(), RowTimer::kWorkerTime, {GetMilliTimeStamp() - row_timer_start}, &in_row); - // Push the row onto the connector for next operator to consume. - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(out_row))); - } - start_time = GetSyscnt(); - // Fetch next data row and map job list - RETURN_IF_NOT_OK(FetchNextWork(worker_id, &in_row, &job_list)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - } - - // map operation with PyFunc use global executor in Python Layer to run transform in eager mode - // release the executor in the current thread when the thread is done - RETURN_IF_NOT_OK(ReleaseResource(worker_id)); - - return Status::OK(); -} - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -Status MapOp::WorkerCompute(const TensorRow &in_row, TensorRow *out_row, - const std::vector> &job_list, device::DeviceContext *device_context, - size_t stream_id) { - size_t num_cols = in_row.size(); - - std::vector job_input_table; - std::vector original_table; - TensorRow to_process; - // Prepare the data that we need from in_row - // to_process : A vector of Tensors only holding cols in input_columns. - - // From the current row, select the Tensor that need to be passed to TensorOp - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(to_process), - [&in_row](const auto &it) { return in_row[it]; }); - to_process.setId(in_row.getId()); - std::vector cur_row_path = in_row.getPath(); - if (cur_row_path.size() > 0) { - std::vector to_process_path; - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(to_process_path), - [&cur_row_path](const auto &it) { return cur_row_path[it]; }); - to_process.setPath(to_process_path); - } - job_input_table.push_back(std::move(to_process)); - original_table.push_back(in_row); - - // Variable to keep the result after executing the job. - std::vector result_table; - // Executing the list of jobs. - for (size_t i = 0; i < job_list.size(); i++) { - RETURN_IF_INTERRUPTED(); - // Execute MapWorkerJob. - Status rc; - if (job_list[i]->Type() == MapTargetDevice::kCpu) { - rc = job_list[i]->Run(job_input_table, &result_table); - } else if (job_list[i]->Type() == MapTargetDevice::kAscend910B) { - rc = job_list[i]->Run(job_input_table, &result_table, device_context, stream_id); - } else { - RETURN_STATUS_UNEXPECTED("The map job type: " + std::to_string(static_cast(job_list[i]->Type())) + - " is not implemented."); - } - if (rc.IsError()) { - if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kReplace) { - MS_LOG(WARNING) - << "Detected an erroneous sample in MindData Map operation, and will replace with a healthy sample: " + - rc.GetErrDescription(); - *out_row = TensorRow(TensorRow::kFlagError); - return Status::OK(); - } else if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kSkip) { - MS_LOG(WARNING) << "Detected an erroneous sample in MindData Map operation, and will skip this sample: " + - rc.GetErrDescription(); - *out_row = TensorRow(TensorRow::kFlagError); - return Status::OK(); - } else { - // if thread had been interrupted, don't care the error - if (TaskManager::FindMe()->Interrupted()) { - MS_LOG(INFO) << "Current thread had been interrupted by TaskManager."; - return StatusCode::kMDInterrupted; - } else if (python_multiprocessing_runtime_ != nullptr && !python_multiprocessing_runtime_->is_running()) { - // when sink_mode=True, dataset_size / output_shapes / output_types / columna_names ops before training - // will cause map workers to stop first - MS_LOG(INFO) << "The multi workers of map operation had stopped."; - return StatusCode::kMDInterrupted; - } - return rc; - } - } - // Assign the processed data as an input for the next job processing, except for the last TensorOp in the list. - if (i + 1 < job_list.size()) { - job_input_table = std::move(result_table); - } - } - - // Sanity check a row in result_table - if (!result_table.empty() && out_columns_.size() != result_table[0].size()) { - RETURN_STATUS_UNEXPECTED( - "Invalid columns, the number of columns returned in 'map' operations should match " - "the number of 'output_columns', but got the number of columns returned in 'map' operations: " + - std::to_string(result_table[0].size()) + - ", the number of 'output_columns': " + std::to_string(out_columns_.size()) + "."); - } - - // Merging the data processed by job (result_table) with the data that are not used. - if (in_columns_.size() == out_columns_.size()) { - // Place the processed tensor back into the original index of the input tensor - for (size_t i = 0; i < result_table[0].size(); i++) { - original_table[0][to_process_indices_[i]] = std::move(result_table[0][i]); - } - *out_row = std::move(original_table[0]); - } else { - // Append the data in the original table that we did not use to the end of each row in result_table. - for (int32_t i = 0; i < num_cols; i++) { - if (keep_input_columns_[i]) { - result_table[0].push_back(std::move(original_table[0][i])); - } - } - *out_row = std::move(result_table[0]); - } - - return Status::OK(); -} -#else -Status MapOp::WorkerCompute(const TensorRow &in_row, TensorRow *out_row, - const std::vector> &job_list) { - size_t num_cols = in_row.size(); - - std::vector job_input_table; - std::vector original_table; - TensorRow to_process; - // Prepare the data that we need from in_row - // to_process : A vector of Tensors only holding cols in input_columns. - - // From the current row, select the Tensor that need to be passed to TensorOp - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(to_process), - [&in_row](const auto &it) { return in_row[it]; }); - to_process.setId(in_row.getId()); - std::vector cur_row_path = in_row.getPath(); - if (cur_row_path.size() > 0) { - std::vector to_process_path; - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(to_process_path), - [&cur_row_path](const auto &it) { return cur_row_path[it]; }); - to_process.setPath(to_process_path); - } - job_input_table.push_back(std::move(to_process)); - original_table.push_back(in_row); - - // Variable to keep the result after executing the job. - std::vector result_table; - // Executing the list of jobs. - for (size_t i = 0; i < job_list.size(); i++) { - RETURN_IF_INTERRUPTED(); - // Execute MapWorkerJob. - Status rc = job_list[i]->Run(job_input_table, &result_table); - if (rc.IsError()) { - if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kReplace) { - MS_LOG(WARNING) - << "Detected an erroneous sample in MindData Map operation, and will replace with a healthy sample: " + - rc.GetErrDescription(); - *out_row = TensorRow(TensorRow::kFlagError); - return Status::OK(); - } else if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kSkip) { - MS_LOG(WARNING) << "Detected an erroneous sample in MindData Map operation, and will skip this sample: " + - rc.GetErrDescription(); - *out_row = TensorRow(TensorRow::kFlagError); - return Status::OK(); - } else { - // if thread had been interrupted, don't care the error - if (TaskManager::FindMe()->Interrupted()) { - MS_LOG(INFO) << "Current thread had been interrupted by TaskManager."; - return StatusCode::kMDInterrupted; - } else if (python_multiprocessing_runtime_ != nullptr && !python_multiprocessing_runtime_->is_running()) { - // when sink_mode=True, dataset_size / output_shapes / output_types / columna_names ops before training - // will cause map workers to stop first - MS_LOG(INFO) << "The multi workers of map operation had stopped."; - return StatusCode::kMDInterrupted; - } - return rc; - } - } - // Assign the processed data as an input for the next job processing, except for the last TensorOp in the list. - if (i + 1 < job_list.size()) { - job_input_table = std::move(result_table); - } - } - - // Sanity check a row in result_table - if (!result_table.empty() && out_columns_.size() != result_table[0].size()) { - RETURN_STATUS_UNEXPECTED( - "Invalid columns, the number of columns returned in 'map' operations should match " - "the number of 'output_columns', but got the number of columns returned in 'map' operations: " + - std::to_string(result_table[0].size()) + - ", the number of 'output_columns': " + std::to_string(out_columns_.size()) + "."); - } - - // Merging the data processed by job (result_table) with the data that are not used. - if (in_columns_.size() == out_columns_.size()) { - // Place the processed tensor back into the original index of the input tensor - for (size_t i = 0; i < result_table[0].size(); i++) { - original_table[0][to_process_indices_[i]] = std::move(result_table[0][i]); - } - *out_row = std::move(original_table[0]); - } else { - // Append the data in the original table that we did not use to the end of each row in result_table. - for (int32_t i = 0; i < num_cols; i++) { - if (keep_input_columns_[i]) { - result_table[0].push_back(std::move(original_table[0][i])); - } - } - *out_row = std::move(result_table[0]); - } - - return Status::OK(); -} -#endif - -Status MapOp::ComputeColMap() { - // If the map has not been set up yet in the base class, then set it up - if (column_name_id_map_.empty()) { - std::unordered_map current_name_id_map = child_[0]->column_name_id_map(); - // Initialize private variables - RETURN_IF_NOT_OK(InitPrivateVariable(¤t_name_id_map)); - // Create the final column name to index mapping in the base class field - CreateFinalColMap(¤t_name_id_map); - MS_LOG(DEBUG) << "Column name map for map op is: " << this->ColumnNameMapAsString(); - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Validating if each of the input_columns exists in the col_name_id_map. -Status MapOp::ValidateInColumns(const std::unordered_map &col_name_id_map) { - for (const auto &inCol : in_columns_) { - bool found = col_name_id_map.find(inCol) != col_name_id_map.end(); - if (!found) { - std::string err_msg = "Invalid parameter, input column name: " + inCol + " doesn't exist in the dataset columns."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} - -Status MapOp::InitPrivateVariable(std::unordered_map *col_name_id_map) { - // If input_columns is empty(), The col at index-0 will be picked. - if (in_columns_.empty()) { - auto itr = - std::find_if(col_name_id_map->begin(), col_name_id_map->end(), [](const auto &it) { return it.second == 0; }); - CHECK_FAIL_RETURN_UNEXPECTED(itr != col_name_id_map->end(), - "[Internal ERROR] Column name id map doesn't have id 0"); - MS_LOG(INFO) << "Input columns empty for map op, will apply to the first column in the current table."; - in_columns_.push_back(itr->first); - - // If caller didn't specify the out_col_names, assume they are same as the input_columns. - // This was done in the constructor, but if input columns was empty to start we have to redo it here. - if (out_columns_.empty() || out_columns_[0].empty()) { - out_columns_ = in_columns_; - } - } - - // Before we continue, issue a sanity check to make sure the input columns from user and the incoming - // columns from child are correct - RETURN_IF_NOT_OK(this->ValidateInColumns(*col_name_id_map)); - - // Initialize keep_input_columns, true means to keep the column. - keep_input_columns_.resize(col_name_id_map->size(), true); - for (const auto &col_name : in_columns_) { - int32_t missed = (*col_name_id_map)[col_name]; - keep_input_columns_[missed] = false; - } - - // initialize to_process_indices. - for (const auto &col_name : in_columns_) { - to_process_indices_.push_back((*col_name_id_map)[col_name]); - } - return Status::OK(); -} - -// Create the final column name to index mapping and get indices of the columns this mapop does not use. -void MapOp::CreateFinalColMap(std::unordered_map *col_name_id_map) { - std::unordered_map final_col_name_id_map; - size_t num_cols = col_name_id_map->size(); - std::vector new_ids(num_cols); - if (in_columns_.size() == out_columns_.size()) { - // example - // in_columns: [a, b], out_columns: [c, d] - // in_columns: [a, b], out_columns: [b, a] - // in_columns: [a, b, c], out_columns: [b, c, a] - - // get the input columns index - std::vector input_columns_index = {}; - for (size_t i = 0; i < in_columns_.size(); i++) { - input_columns_index.push_back((*col_name_id_map)[in_columns_[i]]); - (void)col_name_id_map->erase(in_columns_[i]); - } - - // update the output column index - for (size_t i = 0; i < input_columns_index.size(); i++) { - (*col_name_id_map)[out_columns_[i]] = input_columns_index[i]; - } - - // Set the base class final column id map result - column_name_id_map_ = *col_name_id_map; - } else { - int32_t fill_idx = 0; - // First columns of the tables are occupied by the output columns from tensorOp. - for (const auto &col_name : out_columns_) { - final_col_name_id_map[col_name] = fill_idx++; - } - - // Creating new_ids mapping for the columns we keep. - for (size_t i = 0; i < num_cols; i++) { - if (keep_input_columns_[i]) { - new_ids[i] = fill_idx++; - } - } - - // Iterating through the old mapping to update the final mapping for the columns we kept. - std::string name; - for (const auto &pair : *col_name_id_map) { - name = pair.first; - int32_t old_id = pair.second; - if (keep_input_columns_[old_id]) { - final_col_name_id_map[name] = new_ids[old_id]; - } - } - - // Set the base class final column id map result - column_name_id_map_ = final_col_name_id_map; - } -} - -Status MapOp::SendWaitFlagToWorker(int32_t worker_id) { - TensorRow wait_row(TensorRow::kFlagWait); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::make_unique(wait_row))); - return Status::OK(); -} - -Status MapOp::SendQuitFlagToWorker(int32_t worker_id) { - TensorRow quit_flag(TensorRow::kFlagQuit); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::make_unique(quit_flag))); - return Status::OK(); -} - -Status MapOp::AddNewWorkers(int32_t num_new_workers) { - RETURN_IF_NOT_OK(ParallelOp::AddNewWorkers(num_new_workers)); - for (int32_t i = 0; i < num_new_workers; i++) { - tfuncs_.push_back(std::vector>()); - (void)std::transform( - tensor_operations_.begin(), tensor_operations_.end(), std::back_inserter(tfuncs_[tfuncs_.size() - 1]), - [](std::shared_ptr operation) -> std::shared_ptr { return operation->Build(); }); - } - if (python_multiprocessing_runtime_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(num_new_workers > 0, "Number of workers added should be greater than 0."); - python_multiprocessing_runtime_->add_new_workers(num_new_workers); - } - return Status::OK(); -} - -Status MapOp::RemoveWorkers(int32_t num_workers) { - RETURN_IF_NOT_OK(ParallelOp::RemoveWorkers(num_workers)); - for (int32_t i = 0; i < num_workers; i++) { - tfuncs_.pop_back(); - } - if (python_multiprocessing_runtime_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(num_workers > 0, "Number of workers removed should be greater than 0."); - python_multiprocessing_runtime_->remove_workers(num_workers); - } - return Status::OK(); -} -void MapOp::SetPythonMp(std::shared_ptr python_multiprocessing_runtime) { - python_multiprocessing_runtime_ = std::move(python_multiprocessing_runtime); -} - -Status MapOp::Launch() { - // launch python multiprocessing. This will create the MP pool and shared memory if needed. - if (python_multiprocessing_runtime_) { - MS_LOG(DEBUG) << "Launch Python Multiprocessing for MapOp:" << id(); - python_multiprocessing_runtime_->launch(id()); - } - return DatasetOp::Launch(); -} - -Status MapOp::Terminate() { - // Terminate python multiprocessing. This will stop the MP pool. - if (python_multiprocessing_runtime_) { - MS_LOG(INFO) << "Terminate Python Multiprocessing for MapOp:" << id(); - python_multiprocessing_runtime_->terminate(); - } - return Status::OK(); -} - -std::vector MapOp::GetMPWorkerPIDs() const { - if (python_multiprocessing_runtime_ != nullptr) { - return python_multiprocessing_runtime_->get_pids(); - } - return DatasetOp::GetMPWorkerPIDs(); -} - -Status MapOp::GetNextRowPullMode(TensorRow *const row) { -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // init Ascend910B resource - device::DeviceContext *device_context = nullptr; - size_t stream_id = 0; - RETURN_IF_NOT_OK(InitResource(tfuncs_, &device_context, &stream_id)); -#endif - - RETURN_UNEXPECTED_IF_NULL(row); - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eoe()) { - UpdateRepeatAndEpochCounter(); - } - if (new_row.empty()) { - (*row) = std::move(new_row); - return Status::OK(); - } - auto column_name_id_map = child_[0]->column_name_id_map(); - TensorRow i_row; - TensorRow o_row; - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(i_row), - [&new_row](const auto &it) { return new_row[it]; }); - i_row.setId(new_row.getId()); - std::vector cur_row_path = new_row.getPath(); - if (cur_row_path.size() > 0) { - std::vector to_process_path; - (void)std::transform(to_process_indices_.begin(), to_process_indices_.end(), std::back_inserter(to_process_path), - [&cur_row_path](const auto &it) { return cur_row_path[it]; }); - i_row.setPath(to_process_path); - } - // Apply transforms on tensor - for (auto &t : tfuncs_[0]) { -#if !defined(BUILD_LITE) && defined(ENABLE_D) - if (t->IsDvppOp()) { - RETURN_IF_NOT_OK(ComputeIsDvpp(t, &i_row, &o_row, device_context, stream_id)); - } else { -#endif - Status rc = t->Compute(i_row, &o_row); - if (rc.IsError()) { - std::string op_name = t->Name(); - RETURN_IF_NOT_OK(util::RebuildMapErrorMsg(i_row, op_name, &rc)); - } -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } -#endif - i_row = std::move(o_row); - } - - // Sanity check a row in result_table - if (!i_row.empty() && out_columns_.size() != i_row.size()) { - RETURN_STATUS_UNEXPECTED( - "Invalid columns, the number of columns returned in 'map' operations should match " - "the number of 'output_columns', but got the number of columns returned in 'map' operations: " + - std::to_string(i_row.size()) + ", the number of 'output_columns': " + std::to_string(out_columns_.size()) + "."); - } - - if (in_columns_.size() == out_columns_.size()) { - // assign transformed tensor back to the original - for (size_t i = 0; i < to_process_indices_.size(); i++) { - new_row[to_process_indices_[i]] = i_row.at(i); - } - (*row) = std::move(new_row); - } else { - // Append the data in the new row that we did not use to the end of i_row. - for (size_t i = 0; i < new_row.size(); i++) { - if (keep_input_columns_[i]) { - i_row.push_back(std::move(new_row[i])); - } - } - (*row) = std::move(i_row); - } - return Status::OK(); -} - -Status MapOp::ReleaseResource(int32_t worker_id) { - if (python_multiprocessing_runtime_ == nullptr) { - for (auto &op : tfuncs_[worker_id]) { - if (op->Name() == kPyFuncOp) { - RETURN_IF_NOT_OK(op->ReleaseResource()); - } - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h deleted file mode 100644 index 131d2ee6a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h +++ /dev/null @@ -1,256 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_MAP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_MAP_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declare -class ExecutionTree; - -// A unit of job for map worker thread. -// MapWorkerJob holds a list of MapJob where each MapJob can be a CpuMapJob, GpuMapJob or DvppMapJob. -struct MapWorkerJob { - explicit MapWorkerJob(TensorRow tr) : tensor_row(std::move(tr)) {} - std::vector> jobs; - TensorRow tensor_row; -}; - -// MapOp class implements the Map operator. It will apply a list of operations to each record specified by column names. -// The column order behavior after MapOp is as follows. -// [Case 1] If the number of Input Columns == the number of Output Column, column ordering after MapOp -// is the same as the original column order where the Remainder Columns stay in the same position, -// and the Output Columns are placed the same position of the Input Columns. -// For example, initially if the dataset has column order |A, B, C, D, E|, -// and we apply MapOp() with Input Columns {B, C} and Output Columns {X, Y}. -// The column order after applying MapOp will be |A, X, Y, D, E|. -// Note that in this case, |X, Y| is the Output Columns and |A, D, E| which is the Remainder Columns stay in -// their original position, and column B is replaced by column X and column C is replace by column Y. -// [Case 2] If the number of Input Columns != the number of Output Column, column ordering after MapOp -// is Output Columns followed by Remainder Columns. -// For example, initially if the dataset has column order |A, B, C, D, E|, -// and we apply MapOp() with Input Columns {B, C, A} and Output Columns {X, Y}. -// The column order after applying MapOp will be |X, Y, D, E|. -// Note that in this case, |X, Y| is the Output Columns and |D, E| is the Remainder Columns, -// and the Input Columns are gone and replaced by the Output Columns. - -// Keywords: -// Input Columns : a vector of column names (string) passed to MapOp specifying the column names from which -// Tensors are taken and passed to the TensorOp Compute(). -// Output Columns : a vector of column names (string) passed to MapOp specifying what are the column names -// for the Tensors produced by TensorOp Compute(). -// Remainder Columns : columns that exist in the dataset but are not mentioned in Input Columns. -// These columns will not be passed to TensorOp Compute(), but will be appended to the end of the Output Columns. -class MapOp : public ParallelOp, TensorRow> { - public: - // Constructor of MapOp - // @note The builder class should be used to call it. - // @param in_col_names A list of input column names (should match the input/output \p tensorFuncs). - // @param out_col_names A list of output column names (should match the input/output \p tensorFuncs). - // @param tensor_operations A list of TensorOperation pointers for MapOp to apply to each data. - // @param num_workers The number of worker threads. - // @param op_connector_size The size of each queue in the connector. - MapOp(const std::vector &in_col_names, const std::vector &out_col_names, - std::vector> tensor_operations, int32_t num_workers, - int32_t op_connector_size); - - // Destructor - ~MapOp() = default; - - // A print method typically used for debugging - // @param out The output stream to write output to - // @param show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out reference to the output stream being overloaded - // @param mo reference to the MapOp to display - // @return the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const MapOp &mo) { - mo.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // This main thread creates local queues, pulls TensorRow from the previous - // op's Connector and distributes them to the local queues. Workers pull from the local queues. - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kMapOp; } - - /// Send wait flag row to worker at worker_id to make it wait - /// \param worker_id id of the worker - /// \return Status code - Status SendWaitFlagToWorker(int32_t worker_id) override; - - /// Send quit flag row to worker at worker_id to make it exit - /// \param worker_id id of the worker - /// \return Status code - Status SendQuitFlagToWorker(int32_t worker_id) override; - - // List of tensor ops getter/setter - // @Return the vector of tensor ops by non-const reference - - auto &TFuncs() { return tfuncs_; } - - const auto &TFuncs() const { return tfuncs_; } - - bool IsPython() const override { - if (tfuncs_.size() > 0) { - for (const auto &tensorOp : tfuncs_[0]) { - if (tensorOp->Name() == kPyFuncOp) { - return true; - } - } - } - return false; - } - - /// Set the instance of python multiprocessing which will passed from python - /// \param python_multiprocessing_runtime PythonMultiprocessingRuntime - void SetPythonMp(std::shared_ptr python_multiprocessing_runtime); - - /// Return the list of PIDs of worker processes - /// \return vector of int - std::vector GetMPWorkerPIDs() const override; - - Status GetNextRowPullMode(TensorRow *const row) override; - - /// Used by independent dataset mode to stop the subprocess - /// \return Status The status code returned - Status Terminate() override; - - private: - // A helper function to create jobs for workers. - Status GenerateWorkerJob(const std::unique_ptr *worker_job, int32_t worker_id); - - // A helper function that fetch worker map job from local queues and extract the data and map job list - Status FetchNextWork(int32_t worker_id, TensorRow *row, std::vector> *job_list); - - // TensorOperations to be read - std::vector> tensor_operations_; - - // TensorOps to be applied by worker threads - std::vector>> tfuncs_; - - // Variable to store the column name that the tensorOps are consuming - std::vector in_columns_; - - // Variable to store the column name that the tensorOps are producing - std::vector out_columns_; - - // Boolean mapping, true means to keep the column. - std::vector keep_input_columns_; - - // Indices of the columns to process. - std::vector to_process_indices_; - - std::unique_ptr child_iterator_; // An iterator for fetching. - - std::shared_ptr python_multiprocessing_runtime_; // python multiprocessing instance - - // Private function for worker/thread to loop continuously. It comprises the main - // logic of MapOp: getting the data from previous Op, validating user specified column names, - // applying a list of TensorOps to each of the data, process the results and then - // pushing them back to MapOp's output Connector to be fetched by the next Op. - // @param worker_id The id assigned to this thread/worker upon creation. - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; // In: workerId assigned by tree_ - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Private function for worker thread to perform TensorOp's compute function and get the result. - // @param in_row Input TensorRow - // @param[out] out_row Generated TensorRow - Status WorkerCompute(const TensorRow &in_row, TensorRow *out_row, - const std::vector> &job_list, device::DeviceContext *device_context, - size_t stream_id); -#else - Status WorkerCompute(const TensorRow &in_row, TensorRow *out_row, - const std::vector> &job_list); -#endif - - // Private function that create the final column name to index mapping and - // get indices of the columns this mapop does not use. - // @param col_name_id_map The column name to index mapping obtained from child operator - void CreateFinalColMap(std::unordered_map *col_name_id_map); - - // Validating if each of the input_columns exists in col_name_id_map. - // @param - the column map to check - // @return - status return code - Status ValidateInColumns(const std::unordered_map &col_name_id_map); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - // Private function for initializing private variables such as in_columns_, out_columns_. - // @return - Status - Status InitPrivateVariable(std::unordered_map *col_name_id_map); - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Init Ascend910B resource. - // @return - Status - Status InitResource(const std::vector>> &tfuncs, - device::DeviceContext **device_context, size_t *stream_id); - - // Apply transforms on tensor - // @return - Status - Status ComputeIsDvpp(const std::shared_ptr tfunc, TensorRow *i_row, TensorRow *o_row, - device::DeviceContext *device_context, size_t stream_id); - - std::mutex device_context_mutex_; -#endif - - protected: - Status Launch() override; - Status AddNewWorkers(int32_t num_new_workers) override; - Status RemoveWorkers(int32_t num_workers) override; - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - Status RebuildMapErrorMsg(const TensorRow &input_row, const std::string &op_name, Status *rc); - - Status ReleaseResource(int32_t worker_id); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_MAP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.cc b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.cc deleted file mode 100644 index 0859fde82..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.cc +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/device_tensor_ascend910b.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { - -// Constructor -NpuMapJob::NpuMapJob() = default; - -// Constructor -NpuMapJob::NpuMapJob(std::vector> operations) : MapJob(std::move(operations)) {} - -// Destructor -NpuMapJob::~NpuMapJob() = default; - -// A function to execute a npu map job -Status NpuMapJob::Run(std::vector in, std::vector *out, - mindspore::device::DeviceContext *device_context, const size_t &stream_id) { - RETURN_UNEXPECTED_IF_NULL(out); - RETURN_UNEXPECTED_IF_NULL(device_context); - size_t num_rows = in.size(); - - // create the device tensor which copy the data from host to device - std::vector>> device_in(num_rows); - uint32_t i = 0; - for (auto &tensor_row : in) { - for (auto &tensor : tensor_row) { - // if the first op is Decode, confirm that it is in jpeg format. - if (ops_[0]->Name() == kDvppDecodeOp) { - CHECK_FAIL_RETURN_UNEXPECTED(tensor->shape().Rank() == 1, - "[DvppDecode] Invalid data shape. Currently only support 1D. Its rank is: " + - std::to_string(tensor->shape().Rank())); - CHECK_FAIL_RETURN_UNEXPECTED( - IsNonEmptyJPEG(tensor) == true, - "[DvppDecode] Invalid image type. Currently only support JPG. Its shape is: " + tensor->shape().ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - tensor->type() == DataType::DE_UINT8, - "[DvppDecode] Invalid data type. Currently only support uint8. Its type is: " + tensor->type().ToString()); - } - std::shared_ptr device_tensor = nullptr; - // here we use the first op's IsHWC() to create device tensor - if (ops_[0]->Name() == kDvppConvertColorOp) { - std::vector channels = {1, 3, 4}; - RETURN_IF_NOT_OK(DeviceTensorAscend910B::CreateDeviceTensor(tensor, device_context, stream_id, &device_tensor, - ops_[0]->IsHWC(), channels)); - } else { - RETURN_IF_NOT_OK(DeviceTensorAscend910B::CreateDeviceTensor(tensor, device_context, stream_id, &device_tensor, - ops_[0]->IsHWC())); - } - device_in[i].push_back(std::move(device_tensor)); - } - i += 1; - } - - std::vector>> device_out; - std::vector>>> hold_input_lists; - - for (int32_t row = 0; row < num_rows; row++) { - std::vector> input_row = device_in[row]; - std::vector> result_row; - - // hold all the inputs and release them when the npu_map_job finish - std::vector>> hold_input_list; - - for (size_t i = 0; i < ops_.size(); i++) { - // if the op is Decode, we should get the height and width form JPEG header and create the output tensor first - int img_width = 0; - int img_height = 0; - if (i == 0 && ops_[i]->Name() == kDvppDecodeOp) { - for (int32_t k = 0; k < in[0].size(); k++) { - RETURN_IF_NOT_OK(GetJpegImageInfo(in[0][k], &img_width, &img_height)); - TensorShape shape{1, img_height, img_width, 3}; - DataType type(DataType::DE_UINT8); - std::shared_ptr device_tensor = nullptr; - RETURN_IF_NOT_OK(DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input_row[k]->GetDeviceContext(), - input_row[k]->GetStreamID(), &device_tensor)); - result_row.push_back(std::move(device_tensor)); - } - } - // Call compute function for npu - Status rc = ops_[i]->Compute(input_row, &result_row); - if (rc.IsError()) { - std::string op_name = ops_[i]->Name(); - RETURN_IF_NOT_OK(util::RebuildMapErrorMsg(in[row], op_name, &rc)); - } - - // move the input to the hold_input_list first - hold_input_list.push_back(std::move(input_row)); - - // Assign result_row to to_process for the next TensorOp processing, except for the last TensorOp in the list. - if (i + 1 < ops_.size()) { - input_row = std::move(result_row); - } - } - device_out.push_back(std::move(result_row)); - hold_input_lists.push_back(std::move(hold_input_list)); - } - - // Because we do ToHostTensor, we should sync first - if (!device_context->device_res_manager_->SyncStream(stream_id)) { - std::string err_msg = "SyncStream stream id: " + std::to_string(stream_id) + " failed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // copy the data from device to host - for (auto &tensor_row : device_out) { - TensorRow result_row; - for (auto &tensor : tensor_row) { - std::shared_ptr host_out; - CHECK_FAIL_RETURN_UNEXPECTED(tensor->ToHostTensor(&host_out), "Copy tensor from device to host failed."); - result_row.push_back(std::move(host_out)); - } - out->push_back(std::move(result_row)); - } - - // release all the device memory - for (auto &hold_input_list : hold_input_lists) { - for (auto &tensor_list : hold_input_list) { - for (auto &item : tensor_list) { - if (!item->ReleaseDeviceMemory()) { - std::string err_msg = "Release the device memory failed after the dvpp ops executed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - } - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h b/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h deleted file mode 100644 index 5ecc5d020..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/map_op/npu_map_job.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_MAP_OP_NPU_MAP_JOB_H_ -#define DATASET_ENGINE_DATASETOPS_MAP_OP_NPU_MAP_JOB_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h" -#include "runtime/hardware_abstract/device_context/device_context.h" - -namespace mindspore { -namespace dataset { -class NpuMapJob : public MapJob { - public: - // Constructor - NpuMapJob(); - - // Constructor - explicit NpuMapJob(std::vector> operations); - - // Destructor - ~NpuMapJob(); - - // A pure virtual run function to execute a npu map job - Status Run(std::vector in, std::vector *out) override { - RETURN_STATUS_UNEXPECTED("The run operation is not implemneted in NPU platform."); - } - - // A pure virtual run function to execute a npu map job for Ascend910B DVPP - Status Run(std::vector in, std::vector *out, mindspore::device::DeviceContext *device_context, - const size_t &stream_id) override; - - MapTargetDevice Type() override { return MapTargetDevice::kAscend910B; } -}; - -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_DATASETOPS_MAP_OP_NPU_MAP_JOB_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h deleted file mode 100644 index 3b1dee09b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h +++ /dev/null @@ -1,468 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PARALLEL_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PARALLEL_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -constexpr int64_t kCachedRowsSize = 16; - -class ExecutionTree; - -// A ParallelOp provides a multi-threaded DatasetOp -template -class ParallelOp : public DatasetOp { - public: - /// Constructor - /// \param num_workers - /// \param op_connector_size - size of the output connector for this operator - /// \param sampler - The sampler for the op - ParallelOp(int32_t num_workers, int32_t op_connector_size, const std::shared_ptr sampler = nullptr) - : DatasetOp(op_connector_size, sampler), - num_workers_paused_(0), - epoch_sync_flag_(false), - num_workers_(num_workers), - next_worker_id_(0), - worker_connector_size_(op_connector_size), - strategy_{nullptr} { - // reduce excessive memory usage with high parallelism - constexpr int32_t worker_limit = 4; - if (num_workers_ > worker_limit) { - worker_connector_size_ = std::max(1, op_connector_size * worker_limit / num_workers_); - } - } - // Destructor - ~ParallelOp() override = default; - - /// A print method typically used for debugging - /// \param out - The output stream to write output to - /// \param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override { - DatasetOp::Print(out, show_all); - out << " [workers: " << num_workers_ << "]"; - } - - std::string Name() const override { return kParallelOp; } - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param pO - reference to the ParallelOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ParallelOp &po) { - po.Print(out, false); - return out; - } - - int32_t NumWorkers() const override { - int32_t num_workers = 1; - { - std::unique_lock _lock(mux_); - num_workers = num_workers_; - } - return num_workers; - } - - // pause all the worker thread and collector thread - Status WaitForWorkers() override { - // reset num_paused workers to 0 - num_workers_paused_ = 0; - uint32_t num_workers = NumWorkers(); - for (int32_t wkr_id = 0; wkr_id < num_workers; wkr_id++) { - RETURN_IF_NOT_OK(SendWaitFlagToWorker(NextWorkerID())); - } - // wait until all workers are done processing their work in local_queue_ - RETURN_IF_NOT_OK(wait_for_workers_post_.Wait()); - next_worker_id_ = 0; - // clear the WaitPost for the next Wait() - wait_for_workers_post_.Clear(); - return Status::OK(); - } - - // wakeup all the worker threads and collector thread - Status PostForWorkers() override { - // wakeup old workers - for (auto &item : worker_tasks_) { - item->Post(); - } - - // wakeup the collector thread - wait_for_collector_.Set(); - - return Status::OK(); - } - - /// Add a new worker to the parallelOp. The function will have to wait for all workers to process current rows. - /// Then it adds a new thread to the list. - /// \note The caller of this function has to be the main thread of the Op, since it's the only entity responsible to - /// push rows to workers_in_queue - /// \return Status The status code returned - Status AddNewWorkers(int32_t num_new_workers = 1) override { - // wait for workers to process the current rows - RETURN_IF_NOT_OK(WaitForWorkers()); - for (int32_t i = 0; i < num_new_workers; i++) { - RETURN_IF_NOT_OK(worker_in_queues_.AddQueue(tree_->AllTasks())); - RETURN_IF_NOT_OK(worker_out_queues_.AddQueue(tree_->AllTasks())); - } - - for (int32_t i = 0; i < num_new_workers; i++) { - Task *new_task; - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - Name() + "::WorkerEntry", std::bind(&ParallelOp::WorkerEntry, this, num_workers_), &new_task, id())); - CHECK_FAIL_RETURN_UNEXPECTED(new_task != nullptr, "Cannot create a new worker."); - worker_tasks_.push_back(new_task); - { - std::unique_lock _lock(mux_); - num_workers_++; - } - MS_LOG(INFO) << "A new worker has been added to op: " << Name() << "::" << id() - << " num_workers=" << num_workers_; - } - - // wakeup all the workers threads and collector thread - RETURN_IF_NOT_OK(PostForWorkers()); - - return Status::OK(); - } - - /// Add a new worker to the parallelOp. The function will have to wait for all workers to process current rows. - /// Then it adds a new thread to the list. - /// \note The caller of this function has to be the main thread of the Op, since it's the only entity responsible to - /// push rows to workers_in_queue - /// \return Status The status code returned - Status RemoveWorkers(int32_t num_workers = 1) override { - // wait for workers to process the current rows - RETURN_IF_NOT_OK(WaitForWorkers()); - for (size_t i = 0; i < num_workers; i++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(static_cast(num_workers_) - 1)); - worker_tasks_[num_workers_ - 1]->Post(); // wakeup the worker - RETURN_IF_NOT_OK(worker_tasks_[static_cast(num_workers_) - 1]->Join()); - RETURN_IF_NOT_OK(worker_in_queues_.RemoveLastQueue()); - worker_tasks_.pop_back(); - { - std::unique_lock _lock(mux_); - num_workers_--; - } - MS_LOG(INFO) << "Worker ID " << num_workers_ << " is requested to be removed in operator: " << NameWithID() - << " num_workers=" << num_workers_; - } - - // wakeup all the workers threads and collector thread - RETURN_IF_NOT_OK(PostForWorkers()); - - return Status::OK(); - } - - protected: - /// Interface for derived classes to implement. All derived classes must provide the entry - /// function with the main execution loop for worker threads. - /// \return Status The status code returned - virtual Status WorkerEntry(int32_t workerId) = 0; - - /// Called first when function is called - /// \return Status The status code returned - virtual Status RegisterAndLaunchThreads() { - RETURN_UNEXPECTED_IF_NULL(tree_); - worker_in_queues_.Init(num_workers_, worker_connector_size_); - worker_out_queues_.Init(num_workers_, worker_connector_size_); - - // Registers QueueList and individual Queues for interrupt services - RETURN_IF_NOT_OK(worker_in_queues_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(worker_out_queues_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(wait_for_workers_post_.Register(tree_->AllTasks())); - - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&ParallelOp::WorkerEntry, this, std::placeholders::_1), - &worker_tasks_, Name() + "::WorkerEntry", id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(1, std::bind(&ParallelOp::Collector, this), Name() + "::Collector", id())); - - return Status::OK(); - } - - class RowHandlingStrategy { - public: - explicit RowHandlingStrategy(ParallelOp *op) : op_(op) {} - virtual ~RowHandlingStrategy() = default; - - virtual Status HandleHealthyRow([[maybe_unused]] TensorRow *row) { - ++this->op_->ep_step_; - ++this->op_->total_step_; - RETURN_IF_NOT_OK(this->op_->callback_manager_.StepEnd(CallbackParam( - static_cast(this->op_->current_epochs_) + 1, this->op_->ep_step_, this->op_->total_step_))); - return this->op_->out_connector_->Add(std::move(*row)); - } - virtual Status HandleErrorRow([[maybe_unused]] TensorRow *row) = 0; - - virtual Status HandleEOE([[maybe_unused]] TensorRow *row) { - this->op_->current_repeats_++; - // check whether this is the end of a real epoch (not all eoe signals end of epoch) - if (this->op_->current_repeats_ % this->op_->GetOpNumRepeatsPerEpoch() == 0) { - this->op_->current_epochs_++; - RETURN_IF_NOT_OK(this->op_->callback_manager_.EpochEnd( - CallbackParam(this->op_->current_epochs_, this->op_->ep_step_, this->op_->total_step_))); - this->op_->ep_step_ = 0; - } - return op_->out_connector_->Add(std::move(*row)); - } - virtual Status HandleEOF([[maybe_unused]] TensorRow *row) { - RETURN_IF_NOT_OK(this->op_->callback_manager_.End(CallbackParam( - static_cast(this->op_->current_epochs_) + 1, this->op_->ep_step_, this->op_->total_step_))); - return op_->out_connector_->Add(std::move(*row)); - } - - protected: - ParallelOp *op_; - }; - - class ErrorStrategy : public RowHandlingStrategy { - public: - using RowHandlingStrategy::RowHandlingStrategy; - Status HandleErrorRow([[maybe_unused]] TensorRow *row) override { - return Status(StatusCode::kMDUnexpectedError, - "[Internal Error] Error row is detected in collector while Error strategy is set to error out!"); - } - }; - - class SkipStrategy : public RowHandlingStrategy { - public: - using RowHandlingStrategy::RowHandlingStrategy; - Status HandleErrorRow([[maybe_unused]] TensorRow *row) override { return Status::OK(); } - }; - - class ReplaceStrategy : public RowHandlingStrategy { - public: - using RowHandlingStrategy::RowHandlingStrategy; - - Status HandleHealthyRow([[maybe_unused]] TensorRow *row) override { - CHECK_FAIL_RETURN_UNEXPECTED(backup_index_ < kCachedRowsSize, - "[Internal Error] Number of cached rows is beyond the number set."); - if (backup_index_ < kCachedRowsSize - 1) { // cache has used row(s) or is not full - if (IsCacheFull()) { - // remove the last element from cache (a used row) - PopFromCache(); - } - RETURN_IF_NOT_OK(AddToCache(*row)); - } else { // cache is full of unused rows - if (missing_errors_ > 0) { - // send a cached row to next op and cache the current row - RETURN_IF_NOT_OK(AddFromCache()); - PopFromCache(); - missing_errors_--; - RETURN_IF_NOT_OK(AddToCache(*row)); - } - } - // send the healthy row to next op - ++this->op_->ep_step_; - ++this->op_->total_step_; - RETURN_IF_NOT_OK(this->op_->callback_manager_.StepEnd(CallbackParam( - static_cast(this->op_->current_epochs_) + 1, this->op_->ep_step_, this->op_->total_step_))); - return this->op_->out_connector_->Add(std::move(*row)); - } - - Status HandleErrorRow([[maybe_unused]] TensorRow *row) override { - CHECK_FAIL_RETURN_UNEXPECTED(backup_index_ < kCachedRowsSize, - "[Internal Error] Number of cached rows is beyond the number set."); - // cache is not full of unused rows - if (backup_index_ != kCachedRowsSize - 1) { - missing_errors_++; - return Status::OK(); - } - // cache is full of unused rows and we have an error row - return AddFromCache(); - } - - Status HandleEOE([[maybe_unused]] TensorRow *row) override { - CHECK_FAIL_RETURN_UNEXPECTED(missing_errors_ == 0 || !IsCacheEmpty(), - "All data is garbage and cannot be replaced."); - // send outstanding rows first and then send eoe - while (missing_errors_ > 0) { - RETURN_IF_NOT_OK(AddFromCache()); - missing_errors_--; - } - return RowHandlingStrategy::HandleEOE(row); - } - - Status HandleEOF([[maybe_unused]] TensorRow *row) override { - // release memory - std::deque().swap(backup_rows); - return RowHandlingStrategy::HandleEOF(row); - } - - private: - Status AddFromCache() { - CHECK_FAIL_RETURN_UNEXPECTED(backup_rows.size() > 0, "Cannot add a row from cache since cache is empty!"); - const TensorRow &cached_row = backup_rows[static_cast(backup_index_) % backup_rows.size()]; - TensorRow copy_row; - RETURN_IF_NOT_OK(cached_row.Clone(©_row)); - backup_index_--; - ++this->op_->ep_step_; - ++this->op_->total_step_; - RETURN_IF_NOT_OK(this->op_->callback_manager_.StepEnd(CallbackParam( - static_cast(this->op_->current_epochs_) + 1, this->op_->ep_step_, this->op_->total_step_))); - return this->op_->out_connector_->Add(std::move(copy_row)); - } - - Status AddToCache(const TensorRow &row) { - CHECK_FAIL_RETURN_UNEXPECTED(backup_rows.size() < kCachedRowsSize, - "[Internal Error] Inserting another row to cache while cache is already full."); - CHECK_FAIL_RETURN_UNEXPECTED( - backup_index_ < kCachedRowsSize - 1, - "[Internal Error] Inserting another row to cache while cache is already full of unused rows."); - TensorRow copy_row; - RETURN_IF_NOT_OK(row.Clone(©_row)); - (void)backup_rows.emplace_front(std::move(copy_row)); - backup_index_++; - return Status::OK(); - } - - void PopFromCache() { backup_rows.pop_back(); } - bool IsCacheFull() const { return backup_rows.size() == kCachedRowsSize; } - bool IsCacheEmpty() const { return backup_rows.size() == 0; } - std::deque backup_rows{}; // will hold a copy of some healthy rows collected (NOT error, skip, eoe, eof) - int32_t backup_index_{-1}; // index of the backup we should pick next time (can be negative if we run out of - // unused cached rows) - int32_t missing_errors_{0}; // the number of unaddressed error rows (that we need to send a replacement to output) - }; - - virtual Status Collector() { - TaskManager::FindMe()->Post(); - // num_rows received, including eoe, - int64_t num_rows = 0; - current_repeats_ = 0; - current_epochs_ = 0; - SetStrategy(); - // num_step of current epoch and the total - ep_step_ = 0, total_step_ = 0; - do { - TensorRow row; - RETURN_IF_NOT_OK(worker_out_queues_[static_cast(num_rows++ % NumWorkers())]->PopFront(&row)); - if (row.wait()) { - // When collector receives the signal from worker thread, it increments an atomic int - // If num_worker signals are received, wakes up the main thread - if (++num_workers_paused_ == num_workers_) { - wait_for_workers_post_.Set(); - RETURN_IF_NOT_OK(wait_for_collector_.Wait()); - wait_for_collector_.Clear(); - num_rows = 0; - } - continue; - } else if (row.eoe()) { - RETURN_IF_NOT_OK(strategy_->HandleEOE(&row)); - } else if (row.eof()) { - RETURN_IF_NOT_OK(strategy_->HandleEOF(&row)); - break; - } else if (row.skip()) { - continue; - } else if (row.error()) { - RETURN_IF_NOT_OK(strategy_->HandleErrorRow(&row)); - } else if (row.Flags() == TensorRow::TensorRowFlags::kFlagNone) { - RETURN_IF_NOT_OK(strategy_->HandleHealthyRow(&row)); - } - } while (true); - return Status::OK(); - } - - // Wait post used to perform the pausing logic - WaitPost wait_for_workers_post_; - - // Wait post used to perform the collector thread - WaitPost wait_for_collector_; - - // Count number of workers that have signaled master - std::atomic_int num_workers_paused_; - - /// Whether or not to sync worker threads at the end of each epoch - bool epoch_sync_flag_; - - /// The number of worker threads - int32_t num_workers_; - - std::vector worker_tasks_; - - int32_t NextWorkerID() { - { - std::unique_lock _lock(mux_); - int32_t next_worker = next_worker_id_; - next_worker_id_ = (next_worker_id_ + 1) % num_workers_; - return next_worker; - } - } - - public: - int32_t NumWorkers() override { - int32_t num_workers = 1; - { - std::unique_lock _lock(mux_); - num_workers = num_workers_; - } - return num_workers; - } - - private: - void SetStrategy() { - if (Name() != kMapOp) { - strategy_ = std::make_unique(this); - return; - } - if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kSkip) { - strategy_ = std::make_unique(this); - } else if (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kReplace) { - strategy_ = std::make_unique(this); - } else { - strategy_ = std::make_unique(this); - } - } - - protected: - std::atomic_int next_worker_id_; - - std::map quit_ack_; - - /// The size of input/output worker queeus - int32_t worker_connector_size_; - /// queues to hold the input rows to workers - QueueList worker_in_queues_; - /// queues to hold the output from workers - QueueList worker_out_queues_; - - // lock for num_workers_ read and write - mutable std::mutex mux_; - - private: - std::unique_ptr strategy_; - int32_t ep_step_{0}; - int32_t total_step_{0}; - int32_t current_epochs_{0}; - int32_t current_repeats_{0}; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PARALLEL_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.cc deleted file mode 100644 index 002bcb015..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.cc +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include - -namespace mindspore { -namespace dataset { -// Constructor -PipelineOp::PipelineOp(int32_t op_connector_size, std::shared_ptr sampler) - : DatasetOp(op_connector_size, sampler) {} - -// A print method typically used for debugging -void PipelineOp::Print(std::ostream &out, bool show_all) const { - // Summary 1-liner print - if (!show_all) { - // Call super class printer - DatasetOp::Print(out, show_all); - out << " [workers: "; - if (this->inlined()) { - out << "0 (inlined)]"; - } else { - out << "1]"; // Pipeline ops only have 1 worker - } - } else { - // Detailed print - DatasetOp::Print(out, show_all); - out << "\nNum workers: "; - if (this->inlined()) { - out << "0 (inlined)"; - } else { - out << "1"; // Pipeline ops only have 1 worker - } - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h deleted file mode 100644 index 391c1cb3b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PIPELINE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PIPELINE_OP_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -namespace mindspore { -namespace dataset { -// forward declare -class ExecutionTree; - -class PipelineOp : public DatasetOp { - public: - // Constructor - // @param op_connector_size - size of the output connector - // @return Builder setter method returns reference to the builder. - // @param sampler - The sampler for the op - explicit PipelineOp(int32_t op_connector_size, std::shared_ptr sampler = nullptr); - - // Destructor - ~PipelineOp() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - std::string Name() const override { return kPipelineOp; } - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param po - reference to the PipelineOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const PipelineOp &po) { - po.Print(out, false); - return out; - } - - // Getter - // @return The number of workers inside this op. Pipeline ops only have a single worker. - int32_t NumWorkers() const override { return 1; } - - protected: - // ******************************************************************************* - // I'm predicting there will be common arguments or functionality for pipeline ops, - // just not sure yet what those are. perhaps this intermediate class between - // DatasetOp and the actual ops is not needed at all? - // For example, if there's no common code for all of the non-parallel ops, then - // they can just inherit from DatasetOp directly and we can put this class into the - // trash. -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PIPELINE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/project_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/project_op.cc deleted file mode 100644 index 9345959cf..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/project_op.cc +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/project_op.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -ProjectOp::ProjectOp(const std::vector &columns_to_project) - : PipelineOp(0), columns_to_project_(columns_to_project) {} - -void ProjectOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nColumns that are projected:"; - for (size_t i = 0; i < columns_to_project_.size(); i++) { - out << "\n" << columns_to_project_[i]; - } - out << "\n\n"; - } -} - -// Gets a row from the child operator and projects the buffer. -Status ProjectOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - if (!row->eoe() && !row->eof()) { - *row = Project(*row); - } - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -TensorRow ProjectOp::Project(const TensorRow &row) { - TensorRow new_row; - (void)std::transform(projected_column_indices_.begin(), projected_column_indices_.end(), std::back_inserter(new_row), - [&row](uint32_t x) { return row[x]; }); - // Now if columns changed after map, we don't know which column we should keep, - // so temporarily we don't support print file_path after ProjectOp. - new_row.setPath({}); - row.CopyTimerTo(&new_row); - return new_row; -} - -// Class functor operator () override. -// Most dataset ops operate by launching a thread (see ExecutionTree). -// However, the ProjectOp is defined as a inlined operator, so it is invalid to launch the -// functor since this op runs inlined inside another operator. The function is overloaded to -// ensure that it is not called by mistake (it will generate an error). -Status ProjectOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] ProjectOp is an inlined operator."); } - -Status ProjectOp::EoeReceived(int32_t worker_id) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -Status ProjectOp::EofReceived(int32_t worker_id) { return Status::OK(); } - -// Compute the column map and save it into our own column name map -// We cannot use the super class ComputeColMap here because we're making a modification of the -// map from the child map. -Status ProjectOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - std::unordered_map child_column_name_mapping = child_[0]->column_name_id_map(); - for (size_t i = 0; i < columns_to_project_.size(); i++) { - std::string ¤t_column = columns_to_project_[i]; - if (child_column_name_mapping.find(current_column) == child_column_name_mapping.end()) { - std::string err_msg = "Invalid column, column name: " + current_column + " does not exist."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - // Setup the new column name mapping for ourself (base class field) - column_name_id_map_[current_column] = i; - projected_column_indices_.push_back(child_column_name_mapping[current_column]); - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status ProjectOp::GetNextRowPullMode(TensorRow *const row) { - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.empty()) { - (*row) = std::move(new_row); - return Status::OK(); - } - (void)std::transform(projected_column_indices_.begin(), projected_column_indices_.end(), std::back_inserter(*row), - [&new_row](uint32_t x) { return new_row[x]; }); - // Now if columns changed after map, we don't know which column we should keep, - // so temporarily we don't support print file_path after ProjectOp. - new_row.setPath({}); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/project_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/project_op.h deleted file mode 100644 index bf93784a6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/project_op.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PROJECT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PROJECT_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" - -namespace mindspore { -namespace dataset { -class ProjectOp : public PipelineOp { - public: - // Constructor of the ProjectOp. - // @param columnsToProject - - explicit ProjectOp(const std::vector &columns_to_project); - - // Destructor. - ~ProjectOp() = default; - - // A print method typically used for debugging. - // @param out - The output stream to write output to. - // @param show_all - A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload. - // @notes This allows you to write the debug print info using stream operators. - // @param out - reference to the output stream being overloaded. - // @param project_op - reference to the ProjectOp to display. - // @return - the output stream must be returned. - friend std::ostream &operator<<(std::ostream &out, const ProjectOp &project_op) { - project_op.Print(out, false); - return out; - } - - // Class functor operator () override. - // Most dataset ops operate by launching a thread (see ExecutionTree). - // However, the ProjectOp is defined as a inlined operator, so it is invalid to launch the - // functor since this op runs inlined inside another operator. The function is overloaded to - // ensure that it is not called by mistake (it will generate an error). - // @return Status The status code returned - Status operator()() override; - - // Gets a row from the child node and projects that row. The caller is typically our parent node. - // @param row - output pointer to the projected row. - // @param worker_id - The worker id - Status GetNextRow(TensorRow *row) override; - - // Base-class override for special eoe handler. - // Inline operators must override this because there is no connector to push eoe onto. - // @return Status The status code returned - Status EoeReceived(int32_t worker_id) override; - - // Base-class override for special eof handler. - // Inline operators must override this because there is no connector to push eof onto. - // @return Status The status code returned - Status EofReceived(int32_t worker_id) override; - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kProjectOp; } - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - std::vector columns_to_project_; - std::vector projected_column_indices_; - - TensorRow Project(const TensorRow &row); - - // Computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_PROJECT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.cc deleted file mode 100644 index bdc2f6ab9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.cc +++ /dev/null @@ -1,320 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/callback/callback_param.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include "mindspore-lite/minddata/dataset/util/monitor.h" - -namespace mindspore { -namespace dataset { -// Constructor of ReceiveBridgeOp -ReceiveBridgeOp::ReceiveBridgeOp(int32_t op_connector_size, SharedMemoryQueue receive_queue, MessageQueue msg_queue) - : ParallelOp(1, op_connector_size), - receive_queue_(receive_queue), - msg_queue_(msg_queue), - subprocess_pid_(-1), - err_status_(Status::OK()) { - receive_info_.normal_row_.sample_ = 0; - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kNone; - receive_info_.eoe_row_.sample_ = 0; - receive_info_.eoe_row_.row_step_ = ReceiveBridgeOp::RowStep::kNone; - receive_info_.eof_row_.sample_ = 0; - receive_info_.eof_row_.row_step_ = ReceiveBridgeOp::RowStep::kNone; -} - -ReceiveBridgeOp::~ReceiveBridgeOp() { - receive_queue_.SetReleaseFlag(true); - msg_queue_.SetReleaseFlag(true); - - std::string err_msg = "Dataset ReceiveOp normal_row: " + std::to_string(receive_info_.normal_row_.sample_) + - ", status: " + std::to_string(receive_info_.normal_row_.row_step_) + - ", eoe_row: " + std::to_string(receive_info_.eoe_row_.sample_) + - ", status: " + std::to_string(receive_info_.eoe_row_.row_step_) + - ", eof_row: " + std::to_string(receive_info_.eof_row_.sample_) + - ", status: " + std::to_string(receive_info_.eof_row_.row_step_); - if (receive_info_.normal_row_.row_step_ == 0 || - receive_info_.normal_row_.row_step_ == ReceiveBridgeOp::RowStep::kNone) { - return; - } - if (receive_info_.normal_row_.row_step_ != ReceiveBridgeOp::RowStep::kAfterReceiveMsg) { - MS_LOG(WARNING) << err_msg; - } - if (receive_info_.eoe_row_.row_step_ == ReceiveBridgeOp::RowStep::kNone) { - return; - } - if (receive_info_.normal_row_.row_step_ == ReceiveBridgeOp::RowStep::kAfterReceiveMsg && - receive_info_.eoe_row_.row_step_ != ReceiveBridgeOp::RowStep::kAfterReceiveMsg) { - MS_LOG(WARNING) << err_msg; - } - if (receive_info_.eof_row_.row_step_ == ReceiveBridgeOp::RowStep::kNone) { - return; - } - if (receive_info_.normal_row_.row_step_ == ReceiveBridgeOp::RowStep::kAfterReceiveMsg && - receive_info_.eoe_row_.row_step_ == ReceiveBridgeOp::RowStep::kAfterReceiveMsg && - receive_info_.eof_row_.row_step_ != ReceiveBridgeOp::RowStep::kAfterReceiveMsg) { - MS_LOG(WARNING) << err_msg; - } -} - -// A print method typically used for debugging -void ReceiveBridgeOp::Print(std::ostream &out, bool show_all) const { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; -} - -Status ReceiveBridgeOp::MonitorIndependentDatasetProcess() { - TaskManager::FindMe()->Post(); - - while (!tree_->isFinished()) { - if (mindspore::dataset::this_thread::is_interrupted()) { - // send message to independent process and independent process will exit - RETURN_IF_NOT_OK(msg_queue_.MsgSnd(kMasterReceiveBridgeOpFinishedMsg)); - - // sleep waiting for independent dataset process get the message queue - sleep(kMonitorInterval * kSleepDelays); - - MS_LOG(INFO) << "Monitor thread get interrupt signal and will exit."; - break; - } - - auto st = MonitorSubprocess(subprocess_pid_); - if (st != Status::OK() && receive_info_.eof_row_.row_step_ == 0) { - MS_LOG(INFO) << "Monitor thread detects that the independent dataset process is abnormal."; - err_status_ = st; - break; - } - - // get error flag from dataset independent process - if (msg_queue_.MsgRcv(kWorkerErrorMsg, IPC_NOWAIT) > 0) { - MS_LOG(INFO) << "Monitor thread get err status from the independent dataset process."; - err_status_ = msg_queue_.DeserializeStatus(); - break; - } - - sleep(kMonitorInterval); // check the independent dataset process status in every 1s - } - - // release the message queue - msg_queue_.SetReleaseFlag(true); - - // this will break hung by MsgRcv which is in ReceiveBridgeOp::operator() - msg_queue_.ReleaseQueue(); - - // release the shm memory queue - if (receive_queue_.ReleaseCurrentShm() != Status::OK()) { - MS_LOG(ERROR) << "Release the shm memory failed."; - } - - // Quit all workers, this code might never be reached if EpochCtrl is -1. - for (int32_t wkr_id = 0; wkr_id < num_workers_; wkr_id++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - - return Status::OK(); -} - -// This class functor will provide the master loop that drives the logic for performing the work -Status ReceiveBridgeOp::operator()() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - - // start the monitor thread - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "ReceiveBridge-MonitorIndependentDatasetProcess", - std::bind(&ReceiveBridgeOp::MonitorIndependentDatasetProcess, this), nullptr, id())); - - // Synchronize with TaskManager - TaskManager::FindMe()->Post(); - - // Get msg from the independent dataset process by msg_queue_ - receive_info_.normal_row_.sample_ = 0; - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kBeginReceiveMsg; - auto status = msg_queue_.MsgRcv(kWorkerSendDataMsg); - if (status != Status::OK()) { - if (err_status_ != Status::OK()) { - MS_LOG(INFO) << "The independent dataset process exit, please check the error message."; - return err_status_; - } else { - return status; - } - } - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterReceiveMsg; - - TensorRow new_row; - RETURN_IF_NOT_OK(receive_queue_.ToTensorRow(&new_row, msg_queue_.shm_id_, msg_queue_.shm_size_)); - - while (!new_row.eof()) { - while (!new_row.eoe()) { - RETURN_IF_INTERRUPTED(); - receive_info_.normal_row_.sample_ += 1; - // The NextWorkerID() should always 0, because ReceiveBridgeOp is a single thread op - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - - MS_LOG(INFO) << "Dataset ReceiveOp normal_row: " << std::to_string(receive_info_.normal_row_.sample_) - << ", status: " << receive_info_.normal_row_.row_step_ - << ", eoe_row: " << std::to_string(receive_info_.eoe_row_.sample_) - << ", status: " << receive_info_.eoe_row_.row_step_ - << ", eof_row: " << std::to_string(receive_info_.eof_row_.sample_) - << ", status: " << receive_info_.eof_row_.row_step_; - - // Send msg to the independent dataset process by msg_queue_ - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kBeginSendMsg; - RETURN_IF_NOT_OK(msg_queue_.MsgSnd(kMasterSendDataMsg, msg_queue_.shm_id_, msg_queue_.shm_size_)); - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterSendMsg; - - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kBeginReceiveMsg; - // Get msg from the independent dataset process by msg_queue_ - status = msg_queue_.MsgRcv(kWorkerSendDataMsg); - if (status != Status::OK()) { - if (err_status_ != Status::OK()) { - MS_LOG(INFO) << "The independent dataset process exit, please check the error message."; - return err_status_; - } else { - // if the pipeline had been interrupted, ignore the MsgRcv error - RETURN_IF_INTERRUPTED(); - if (tree_->isFinished()) { - return Task::OverrideInterruptRc(this_thread::GetInterruptStatus()); - } - return status; - } - } - receive_info_.normal_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterReceiveMsg; - - RETURN_IF_NOT_OK(receive_queue_.ToTensorRow(&new_row, msg_queue_.shm_id_, msg_queue_.shm_size_)); - } - - receive_info_.eoe_row_.sample_ += 1; - - // Propagate the eoe row to worker - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - UpdateRepeatAndEpochCounter(); - - // Send msg to the independent dataset process by msg_queue_ - receive_info_.eoe_row_.row_step_ = ReceiveBridgeOp::RowStep::kBeginSendMsg; - RETURN_IF_NOT_OK(msg_queue_.MsgSnd(kMasterSendDataMsg, msg_queue_.shm_id_, msg_queue_.shm_size_)); - receive_info_.eoe_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterSendMsg; - - // Get msg from the independent dataset process by msg_queue_ - receive_info_.eoe_row_.row_step_ = ReceiveBridgeOp::RowStep::kBeginReceiveMsg; - status = msg_queue_.MsgRcv(kWorkerSendDataMsg); - if (status != Status::OK()) { - if (err_status_ != Status::OK()) { - MS_LOG(INFO) << "The independent dataset process exit, please check the error message."; - return err_status_; - } else { - // if the pipeline had been interrupted, ignore the MsgRcv error - RETURN_IF_INTERRUPTED(); - if (tree_->isFinished()) { - return Task::OverrideInterruptRc(this_thread::GetInterruptStatus()); - } - return status; - } - } - receive_info_.eoe_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterReceiveMsg; - - RETURN_IF_NOT_OK(receive_queue_.ToTensorRow(&new_row, msg_queue_.shm_id_, msg_queue_.shm_size_)); - } - receive_info_.eof_row_.sample_ += 1; - receive_info_.eof_row_.row_step_ = ReceiveBridgeOp::RowStep::kAfterReceiveMsg; - - // End() is commented out because it might never be called due to the lack of EOF when EpochCtrl is -1 - // Handle eof logic, this code might never be reached if epoch_ctrl = -1. - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - - // Quit all workers, this code might never be reached if EpochCtrl is -1. - for (int32_t wkr_id = 0; wkr_id < num_workers_; wkr_id++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - - return Status::OK(); -} - -// Private function for worker/thread to loop continuously. It comprises the main -// logic of ReceiveBridgeOp: getting the data from previous Op, validating user specified column names, -// applying a list of TensorOps to each of the data, process the results and then -// pushing them back to ReceiveBridgeOp's output Connector to be fetched by the next Op. -Status ReceiveBridgeOp::WorkerEntry(int32_t worker_id) { - // Handshake with TaskManager that thread creation is successful. - TaskManager::FindMe()->Post(); - - uint64_t start_time = GetSyscnt(); - TensorRow in_row; - // Fetch next data from parent node - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(worker_id)]->PopFront(&in_row)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "ReceiveBridgeGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - start_time = GetSyscnt(); - - // Now that init work is done, drop into the main fetching loop. - // receive op does not use child iterator, and it needs to manually handle eoe and eof's itself - // rather than use the base-class defaults. - while (true) { - // Handle special logic where row carries a ctrl flag. - if (in_row.Flags() != TensorRow::kFlagNone) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "ReceiveBridgeProcess", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - if (in_row.quit()) { - break; - } - } - // Push the row onto the connector for next operator to consume. - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(in_row))); - - start_time = GetSyscnt(); - // Fetch next data from parent node - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(worker_id)]->PopFront(&in_row)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "ReceiveBridgeGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - start_time = GetSyscnt(); - } - - return Status::OK(); -} - -Status ReceiveBridgeOp::SendQuitFlagToWorker(int32_t worker_id) { - TensorRow quit_flag(TensorRow::kFlagQuit); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::move(quit_flag))); - return Status::OK(); -} - -Status ReceiveBridgeOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eoe()) { - UpdateRepeatAndEpochCounter(); - } - (*row) = std::move(new_row); - return Status::OK(); -} - -MessageQueue ReceiveBridgeOp::GetMessageQueue() { return msg_queue_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h deleted file mode 100644 index 91d71b5b7..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RECEIVE_BRIDGE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RECEIVE_BRIDGE_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/core/shared_memory_queue.h" -#include "mindspore-lite/minddata/dataset/core/message_queue.h" - -namespace mindspore { -namespace dataset { -const int kMonitorInterval = 1; -const int kSleepDelays = 2; - -class ReceiveBridgeOp : public ParallelOp { - public: - enum RowStep { - kNone = 0, - kBeginReceiveMsg, - kAfterReceiveMsg, - kBeginSendMsg, - kAfterSendMsg, - }; - struct RowStatus { - uint64_t sample_; - RowStep row_step_; - }; - struct ReceiveInfo { - RowStatus normal_row_; - RowStatus eoe_row_; - RowStatus eof_row_; - }; - - ReceiveBridgeOp(int32_t op_connector_size, SharedMemoryQueue receive_queue, MessageQueue msg_queue); - - // Destructor - ~ReceiveBridgeOp(); - - // A print method typically used for debugging - // @param out The output stream to write output to - // @param show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out reference to the output stream being overloaded - // @param mo reference to the ReceiveBridgeOp to display - // @return the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ReceiveBridgeOp &mo) { - mo.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // This main thread creates local queues, pulls TensorRow from the previous - // op's Connector and distributes them to the local queues. Workers pull from the local queues. - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kReceiveBridgeOp; } - - /// Send quit flag row to worker at worker_id to make it exit - /// \param worker_id id of the worker - /// \return Status code - Status SendQuitFlagToWorker(int32_t worker_id) override; - - Status GetNextRowPullMode(TensorRow *const row) override; - - MessageQueue GetMessageQueue(); - - void SetSubprocessID(int pid) { subprocess_pid_ = pid; } - - Status MonitorIndependentDatasetProcess(); - - private: - std::unique_ptr child_iterator_; // An iterator for fetching. - - // Private function for worker/thread to loop continuously. It comprises the main - // logic of ReceiveBridgeOp: getting the data from previous Op, validating user specified column names, - // applying a list of TensorOps to each of the data, process the results and then - // pushing them back to ReceiveBridgeOp's output Connector to be fetched by the next Op. - // @param worker_id The id assigned to this thread/worker upon creation. - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; // In: workerId assigned by tree_ - - private: - ReceiveInfo receive_info_; // receive info, including msgrcv and msgsnd status - int subprocess_pid_; // the independent dataset process id - Status err_status_; // the err status from independent dataset process - SharedMemoryQueue receive_queue_; // get data from independent process - MessageQueue msg_queue_; // get msg from independent process - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RECEIVE_BRIDGE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.cc deleted file mode 100644 index 30187e8b7..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.cc +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// constructor -RenameOp::RenameOp(const std::vector &in_col_names, const std::vector &out_col_names) - : PipelineOp(0), in_columns_(in_col_names), out_columns_(out_col_names) {} - -// destructor -RenameOp::~RenameOp() {} - -// Gets a row from the child operator -Status RenameOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -// For Pullmode, gets a row from the child operator -Status RenameOp::GetNextRowPullMode(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - } - return Status::OK(); -} - -Status RenameOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] RenameOp is an inlined operator."); } - -// Rename core functionality to compute the new column name id map. -// We need to overwrite the super class ComputeColMap here because we're making a modification of the -// map from the child map. -Status RenameOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - column_name_id_map_ = child_[0]->column_name_id_map(); - // iterate over my index in input vector, find the corresponding position - std::unordered_map new_col_name_id_map = {}; - // parameter for input check - size_t found = 0; - std::set new_col_name; - - // iterate over all the pairs and if there is a name match with rename, rename the column and add it to new map - // by doing it this way we recreate a new ColNameIdMap and allow for switching - for (const auto &pair : column_name_id_map_) { - std::string name = pair.first; - int32_t id = pair.second; - // find name - std::vector::iterator it = std::find(in_columns_.begin(), in_columns_.end(), name); - // for c input checks here we have to count the number of times we find the stuff in in_columns_ - // because we iterate over the mInputList n times - if (it != in_columns_.end()) { - // found - found += 1; - int index = std::distance(in_columns_.begin(), it); - MS_LOG(DEBUG) << "Rename operator index found " << index << " value " << id << "."; - if (new_col_name.find(out_columns_[index]) != new_col_name.end()) { - std::string err_msg( - "Invalid column, rename operation does not support rename one column name into another already exist " - "column name, existing column name is: " + - out_columns_[index] + "."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - new_col_name_id_map[out_columns_[index]] = id; - new_col_name.insert(out_columns_[index]); - } else { - // not found - if (new_col_name.find(name) != new_col_name.end()) { - std::string err_msg( - "Invalid column, rename operation does not support rename one column name into another already exist " - "column name, existing column name is: " + - name + "."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - MS_LOG(DEBUG) << "Rename operator index not found: " << id << " is the column id."; - new_col_name_id_map[name] = id; - new_col_name.insert(name); - } - } - // only checks number of renamed columns have been found, this input check doesn't check everything - if (found != in_columns_.size()) { - MS_LOG(DEBUG) << "Rename operator column names found: " << found << " out of " << in_columns_.size() << "."; - std::string err_msg = "Invalid column, column to be renamed does not exist."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Now, overwrite our column map with the new renamed columns/id's - column_name_id_map_ = new_col_name_id_map; - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// prints rename -void RenameOp::Print(std::ostream &out, // In: The output stream to print to - bool show_all) const { // In: T/F if it should print everything - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nIn columns:"; - for (size_t i = 0; i < in_columns_.size(); ++i) { - out << "\n " << in_columns_[i]; - } - for (size_t i = 0; i < out_columns_.size(); ++i) { - out << "\n " << out_columns_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h deleted file mode 100644 index 2683ddd03..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RENAME_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RENAME_OP_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RenameOp : public PipelineOp { - public: - // Constructor for RenameOp - // @param in_col_names names of columns to rename - // @param out_col_names names of columns after rename - // @param op_connector_size connector size - RenameOp(const std::vector &in_col_names, const std::vector &out_col_names); - - // Destructor - ~RenameOp(); - - // Print function for Rename - // @param out output stream to print to - // @param show_all if it should print everything - void Print(std::ostream &out, bool show_all) const override; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RenameOp &ro) { - ro.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kRenameOp; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRow(TensorRow *row) override; - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return Implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - protected: - // Rename core functionality - // Computing the assignment of the new column name map. - // @return - Status - Status ComputeColMap() override; - - // Variable to store the input column names - std::vector in_columns_; - - // Variable to store the output column names - std::vector out_columns_; - - std::unique_ptr child_iterator_; // An iterator for fetching. -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_RENAME_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.cc deleted file mode 100644 index eabf12d65..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.cc +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// Constructor of the RepeatOp. -RepeatOp::RepeatOp(int32_t count) : PipelineOp(0), num_repeats_(count), repeat_count_(0) {} - -// Destructor -RepeatOp::~RepeatOp() {} - -// A print method typically used for debugging -void RepeatOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [repeats: " << num_repeats_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nCurrent count: " << repeat_count_ << "\nMax count: " << num_repeats_ << "\nLeaf Nodes in execution path:"; - if (!eoe_ops_.empty()) { - for (size_t i = 0; i < eoe_ops_.size(); i++) { - out << "\n Operator: " << eoe_ops_[i]->id(); - } - } else { - out << " None."; - } - out << "\n\n"; - } -} - -// This function returns the row that is at the top of our output connector. The caller is -// typically our parent node, when the parent is asking us to provide the next row of data. -// Since RepeatOp is an inlined op, getting a row from us will simply bounce you to get -// a row from our child. -// This function sets the `retryIfEoe` flag when popping from the child connector. This way, -// this function will retry to pop the connector again and will get the non-EOE row if any. -Status RepeatOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - if (child_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Pipeline init failed, RepeatOp can't be the first op in pipeline."); - } - - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - // Loop until non EOE is received - while (row->eoe()) { - RETURN_IF_NOT_OK(EoeReceived(0)); - if (state_ == OpState::kDeOpIdle) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); - } - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - } - // Check if the last buf is next eof - if (row->eof()) { - RETURN_IF_NOT_OK(EofReceived(0)); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -// Base-class override for handling cases when an eoe is received. -Status RepeatOp::EoeReceived(int32_t worker_id) { - UpdateRepeatAndEpochCounter(); - repeat_count_++; - MS_LOG(DEBUG) << "Repeat operator (" << operator_id_ - << ") end of epoch message received. Repeat count is now: " << repeat_count_ << "."; - - if (repeat_count_ == num_repeats_) { - repeat_count_ = 0; - state_ = OpState::kDeOpIdle; - return Status::OK(); - } else { - state_ = OpState::kDeOpRunning; - } - - // Invoke a reset against the eoe nodes only. - for (auto &eoe_op : eoe_ops_) { - MS_LOG(DEBUG) << "Repeat operator sending reset to operator: " << eoe_op->id(); - RETURN_IF_NOT_OK(eoe_op->Reset()); - } - - return Status::OK(); -} - -// Class functor operator () override. -// Most dataset ops operate by launching a thread (see ExecutionTree). -// However, the RepeatOp is defined as a inlined operator, so it is invalid to launch the -// functor since this op runs inlined inside another operator. The function is overloaded to -// ensure that it is not called by mistake (it will generate an error). -Status RepeatOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] RepeatOp is an inlined operator."); } - -// Base-class override for handling cases when an eof is received. -Status RepeatOp::EofReceived(int32_t worker_id) { - MS_LOG(DEBUG) << "Repeat operator EOF received, do nothing now."; - return Status::OK(); -} - -// Drive reset actions if needed -Status RepeatOp::Reset() { - // If there's nested repeats, an ascendant repeat may have ourself listed as an eoe op. - // In that case, we now have to bounce the reset down to our own eoe ops. - MS_LOG(DEBUG) << "Repeat operator " << operator_id_ << " got reset."; - for (auto &eoe_op : eoe_ops_) { - MS_LOG(DEBUG) << "Nested repeat operator bouncing a reset to operator: " << eoe_op->id(); - RETURN_IF_NOT_OK(eoe_op->Reset()); - } - state_ = OpState::kDeOpRunning; - return Status::OK(); -} - -int64_t RepeatOp::GetTreeRepeatCount() { return num_repeats_; } - -Status RepeatOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (child_.empty()) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Pipeline init failed, RepeatOp can't be the leaf node(first operator) of pipeline."); - } - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - // Loop until non EOE is received - while (row->eoe()) { - MS_LOG(INFO) << "RepeatOp::GetNextRowPullMode eoe received."; - RETURN_IF_NOT_OK(EoeReceived(0)); - if (state_ == OpState::kDeOpIdle) { - return Status::OK(); - } - // Reset TensorRow (both vector and flags) - row->reset(); - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - } - // Check if the last buf is next eof - if (row->eof()) { - RETURN_IF_NOT_OK(EofReceived(0)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h deleted file mode 100644 index 26a3e9d83..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_REPEAT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_REPEAT_OP_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" - -namespace mindspore { -namespace dataset { -class RepeatOp : public PipelineOp { - public: - // Constructor of the RepeatOp. - // @note The builder class should be used to call it - // @param count - The number of repeats to do - explicit RepeatOp(int32_t count); - - // Destructor - ~RepeatOp(); - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param ro - reference to the RepeatOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const RepeatOp &ro) { - ro.Print(out, false); - return out; - } - - // Class functor operator () override. - // Most dataset ops operate by launching a thread (see ExecutionTree). - // However, the RepeatOp is defined as a inlined operator, so it is invalid to launch the - // functor since this op runs inlined inside another operator. The function is overloaded to - // ensure that it is not called by mistake (it will generate an error). - // @return Status The status code returned - Status operator()() override; - - // This function returns the row that is at the top of our output connector. The caller is - // typically our parent node, when the parent is asking us to provide the next row of data. - // Since RepeatOp is an inlined op, getting a row from us will simply bounce you to get - // a row from our child. - // @param row - output pointer to the buffer that it will fetch. - // @return Status The status code returned - Status GetNextRow(TensorRow *row) override; - - // Base-class override for handling cases when an eoe is received. - // @param worker_id - The worker id - Status EoeReceived(int32_t worker_id) override; - - // Base-class override for handling cases when an eof is received. - // @param worker_id - The worker id - Status EofReceived(int32_t worker_id) override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kRepeatOp; } - - /// \brief Getter function - /// \return The number of repeats that the user requested - int32_t num_repeats() { return num_repeats_; } - - /// \brief reset Op - /// \@return Status The status code returned - Status Reset() override; - - int64_t GetTreeRepeatCount() override; - - // \brief Adds an operator to the repeat ops list of tracked leaf/eoe nodes - // \param[in] eoe_op The input leaf/eoe operator to add to the list - void AddToEoeList(std::shared_ptr eoe_op) { eoe_ops_.push_back(std::move(eoe_op)); } - - std::vector> eoe_ops_; // List of operators that can generate EOE underneath this repeat. - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - // The number of repeats that the user requested. - // Note that num_repeats_ is different with op_total_repeats_ or op_num_repeats_per_epoch_ in base DatasetOp class. - // For example, for repeat1 op in pipeline tfreader -> repeat1(3) -> repeat2(2) -> epoch ctrl(4), - // num_repeats_ = 3, op_total_repeats_ = 24, op_num_repeats_per_epoch_ = 6. - int32_t num_repeats_; - // A counter for the current number of executed repeats. - // Note that repeat_count_ is different with op_current_repeats_ in the base DatasetOp class - // because it counts the repeats in the current epoch, whereas op_current_repeats_ counts the global total repeats. - int32_t repeat_count_; - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_REPEAT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.cc deleted file mode 100644 index 74fbc1eca..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.cc +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -// Constructor of SendBridgeOp -SendBridgeOp::SendBridgeOp(int32_t op_connector_size, SharedMemoryQueue send_queue, MessageQueue msg_queue) - : ParallelOp(1, op_connector_size), send_queue_(send_queue), msg_queue_(msg_queue) { - send_info_.normal_row_.sample_ = 0; - send_info_.normal_row_.row_step_ = SendBridgeOp::RowStep::kNone; - send_info_.eoe_row_.sample_ = 0; - send_info_.eoe_row_.row_step_ = SendBridgeOp::RowStep::kNone; - send_info_.eof_row_.sample_ = 0; - send_info_.eof_row_.row_step_ = SendBridgeOp::RowStep::kNone; -} - -SendBridgeOp::~SendBridgeOp() { - std::string err_msg = "Dataset SendOp normal_row: " + std::to_string(send_info_.normal_row_.sample_) + - ", status: " + std::to_string(send_info_.normal_row_.row_step_) + - ", eoe_row: " + std::to_string(send_info_.eoe_row_.sample_) + - ", status: " + std::to_string(send_info_.eoe_row_.row_step_) + - ", eof_row: " + std::to_string(send_info_.eof_row_.sample_) + - ", status: " + std::to_string(send_info_.eof_row_.row_step_); - if (send_info_.normal_row_.row_step_ == 0 || send_info_.normal_row_.row_step_ == SendBridgeOp::RowStep::kNone) { - return; - } - if (send_info_.normal_row_.row_step_ != SendBridgeOp::RowStep::kAfterReceiveMsg) { - MS_LOG(WARNING) << err_msg; - } - if (send_info_.eoe_row_.row_step_ == SendBridgeOp::RowStep::kNone) { - return; - } - if (send_info_.normal_row_.row_step_ == SendBridgeOp::RowStep::kAfterReceiveMsg && - send_info_.eoe_row_.row_step_ != SendBridgeOp::RowStep::kAfterReceiveMsg) { - MS_LOG(WARNING) << err_msg; - } - if (send_info_.eof_row_.row_step_ == SendBridgeOp::RowStep::kNone) { - return; - } - if (send_info_.normal_row_.row_step_ == SendBridgeOp::RowStep::kAfterReceiveMsg && - send_info_.eoe_row_.row_step_ == SendBridgeOp::RowStep::kAfterReceiveMsg && - send_info_.eof_row_.row_step_ != SendBridgeOp::RowStep::kAfterSendMsg) { - MS_LOG(WARNING) << err_msg; - } -} - -// A print method typically used for debugging -void SendBridgeOp::Print(std::ostream &out, bool show_all) const { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; -} - -// This class functor will provide the master loop that drives the logic for performing the work -Status SendBridgeOp::operator()() { - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - - // Synchronize with TaskManager - TaskManager::FindMe()->Post(); - - child_iterator_ = std::make_unique(this, 0, 0); - TensorRow new_row; - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - - while (!new_row.eof()) { - while (!new_row.eoe()) { - RETURN_IF_INTERRUPTED(); - // The NextWorkerID() should always 0, because SendBridgeOp is a single thread op - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - - // Propagate the eoe row to worker - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - UpdateRepeatAndEpochCounter(); - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } - // End() is commented out because it might never be called due to the lack of EOF when EpochCtrl is -1 - // Handle eof logic, this code might never be reached if epoch_ctrl = -1. - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::move(new_row))); - - // Quit all workers, this code might never be reached if EpochCtrl is -1. - for (int32_t wkr_id = 0; wkr_id < num_workers_; wkr_id++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - - return Status::OK(); -} - -// Private function for worker/thread to loop continuously. It comprises the main -// logic of SendBridgeOp: getting the data from previous Op, validating user specified column names, -// applying a list of TensorOps to each of the data, process the results and then -// pushing them back to SendBridgeOp's output Connector to be fetched by the next Op. -Status SendBridgeOp::WorkerEntry(int32_t worker_id) { - // Handshake with TaskManager that thread creation is successful. - TaskManager::FindMe()->Post(); - - send_info_.normal_row_.sample_ = 0; - - uint64_t start_time = GetSyscnt(); - TensorRow in_row; - // Fetch next data from parent node - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(worker_id)]->PopFront(&in_row)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "SendBridgeGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - start_time = GetSyscnt(); - - uint64_t *current_sample = nullptr; - SendBridgeOp::RowStep *current_step = nullptr; - - // Now that init work is done, drop into the main fetching loop. - // send op does not use child iterator, and it needs to manually handle eoe and eof's itself - // rather than use the base-class defaults. - while (true) { - RETURN_IF_INTERRUPTED(); - // Handle special logic where row carries a ctrl flag. - if (in_row.Flags() != TensorRow::kFlagNone) { - if (in_row.quit()) { - break; - } - } - - current_sample = &send_info_.normal_row_.sample_; - current_step = &send_info_.normal_row_.row_step_; - if (in_row.eoe()) { - current_sample = &send_info_.eoe_row_.sample_; - current_step = &send_info_.eoe_row_.row_step_; - } else if (in_row.eof()) { - current_sample = &send_info_.eof_row_.sample_; - current_step = &send_info_.eof_row_.row_step_; - } - - // Copy the in_row to shared memory - RETURN_IF_NOT_OK(send_queue_.FromTensorRow(in_row)); - - // Send msg to the main process by msg_queue_ - *current_step = SendBridgeOp::RowStep::kBeginSendMsg; - RETURN_IF_NOT_OK(msg_queue_.MsgSnd(kWorkerSendDataMsg, send_queue_.GetShmID(), send_queue_.GetShmSize())); - *current_step = SendBridgeOp::RowStep::kAfterSendMsg; - - *current_sample += 1; - - MS_LOG(INFO) << "Dataset SendOp normal_row: " << std::to_string(send_info_.normal_row_.sample_) - << ", status: " << send_info_.normal_row_.row_step_ - << ", eoe_row: " << std::to_string(send_info_.eoe_row_.sample_) - << ", status: " << send_info_.eoe_row_.row_step_ - << ", eof_row: " << std::to_string(send_info_.eof_row_.sample_) - << ", status: " << send_info_.eof_row_.row_step_; - - // all tensor row had been sent to main process from independent process - if (in_row.eof()) { - break; - } - // Get msg from the main process by msg_queue_ - *current_step = SendBridgeOp::RowStep::kBeginReceiveMsg; - auto ret = msg_queue_.MsgRcv(kMasterSendDataMsg); - if (ret != Status::OK()) { - *current_step = SendBridgeOp::RowStep::kAfterReceiveMsg; - tree_->SetFinished(); // the independent dataset process will exit - if (msg_queue_.state_ != MessageQueue::State::kReleased) { - return ret; - } - return Status::OK(); - } - *current_step = SendBridgeOp::RowStep::kAfterReceiveMsg; - - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "SendBridgeProcess", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - start_time = GetSyscnt(); - - // Fetch next data from parent node - RETURN_IF_NOT_OK(worker_in_queues_[static_cast(worker_id)]->PopFront(&in_row)); - - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "SendBridgeGet", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - start_time = GetSyscnt(); - } - - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "SendBridgeProcess", start_time, {{"TensorRowFlags", in_row.FlagName()}})); - return Status::OK(); -} - -Status SendBridgeOp::SendQuitFlagToWorker(int32_t worker_id) { - TensorRow quit_flag(TensorRow::kFlagQuit); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::move(quit_flag))); - return Status::OK(); -} - -Status SendBridgeOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - TensorRow new_row; - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eoe()) { - UpdateRepeatAndEpochCounter(); - } - (*row) = std::move(new_row); - return Status::OK(); -} - -MessageQueue::State SendBridgeOp::MessageQueueState() { return msg_queue_.MessageQueueState(); } - -MessageQueue SendBridgeOp::GetMessageQueue() { return msg_queue_; } - -SharedMemoryQueue SendBridgeOp::GetSharedMemoryQueue() { return send_queue_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h deleted file mode 100644 index 787671338..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SEND_BRIDGE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SEND_BRIDGE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/core/shared_memory_queue.h" -#include "mindspore-lite/minddata/dataset/core/message_queue.h" - -namespace mindspore { -namespace dataset { -class SendBridgeOp : public ParallelOp { - public: - enum RowStep { - kNone = 0, - kBeginSendMsg, - kAfterSendMsg, - kBeginReceiveMsg, - kAfterReceiveMsg, - }; - struct RowStatus { - uint64_t sample_; - RowStep row_step_; - }; - struct SendInfo { - RowStatus normal_row_; - RowStatus eoe_row_; - RowStatus eof_row_; - }; - - SendBridgeOp(int32_t op_connector_size, SharedMemoryQueue send_queue, MessageQueue msg_queue); - - // Destructor - ~SendBridgeOp(); - - // A print method typically used for debugging - // @param out The output stream to write output to - // @param show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out reference to the output stream being overloaded - // @param mo reference to the SendBridgeOp to display - // @return the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const SendBridgeOp &mo) { - mo.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // This main thread creates local queues, pulls TensorRow from the previous - // op's Connector and distributes them to the local queues. Workers pull from the local queues. - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kSendBridgeOp; } - - /// Send quit flag row to worker at worker_id to make it exit - /// \param worker_id id of the worker - /// \return Status code - Status SendQuitFlagToWorker(int32_t worker_id) override; - - Status GetNextRowPullMode(TensorRow *const row) override; - - MessageQueue::State MessageQueueState(); - - MessageQueue GetMessageQueue(); - - SharedMemoryQueue GetSharedMemoryQueue(); - - private: - std::unique_ptr child_iterator_; // An iterator for fetching. - - // Private function for worker/thread to loop continuously. It comprises the main - // logic of SendBridgeOp: getting the data from previous Op, validating user specified column names, - // applying a list of TensorOps to each of the data, process the results and then - // pushing them back to SendBridgeOp's output Connector to be fetched by the next Op. - // @param worker_id The id assigned to this thread/worker upon creation. - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; // In: workerId assigned by tree_ - - private: - SharedMemoryQueue send_queue_; // send data from independent process to main process - MessageQueue msg_queue_; // send msg from independent process to main process - SendInfo send_info_; // send info, including msgrcv and msgsnd status - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SEND_BRIDGE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.cc deleted file mode 100644 index aab017ba6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.cc +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#if defined(_WIN32) || defined(_WIN64) -#include -#endif -#include "mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h" -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -constexpr int32_t ShuffleOp::kShuffleStateInit; -constexpr int32_t ShuffleOp::kShuffleStateActive; -constexpr int32_t ShuffleOp::kShuffleStateDrain; - -// Constructor of the ShuffleOp -ShuffleOp::ShuffleOp(int32_t shuffle_size, uint32_t shuffle_seed, int32_t op_connector_size, bool reset_every_epoch) - : PipelineOp(op_connector_size), - shuffle_size_(shuffle_size), - shuffle_seed_(shuffle_seed), - reshuffle_each_epoch_(reset_every_epoch), - rng_(shuffle_seed), - shuffle_buffer_(std::make_unique()), - shuffle_last_row_idx_(0), - shuffle_buffer_state_(kShuffleStateInit) {} - -// Private function to re-init the shuffle op for another epoch. Shuffle op calls this by -// itself rather than waiting for the reset driven from operators above it in the pipeline. -Status ShuffleOp::SelfReset() { - MS_LOG(DEBUG) << "Shuffle operator performing a self-reset."; - // If reshuffle_each_epoch is false, then we always use the same seed for every - // epoch. - // If reshuffle_each_epoch is true, then the first epoch uses the given seed, - // and all subsequent epochs will then keep on using the rng_ without resetting it - if (!reshuffle_each_epoch_) { - rng_ = std::mt19937_64(shuffle_seed_); - } - - shuffle_buffer_ = std::make_unique(); - shuffle_last_row_idx_ = 0; - shuffle_buffer_state_ = kShuffleStateInit; - return Status::OK(); -} - -// A print method typically used for debugging -void ShuffleOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [shuffle size: " << shuffle_size_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nShuffle size: " << shuffle_size_ << "\nShuffle buffer state: " << shuffle_buffer_state_ - << "\nShuffle seed: " << shuffle_seed_ << "\n\n"; - } -} - -// Private function to add a new row to the shuffle buffer. -Status ShuffleOp::AddRowToShuffleBuffer(TensorRow new_shuffle_row) { - // If the last slot of our shuffle buffer was not the full size of the shuffle buffer then we are - // filling it during the initial fill codepath and thus growing it's size. In that case, we push - // back the new row to grow our shuffle buffer size by 1. - // If we are already at the full size, then we overwrite the last slot with our row (and the last - // slot better be empty because it should already have been swapped out during the random row - // selection that was done previously!) - if (shuffle_last_row_idx_ < (shuffle_size_ - 1)) { - shuffle_buffer_->push_back(std::move(new_shuffle_row)); - shuffle_last_row_idx_ = (shuffle_buffer_->size()) - 1; - } else { - if (!(*shuffle_buffer_)[shuffle_last_row_idx_].empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Last row of shuffle buffer should not be occupied!"); - } - (*shuffle_buffer_)[shuffle_last_row_idx_] = std::move(new_shuffle_row); - } - return Status::OK(); -} - -Status ShuffleOp::GetShuffledRowImpl(TensorRow *const row, bool is_pull_mode) { - RETURN_UNEXPECTED_IF_NULL(row); - // Step 1) - // Randomly select a slot from our shuffle buffer and copy that row into the output - // tensor table. We remove the data from the shuffle buffer, leaving that slot - // in the table as an empty vector - int64_t random_slot = rng_() % (shuffle_last_row_idx_ + 1); - TensorRow random_row = std::move((*shuffle_buffer_)[random_slot]); - *row = std::move(random_row); - - // Step 2) - // Take the last row from shuffle buffer, and swap it into the row position that was - // just vacated. This makes the shuffle buffer contiguous, with an empty slot at the - // tail of the shuffle buffer. - if (random_slot != shuffle_last_row_idx_) { - (*shuffle_buffer_)[random_slot] = std::move((*shuffle_buffer_)[shuffle_last_row_idx_]); - } - - // Step 3) - // Refill the last slot of the shuffle buffer with the next row from input if we are in the - // active state. - // If we are in the draining state, we do not need to fetch another row to replace the one we - // just drained. - if (shuffle_buffer_state_ == kShuffleStateActive) { - TensorRow new_row; - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } else { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - } - - if (!new_row.empty()) { - RETURN_IF_NOT_OK(AddRowToShuffleBuffer(std::move(new_row))); - } else { - shuffle_buffer_state_ = kShuffleStateDrain; - } - } - - // If we are draining, reposition (decrement) our tail index in the shuffle buffer since we - // just drained a row from it. - if (shuffle_buffer_state_ == kShuffleStateDrain) { - shuffle_last_row_idx_--; - } - return Status::OK(); -} - -// Class functor operator () override. -// All dataset ops operate by launching a thread (see ExecutionTree). This class functor will -// provide the master loop that drives the logic for performing the work -Status ShuffleOp::operator()() { - // Synchronize with TaskManager once the thread is launched. - TaskManager::FindMe()->Post(); - - // Shuffle op does not have workers, and only consumes from child 0. - // Create the child iterator to fetch our data from. - int32_t worker_id = 0; - int32_t child_idx = 0; - child_iterator_ = std::make_unique(this, worker_id, child_idx); - - // Main operator loop - while (true) { - // Do an initial populate of the shuffle buffer - RETURN_IF_NOT_OK(InitShuffleBuffer(false)); - - // This is our main loop exit condition, when the iterator has no more data completely. - if (child_iterator_->EofHandled()) { - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - break; - } - - // Next, enter into the main execution loop of the shuffle op. - // When the tail index position of our shuffle buffer goes negative it means that we've - // fully drained the data from the shuffle buffer and we're done. - while (shuffle_last_row_idx_ >= 0) { - TensorRow row; - RETURN_IF_NOT_OK(GetShuffledRowImpl(&row, false)); - MS_LOG(DEBUG) << "Shuffle operator sending a row to output."; - RETURN_IF_NOT_OK(out_connector_->Add(std::move(row))); - } - - // Since we overloaded eoeReceived function, we are responsible to flow the EOE up the - // pipeline manually now that we are done draining the shuffle buffer - MS_LOG(DEBUG) << "Shuffle operator sending EOE."; - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - - // Do not wait for any reset to be flown down from operators above us. - // Instead, manually update ourselves and then go reloop to start fetching from child operator - // right away. Any Reset() from the parent will still perform common reset actions. - RETURN_IF_NOT_OK(this->SelfReset()); - } - - return Status::OK(); -} - -// Private function populate the shuffle buffer initially by fetching from the child output -// connector until the shuffle buffer is full (or there is no more data coming). -Status ShuffleOp::InitShuffleBuffer(bool is_pull_mode) { - MS_LOG(DEBUG) << "Shuffle operator initializing the shuffle buffer."; - - // The first phase of this operator is to read incoming buffers and then drain those - // rows from the buffers, putting them into our own local table of tensors (the shuffle - // buffer). - // This shuffle buffer initialization phase stops when we've either filled up the - // shuffle buffer to it's max size, or the dataset below us is not providing any more - // rows. - if (shuffle_buffer_state_ != kShuffleStateInit) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Invalid shuffle buffer state, shuffle buffer should be init first or reset after each epoch."); - } - - // Before we drop into the fetching loop, call the fetch once for the first time - // to fill the first row and grab the first buffer. - TensorRow new_row; - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - if (child_iterator_->EofHandled()) { - MS_LOG(DEBUG) << "Shuffle operator init picked up EOF. No more epochs."; - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - return Status::OK(); - } - } else { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - if (new_row.eof()) { - MS_LOG(DEBUG) << "Shuffle operator init picked up EOF. No more epochs."; - eof_received_ = true; - return Status::OK(); - } - } - - if (new_row.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Unable to fetch a single row for shuffle buffer."); - } - - // Now fill the rest of the shuffle buffer until we are unable to get the next row or we reached - // the desired shuffle buffer size. - while (!new_row.empty() && shuffle_buffer_->size() < static_cast(shuffle_size_ - 1)) { - // Add the previously fetched row - RETURN_IF_NOT_OK(AddRowToShuffleBuffer(std::move(new_row))); - - // Fetch the next row - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_iterator_->FetchNextTensorRow(&new_row)); - } else { - // Fetch the next row - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(&new_row)); - } - } - - // If we quit the loop due to being at the shuffle size, still need to add the last row here. - if (!new_row.empty()) { - RETURN_IF_NOT_OK(AddRowToShuffleBuffer(std::move(new_row))); - shuffle_buffer_state_ = kShuffleStateActive; // Transition to the active state - } else { - // If init phase doesn't have more rows, then skip the active state and jump straight to the - // shuffle buffer draining state - shuffle_buffer_state_ = kShuffleStateDrain; - } - - MS_LOG(DEBUG) << "Shuffle operator finished initializing the shuffle buffer."; - return Status::OK(); -} - -Status ShuffleOp::EoeReceived(int32_t worker_id) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -Status ShuffleOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - - // Do an initial populate of the shuffle buffer - if (shuffle_buffer_state_ == kShuffleStateInit) { - RETURN_IF_NOT_OK(InitShuffleBuffer(true)); - } - - if (eof_received_) { - *row = TensorRow(TensorRow::kFlagEOF); - return Status::OK(); - } - - if (shuffle_last_row_idx_ >= 0) { - RETURN_IF_NOT_OK(GetShuffledRowImpl(row, true)); - } else { - *row = TensorRow(TensorRow::kFlagEOE); - if (IsLastIteration()) { - eof_received_ = true; - } else { - MS_LOG(DEBUG) << "Shuffle operator sending EOE."; - UpdateRepeatAndEpochCounter(); - RETURN_IF_NOT_OK(this->SelfReset()); - } - } - return Status::OK(); -} - -void ShuffleOp::Skip(int64_t skip_steps) { rng_.discard(skip_steps); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h deleted file mode 100644 index 63850e089..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SHUFFLE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SHUFFLE_OP_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -class ShuffleOp : public PipelineOp { - // Shuffle buffer state flags - // - // Shuffle buffer is in a state of being initialized - static constexpr int32_t kShuffleStateInit = 0; - - // Shuffle buffer is in a state of being actively drained from, but refilling as well - static constexpr int32_t kShuffleStateActive = 1; - - // Shuffle buffer is in a state of being drained - static constexpr int32_t kShuffleStateDrain = 2; - - public: - // Constructor of the ShuffleOp - // @note The builder class should be used to call it - // @param shuffle_size - The size for the shuffle buffer - // @param shuffle_seed - The seed to use for random number generation - // @param op_connector_size - The output connector queue size - ShuffleOp(int32_t shuffle_size, uint32_t shuffle_seed, int32_t op_connector_size, bool reset_every_epoch); - - // Destructor - ~ShuffleOp() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param so - reference to the ShuffleOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ShuffleOp &so) { - so.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Base-class override for special eoe handler. - // ShuffleOp must override this because it shall not perform default handling of eoe. Instead - // the ShuffleOp needs to manage actions related to the end of the epoch itself. - // @return Status The status code returned - Status EoeReceived(int32_t worker_id) override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kShuffleOp; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - /// \brief Skip this op by a specified number of steps to restore the internal status. - /// \param[in] skip_steps The number of steps to skip. - /// \return Status The status code. - void Skip(int64_t skip_steps); - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - // Private function to add a new row to the shuffle buffer. - // @return Status The status code returned - Status AddRowToShuffleBuffer(TensorRow new_shuffle_row); - - /// \brief Private function to populate the shuffle buffer initially by fetching from the child output - /// connector until the shuffle buffer is full (or there is no more data coming). - /// \param is_pull_mode - flag to indicate if pull mode is on - /// \return Status The status code returned - Status InitShuffleBuffer(bool is_pull_mode); - - /// \brief Gets one row out of the shuffle buffer and fills the vacant row with the last row in the buffer. This - /// implemented function is for both pull mode and non-pull mode. If it's in non-pull mode, fetch data from the - /// internal child iterator. Otherwise, fetch by calling GetNextRowPullMode() of its child node. - /// \param row[out] - Fetched TensorRow - /// \param is_pull_mode - flag to indicate if pull mode is on - /// \return Status The status code returned - Status GetShuffledRowImpl(TensorRow *row, bool is_pull_mode); - - // Private function to re-init the shuffle op for another epoch. Shuffle op calls this by - // itself rather than waiting for the reset driven from operators above it in the pipeline. - // @return Status The status code returned - Status SelfReset(); - - int32_t shuffle_size_; // User config for the size of the shuffle buffer (number of rows) - uint32_t shuffle_seed_; - bool reshuffle_each_epoch_; - // rng_ is seeded initially with shuffle_seed_. mt19937 is used for its large period. - // specifically mt19937_64 is used to generate larger random numbers to reduce bias when - // modding to fit within our desired range. we dont use a distribution - // (ie uniform_int_distribution) because we will need to create up to |dataset| instances - // of the distribution object in the common case of a perfect shuffle - std::mt19937_64 rng_; - // A single (potentially large) buffer of tensor rows for performing shuffling. - std::unique_ptr shuffle_buffer_; - int32_t shuffle_last_row_idx_; // Internal tracking of the last slot of our shuffle buffer - int32_t shuffle_buffer_state_; // State tracking for the shuffle buffer phases of work - - std::unique_ptr child_iterator_; // An iterator for fetching. - bool eof_received_{false}; // flag to indicate if eof is reached in pull mode. -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SHUFFLE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.cc deleted file mode 100644 index 695c12f7d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.cc +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// Constructor of the SkipOp. -SkipOp::SkipOp(int32_t count) : PipelineOp(0), max_skips_(count), skip_count_(0) {} - -// Destructor -SkipOp::~SkipOp() = default; - -// A print method typically used for debugging -void SkipOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [skips: " << max_skips_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSkip count: " << skip_count_ << "\nMax skips: " << max_skips_ << "\n\n"; - } -} - -Status SkipOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] SkipOp is an inlined operator."); } - -Status SkipOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - bool eoe_received = false; - while (skip_count_ < max_skips_) { - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - if (row->eof()) { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, - {{"TensorRowFlags", TensorRow(TensorRow::kFlagEOF).FlagName()}})); - return Status::OK(); - } - if (row->eoe() && !once_only_) { - eoe_received = true; - break; - } - if (!row->eoe()) { - skip_count_++; - } - } - if (!eoe_received) { - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - data_produced_++; - } - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - if (!once_only_) { - skip_count_ = 0; - } else { - // In pipeline recovery, if the skip count is equal to the dataset size, - // it means we should begin at the next epoch, so we ignore the eoe - // here and return the next data - if (data_produced_ == 1) { // eoe is the first data we get - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - } - } - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -Status SkipOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - bool eoe_received = false; - while (skip_count_ < max_skips_) { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - if (row->eof()) { - return Status::OK(); - } - if (row->eoe() && !once_only_) { - eoe_received = true; - break; - } - if (!row->eoe()) { - skip_count_++; - } - } - if (!eoe_received) { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - data_produced_++; - } - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - if (!once_only_) { - skip_count_ = 0; - } else { - // In pipeline recovery, if the skip count is equal to the dataset size, - // it means we should begin at the next epoch, so we ignore the eoe - // here and return the next data - if (data_produced_ == 1) { // eoe is the first data we get - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - } - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h deleted file mode 100644 index 2c5ad67fd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SKIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SKIP_OP_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" - -namespace mindspore { -namespace dataset { -class SkipOp : public PipelineOp { - public: - // Constructor of the SkipOp. - // @note The builder class should be used to call it - // @param count - The number of skips to do - explicit SkipOp(int32_t count); - - // Destructor - ~SkipOp(); - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kSkipOp; } - Status GetNextRow(TensorRow *row) override; - - void SetOnceOnly(bool once_only) { once_only_ = once_only; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - int32_t max_skips_; // The number of skips that the user requested - int32_t skip_count_; // A counter for the current number of executed skips - - bool once_only_ = false; // skip for skip_count_ steps only once - int64_t data_produced_ = 0; // The number of data has been pushed to the next op - - std::unique_ptr child_iterator_; // An iterator for fetching. -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SKIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.cc deleted file mode 100644 index ccde88e86..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.cc +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { -AGNewsOp::AGNewsOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, const std::vector &ag_news_list) - : CsvOp(ag_news_list, field_delim, column_default, column_name, num_workers, num_samples, worker_connector_size, - op_connector_size, shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void AGNewsOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nAGNews files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h deleted file mode 100644 index 662d0e1fd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AG_NEWS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AG_NEWS_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/auto_index.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -class AGNewsOp : public CsvOp { - public: - /// \brief Constructor. - /// \param[in] num_workers Number of workers reading images in parallel - /// \param[in] num_samples The number of samples to be included in the dataset. - /// (Default = 0 means all samples). - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. (Default = 1) - /// \param[in] device_id The device ID within num_devices. This argument should be - /// specified only when num_devices is also specified (Default = 0). - /// \param[in] field_delim A char that indicates the delimiter to separate fields (default=','). - /// \param[in] column_default List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). If this is not provided, treats all columns as string type. - /// \param[in] column_name List of column names of the dataset (default={}). If this is not provided, infers the - /// column_names from the first row of CSV file. - /// \param[in] ag_news_list List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - AGNewsOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, char field_delim, - const std::vector> &column_default, const std::vector &column_name, - const std::vector &ag_news_list); - - /// \brief Default destructor. - ~AGNewsOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out he output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a - /// summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "AGNewsOp"; } - - // DatasetName name getter - // \return DatasetName of the current Op - std::string DatasetName(bool upper = false) const { return upper ? "AGNews" : "ag news"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AG_NEWS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.cc deleted file mode 100644 index ca7854e3f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.cc +++ /dev/null @@ -1,440 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h" -#include -#include -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif - -namespace mindspore { -namespace dataset { -AlbumOp::AlbumOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool do_decode, - const std::set &exts, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_wkrs, queue_size, std::move(sampler)), - folder_path_(std::move(file_dir)), - decode_(do_decode), - extensions_(exts), - data_schema_(std::move(data_schema)), - sampler_ind_(0), - dirname_offset_(0) { - // Set the column name map (base class field) - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } -} - -// Helper function for string comparison -// album sorts the files via numerical values, so this is not a simple string comparison -bool StrComp(const std::string &a, const std::string &b) { - // returns 1 if string "a" represent a numeric value less than string "b" - // the following will always return name, provided there is only one "." character in name - // "." character is guaranteed to exist since the extension is checked before this function call. - int64_t value_a = std::stoi(a.substr(1, a.find(".")).c_str()); - int64_t value_b = std::stoi(b.substr(1, b.find(".")).c_str()); - return value_a < value_b; -} - -// Single thread to go through the folder directory and gets all file names -// calculate numRows then return -Status AlbumOp::PrepareData() { - Path folder(folder_path_); - dirname_offset_ = folder_path_.length(); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (!folder.Exists() || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid folder, " + folder_path_ + " does not exist or permission denied."); - } - MS_LOG(INFO) << "Album folder Path found: " << folder_path_ << "."; - - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (extensions_.empty() || extensions_.find(file.Extension()) != extensions_.end()) { - (void)image_rows_.push_back(file.ToString().substr(dirname_offset_)); - } else { - MS_LOG(INFO) << "Album operator unsupported file found: " << file.ToString() - << ", extension: " << file.Extension() << "."; - } - } - - std::sort(image_rows_.begin(), image_rows_.end(), StrComp); - num_rows_ = image_rows_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, AlbumDataset API can't read the data file (interface mismatch or no data found). " - "Check file path:" + - folder_path_ + "."); - } - return Status::OK(); -} - -// Only support JPEG/PNG/GIF/BMP -// Optimization: Could take in a tensor -// This function does not return status because we want to just skip bad input, not crash -bool AlbumOp::CheckImageType(const std::string &file_name, bool *valid) { - if (valid == nullptr) { - MS_LOG(ERROR) << "[Internal ERROR] Album parameter can't be nullptr."; - return false; - } - std::ifstream file_handle; - constexpr int read_num = 3; - *valid = false; - file_handle.open(file_name, std::ios::binary | std::ios::in); - if (!file_handle.is_open()) { - return false; - } - unsigned char file_type[read_num]; - (void)file_handle.read(reinterpret_cast(file_type), read_num); - - if (file_handle.fail()) { - file_handle.close(); - return false; - } - file_handle.close(); - if (file_type[0] == 0xff && file_type[1] == 0xd8 && file_type[2] == 0xff) { - // Normal JPEGs start with \xff\xd8\xff\xe0 - // JPEG with EXIF stats with \xff\xd8\xff\xe1 - // Use \xff\xd8\xff to cover both. - *valid = true; - } - return true; -} - -Status AlbumOp::LoadImageTensor(const std::string &image_file_path, int32_t col_num, TensorRow *row) { - TensorPtr image; - std::ifstream fs; - fs.open(image_file_path, std::ios::binary | std::ios::in); - if (fs.fail()) { - MS_LOG(WARNING) << "File not found:" << image_file_path << "."; - // If file doesn't exist, we don't flag this as error in input check, simply push back empty tensor - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, row)); - return Status::OK(); - } - fs.close(); - // Hack logic to replace png images with empty tensor - Path file(image_file_path); - std::set png_ext = {".png", ".PNG"}; - if (png_ext.find(file.Extension()) != png_ext.end()) { - // load empty tensor since image is not jpg - MS_LOG(INFO) << "PNG!" << image_file_path << "."; - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, row)); - return Status::OK(); - } - // treat bin files separately - std::set bin_ext = {".bin", ".BIN"}; - if (bin_ext.find(file.Extension()) != bin_ext.end()) { - // load empty tensor since image is not jpg - MS_LOG(INFO) << "Bin file found" << image_file_path << "."; - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_file_path, &image)); - row->push_back(std::move(image)); - return Status::OK(); - } - - // check that the file is an image before decoding - bool valid = false; - bool check_success = CheckImageType(image_file_path, &valid); - if (!check_success || !valid) { - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, row)); - return Status::OK(); - } - // if it is a jpeg image, load and try to decode - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_file_path, &image)); - if (decode_ && valid) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, row)); - return Status::OK(); - } - } - row->push_back(std::move(image)); - return Status::OK(); -} - -Status AlbumOp::LoadStringArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - std::vector data = json_obj; - - MS_LOG(INFO) << "String array label found: " << data << "."; - TensorPtr label; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, &label)); - row->push_back(std::move(label)); - return Status::OK(); -} - -Status AlbumOp::LoadStringTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - std::string data = json_obj; - // now we iterate over the elements in json - - MS_LOG(INFO) << "String label found: " << data << "."; - TensorPtr label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, &label)); - row->push_back(std::move(label)); - return Status::OK(); -} - -Status AlbumOp::LoadIntArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - TensorPtr label; - // consider templating this function to handle all ints - if (data_schema_->Column(col_num).Type() == DataType::DE_INT64) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, &label)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_INT32) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items1 = json_obj.items(); - using it_type = decltype(items1.begin()); - (void)std::transform(items1.begin(), items1.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, &label)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid column type, column type of " + data_schema_->Column(col_num).Name() + - " should be int32 or int64, but got " + data_schema_->Column(col_num).Type().ToString()); - } - row->push_back(std::move(label)); - return Status::OK(); -} - -Status AlbumOp::LoadFloatArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - TensorPtr float_array; - // consider templating this function to handle all ints - if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT64) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, &float_array)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT32) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items1 = json_obj.items(); - using it_type = decltype(items1.begin()); - (void)std::transform(items1.begin(), items1.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, &float_array)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid column type, column type of " + data_schema_->Column(col_num).Name() + - " should be float32 nor float64, but got " + - data_schema_->Column(col_num).Type().ToString()); - } - row->push_back(std::move(float_array)); - return Status::OK(); -} - -Status AlbumOp::LoadIDTensor(const std::string &file, int32_t col_num, TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (data_schema_->Column(col_num).Type() == DataType::DE_STRING) { - TensorPtr id; - RETURN_IF_NOT_OK(Tensor::CreateScalar(file, &id)); - row->push_back(std::move(id)); - return Status::OK(); - } - // hack to get the file name without extension, the 1 is to get rid of the backslash character - int64_t image_id = std::stoi(file.substr(1, file.find(".")).c_str()); - TensorPtr id; - RETURN_IF_NOT_OK(Tensor::CreateScalar(image_id, &id)); - MS_LOG(INFO) << "File ID " << image_id << "."; - row->push_back(std::move(id)); - return Status::OK(); -} - -Status AlbumOp::LoadEmptyTensor(int32_t col_num, TensorRow *row) { - // hack to get the file name without extension, the 1 is to get rid of the backslash character - TensorPtr empty_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({0}), data_schema_->Column(col_num).Type(), &empty_tensor)); - row->push_back(std::move(empty_tensor)); - return Status::OK(); -} - -// Loads a tensor with float value, issue with float64, we don't have reverse look up to the type -// So we actually have to check what type we want to fill the tensor with. -// Float64 doesn't work with reinterpret cast here. Otherwise we limit the float in the schema to -// only be float32, seems like a weird limitation to impose -Status AlbumOp::LoadFloatTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - TensorPtr float_tensor; - if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT64) { - double data = json_obj; - MS_LOG(INFO) << "double found: " << json_obj << "."; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, &float_tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT32) { - float data1 = json_obj; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data1, &float_tensor)); - MS_LOG(INFO) << "float found: " << json_obj << "."; - } - row->push_back(std::move(float_tensor)); - return Status::OK(); -} - -// Loads a tensor with int value, we have to cast the value to type specified in the schema. -Status AlbumOp::LoadIntTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row) { - TensorPtr int_tensor; - if (data_schema_->Column(col_num).Type() == DataType::DE_INT64) { - int64_t data = json_obj; - MS_LOG(INFO) << "int64 found: " << json_obj << "."; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, &int_tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_INT32) { - int32_t data = json_obj; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, &int_tensor)); - MS_LOG(INFO) << "int32 found: " << json_obj << "."; - } - row->push_back(std::move(int_tensor)); - return Status::OK(); -} - -// Load 1 TensorRow (image,label) using 1 ImageColumns. 1 function call produces 1 TensorRow -// possible optimization: the helper functions of LoadTensorRow should be optimized -// to take a reference to a column descriptor? -// the design of this class is to make the code more readable, forgoing minor performance gain like -// getting rid of duplicated checks -Status AlbumOp::LoadTensorRow(row_id_type row_id, TensorRow *row) { - std::string file = image_rows_[row_id]; - // testing here is to just print out file path - (*row) = TensorRow(row_id, {}); - MS_LOG(INFO) << "Image row file: " << file << "."; - - std::ifstream file_handle(folder_path_ + file, std::ios::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid json file, " + folder_path_ + file + " does not exist or permission denied."); - } - std::string line; - while (getline(file_handle, line)) { - try { - nlohmann::json js = nlohmann::json::parse(line); - MS_LOG(INFO) << "This Line: " << line << "."; - - // note if take a schema here, then we have to iterate over all column descriptors in schema and check for key - // get columns in schema: - int32_t columns = data_schema_->NumColumns(); - - // loop over each column descriptor, this can optimized by switch cases - for (int32_t i = 0; i < columns; i++) { - auto s = loadColumnData(file, i, js, row); - if (s != Status::OK()) { - file_handle.close(); - return s; - } - } - } catch (const std::exception &err) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, " + folder_path_ + file + " load failed: " + std::string(err.what())); - } - } - file_handle.close(); - std::vector path(row->size(), folder_path_ + file); - row->setPath(path); - return Status::OK(); -} - -Status AlbumOp::loadColumnData(const std::string &file, int32_t index, nlohmann::json js, TensorRow *row) { - int32_t i = index; - // special case to handle - if (data_schema_->Column(i).Name() == "id") { - // id is internal, special case to load from file - return LoadIDTensor(file, i, row); - } - // find if key does not exist, insert placeholder nullptr if not found - if (js.find(data_schema_->Column(i).Name()) == js.end()) { - // iterator not found, push nullptr as placeholder - MS_LOG(INFO) << "Pushing empty tensor for column: " << data_schema_->Column(i).Name() << "."; - return LoadEmptyTensor(i, row); - } - nlohmann::json column_value = js.at(data_schema_->Column(i).Name()); - MS_LOG(INFO) << "This column is: " << data_schema_->Column(i).Name() << "."; - bool is_array = column_value.is_array(); - // load single string - if (column_value.is_string() && data_schema_->Column(i).Type() == DataType::DE_STRING) { - return LoadStringTensor(column_value, i, row); - } - // load string array - if (is_array && data_schema_->Column(i).Type() == DataType::DE_STRING) { - return LoadStringArrayTensor(column_value, i, row); - } - // load image file - if (column_value.is_string() && data_schema_->Column(i).Type() != DataType::DE_STRING) { - std::string image_file_path = column_value; - return LoadImageTensor(image_file_path, i, row); - } - // load float value - bool judge_float = (data_schema_->Column(i).Type() == DataType::DE_FLOAT32) || - (data_schema_->Column(i).Type() == DataType::DE_FLOAT64); - if (!is_array && judge_float) { - return LoadFloatTensor(column_value, i, row); - } - // load float array - if (is_array && judge_float) { - return LoadFloatArrayTensor(column_value, i, row); - } - // int value - bool judge_int = - (data_schema_->Column(i).Type() == DataType::DE_INT64) || (data_schema_->Column(i).Type() == DataType::DE_INT32); - if (!is_array && judge_int) { - return LoadIntTensor(column_value, i, row); - } - // int array - if (is_array && judge_int) { - return LoadIntArrayTensor(column_value, i, row); - } else { - MS_LOG(WARNING) << "Value type for column: " << data_schema_->Column(i).Name() << " is not supported."; - return Status::OK(); - } -} - -void AlbumOp::Print(std::ostream &out, bool show_all) const { - constexpr int64_t field_width = 2; - // Always show the id and name as first line regardless if this summary or detailed print - out << "(" << std::setw(field_width) << operator_id_ << ") :"; - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nAlbum directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status AlbumOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(INFO) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h deleted file mode 100644 index aff110819..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_ALBUM_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_ALBUM_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -// Define row information as a list of file objects to read -using FolderImages = std::shared_ptr>>; - -/// \class AlbumOp album_op.h -class AlbumOp : public MappableLeafOp { - public: - /// \brief Constructor - /// \param[in] num_wkrs - Num of workers reading images in parallel - /// \param[in] file_dir - directory of Album - /// \param[in] queue_size - connector size - /// \param[in] do_decode - decode image files - /// \param[in] exts - set of file extensions to read, if empty, read everything under the dir - /// \param[in] data_schema - schema of dataset - /// \param[in] sampler - sampler tells AlbumOp what to read - AlbumOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool do_decode, const std::set &exts, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~AlbumOp() = default; - - /// \brief A print method typically used for debugging - /// \param[in] out - /// \param[in] show_all - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Check if image ia valid.Only support JPEG/PNG/GIF/BMP - /// This function could be optimized to return the tensor to reduce open/closing files - /// \return bool - if file is bad then return false - bool CheckImageType(const std::string &file_name, bool *valid); - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "AlbumOp"; } - - protected: - /// \brief Initialize AlbumOp related var, calls the function to walk all files - /// \return Status The status code returned - Status PrepareData() override; - - private: - /// \brief Load image to tensor row - /// \param[in] image_file Image name of file - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadImageTensor(const std::string &image_file, int32_t col_num, TensorRow *row); - - /// \brief Load vector of ints to tensor, append tensor to tensor row - /// \param[in] json_obj Json object containing multi-dimensional label - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadIntArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load vector of floatss to tensor, append tensor to tensor row - /// \param[in] json_obj Json object containing array data - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadFloatArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load string array into a tensor, append tensor to tensor row - /// \param[in] json_obj Json object containing string tensor - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadStringArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load string into a tensor, append tensor to tensor row - /// \param[in] json_obj Json object containing string tensor - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadStringTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load float value to tensor row - /// \param[in] json_obj Json object containing float - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadFloatTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load int value to tensor row - /// \param[in] json_obj Json object containing int - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadIntTensor(const nlohmann::json &json_obj, int32_t col_num, TensorRow *row); - - /// \brief Load empty tensor to tensor row - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadEmptyTensor(int32_t col_num, TensorRow *row); - - /// \brief Load id from file name to tensor row - /// \param[in] file The file name to get ID from - /// \param[in] col_num Column num in schema - /// \param[in, out] row Tensor row to push to - /// \return Status The status code returned - Status LoadIDTensor(const std::string &file, int32_t col_num, TensorRow *row); - - /// \brief Load a tensor row according to a json file - /// \param[in] row_id_type row_id - id for this tensor row - /// \param[in, out] TensorRow row Json content stored into a tensor row - /// \return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Load a tensor column according to a json file - /// \param[in] ImageColumns file Json file location - /// \param[in] index - certain column index - /// \param[in] js - json object - /// \param[in, out] TensorRow row Json content stored into a tensor row - /// \return Status The status code returned - Status loadColumnData(const std::string &file, int32_t index, nlohmann::json js, TensorRow *row); - - /// Private function for computing the assignment of the column name map. - /// \return Status The status code returned - Status ComputeColMap() override; - - std::string folder_path_; // directory of image folder - bool decode_; - std::set extensions_; // extensions allowed - std::unordered_map col_name_map_; - std::unique_ptr data_schema_; - int64_t sampler_ind_; - int64_t dirname_offset_; - std::vector image_rows_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_ALBUM_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.cc deleted file mode 100755 index b18b325f4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.cc +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h" - -#include - -namespace mindspore { -namespace dataset { -AmazonReviewOp::AmazonReviewOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id, - char field_delim, const std::vector> &column_default, - const std::vector &column_name, - const std::vector &amazon_review_files_list) - : CsvOp(amazon_review_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id) {} - -void AmazonReviewOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nAmazonReview files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h deleted file mode 100755 index 10f3a6849..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AMAZON_REVIEW_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AMAZON_REVIEW_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -/// \class AmazonReviewOp -/// \brief A Op derived class to represent AmazonReview Op. -class AmazonReviewOp : public CsvOp { - public: - /// \brief Constructor of AmazonReviewOp. - /// \param[in] num_workers Number of worker threads reading data from amazon_review files. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. - /// \param[in] field_delim A char that indicates the delimiter to separate fields. - /// \param[in] column_default List of default values for the CSV field. Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name List of column names of the dataset. - /// \param[in] amazon_review_files_list List of file paths for the dataset files. - AmazonReviewOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, const std::vector &amazon_review_files_list); - - /// \brief Destructor. - ~AmazonReviewOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "AmazonReview" : "amazon review"; } - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "AmazonReviewOp"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_AMAZON_REVIEW_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.cc deleted file mode 100755 index 92223d4cd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.cc +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. -*/ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h" - -#include -#include -#include -#include - -namespace mindspore { -namespace dataset { -const std::set kExts = {".jpg", ".JPEG"}; -const std::map kClassIndex = {}; -CaltechOp::CaltechOp(int32_t num_workers, const std::string &file_dir, int32_t queue_size, bool do_decode, - std::unique_ptr data_schema, std::shared_ptr sampler) - : ImageFolderOp(num_workers, file_dir, queue_size, false, do_decode, kExts, kClassIndex, std::move(data_schema), - std::move(sampler)) {} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h deleted file mode 100755 index 6970b761d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CALTECH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CALTECH_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -/// \brief Read Caltech256 Dataset. -class CaltechOp : public ImageFolderOp { - public: - /// \brief Constructor. - /// \param[in] num_workers Num of workers reading images in parallel. - /// \param[in] file_dir Directory of caltech dataset. - /// \param[in] queue_size Connector queue size. - /// \param[in] do_decode Whether to decode the raw data. - /// \param[in] data_schema Data schema of caltech256 dataset. - /// \param[in] sampler Sampler tells CaltechOp what to read. - CaltechOp(int32_t num_workers, const std::string &file_dir, int32_t queue_size, bool do_decode, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~CaltechOp() = default; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "CaltechOp"; } - - /// \brief DatasetName name getter. - /// \param[in] upper Whether the returned name begins with uppercase. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "Caltech" : "caltech"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CALTECH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.cc deleted file mode 100644 index c7161d3f9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.cc +++ /dev/null @@ -1,337 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. -*/ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h" - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "utils/file_utils.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif - -namespace mindspore { -namespace dataset { -#ifdef ENABLE_PYTHON -CelebAOp::CelebAOp(int32_t num_workers, const std::string &dir, int32_t queue_size, bool decode, - const std::string &usage, const std::set &exts, std::unique_ptr schema, - std::shared_ptr sampler, py::function decrypt) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(dir), - decode_(decode), - extensions_(exts), - data_schema_(std::move(schema)), - num_rows_in_attr_file_(0), - attr_file_(""), - usage_(usage), - decrypt_(std::move(decrypt)) { - attr_info_queue_ = std::make_unique>>(queue_size); -} -#else -CelebAOp::CelebAOp(int32_t num_workers, const std::string &dir, int32_t queue_size, bool decode, - const std::string &usage, const std::set &exts, std::unique_ptr schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(dir), - decode_(decode), - extensions_(exts), - data_schema_(std::move(schema)), - num_rows_in_attr_file_(0), - attr_file_(""), - usage_(usage) { - attr_info_queue_ = std::make_unique>>(queue_size); -} -#endif - -Status CelebAOp::RegisterAndLaunchThreads() { - RETURN_IF_NOT_OK(ParallelOp::RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(attr_info_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK( - tree_->AllTasks()->CreateAsyncTask("Walking attr file", std::bind(&CelebAOp::ParseAttrFile, this), nullptr, id())); - return Status::OK(); -} - -Status CelebAOp::ParseAttrFile() { - TaskManager::FindMe()->Post(); - Path folder_path(folder_path_); - - auto realpath = FileUtils::GetRealPath((folder_path / "list_attr_celeba.txt").ToString().c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << (folder_path / "list_attr_celeba.txt").ToString() << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + (folder_path / "list_attr_celeba.txt").ToString() + - " does not exist."); - } - - std::ifstream attr_file(realpath.value(), std::ios::in); - if (!attr_file.is_open()) { - std::string attr_file_name = (folder_path / "list_attr_celeba.txt").ToString(); - RETURN_STATUS_ERROR(StatusCode::kMDFileNotExist, - "Invalid attr file, failed to open: " + attr_file_name + ", permission denied."); - } - - attr_file_ = (folder_path / "list_attr_celeba.txt").ToString(); - const auto PushBackToQueue = [this](std::vector &vec, std::ifstream &attr_file, - std::ifstream &partition_file) { - Status s = attr_info_queue_->EmplaceBack(vec); - if (s.IsError()) { - CLOSE_FILE(attr_file, partition_file); - return s; - } - return Status::OK(); - }; - - std::string rows_num; - std::string attr_name; - (void)getline(attr_file, rows_num); - try { - num_rows_in_attr_file_ = static_cast(std::stoul(rows_num)); // First line is rows number in attr file - } catch (std::invalid_argument &e) { - CLOSE_FILE(attr_file, partition_file_); - RETURN_STATUS_UNEXPECTED("Invalid rows_num, failed to convert rows_num: " + rows_num + " to unsigned long in " + - attr_file_ + "."); - } catch (std::out_of_range &e) { - CLOSE_FILE(attr_file, partition_file_); - RETURN_STATUS_UNEXPECTED("Invalid rows_num, rows_num in " + attr_file_ + " is out of range, rows_num is " + - rows_num + "."); - } - - (void)getline(attr_file, attr_name); // Second line is attribute name,ignore it - std::string image_info; - std::vector image_infos; - image_infos.reserve(oc_queue_size_); - while (getline(attr_file, image_info)) { - if ((image_info.empty()) || (usage_ != "all" && !CheckDatasetTypeValid())) { - continue; - } - image_infos.push_back(image_info); - if (image_info.size() % oc_queue_size_ == 0) { - RETURN_IF_NOT_OK(PushBackToQueue(image_infos, attr_file, partition_file_)); - image_infos.clear(); - } - } - if (!image_infos.empty()) { - RETURN_IF_NOT_OK(PushBackToQueue(image_infos, attr_file, partition_file_)); - } - std::vector end_indicator = std::vector(0); - RETURN_IF_NOT_OK(PushBackToQueue(end_indicator, attr_file, partition_file_)); // end indicator - CLOSE_FILE(attr_file, partition_file_); - return Status::OK(); -} - -bool CelebAOp::CheckDatasetTypeValid() { - if (!partition_file_.is_open()) { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << folder_path_ << " does not exist."; - return false; - } - Path folder_path(realpath.str()); - partition_file_.open((folder_path / "list_eval_partition.txt").ToString()); - if (!partition_file_.is_open()) { - MS_LOG(ERROR) << "Invalid eval partition file, failed to open eval partition file: " - << (folder_path / "list_eval_partition.txt").ToString() << " does not exist or permission denied."; - return false; - } - } - std::string line; - (void)getline(partition_file_, line); - std::vector vec = Split(line); - if (vec.size() != 2) { - return false; - } - int32_t type; - try { - type = std::stoi(vec[1]); - } catch (std::invalid_argument &e) { - MS_LOG(WARNING) << "Invalid number, the second word in list_eval_partition.txt should be numeric, but got: " - << vec[1] << "."; - return false; - } catch (std::out_of_range &e) { - MS_LOG(WARNING) << "Invalid number, the second word in list_eval_partition.txt is out of range, word is: " << vec[1] - << "."; - return false; - } - // train:0, valid=1, test=2 - constexpr int32_t train_type = 0; - constexpr int32_t valid_type = 1; - constexpr int32_t test_type = 2; - - if (usage_ == "train" && (type == train_type)) { - return true; - } else if (usage_ == "valid" && (type == valid_type)) { - return true; - } else if (usage_ == "test" && (type == test_type)) { - return true; - } - - return false; -} - -Status CelebAOp::PrepareData() { - std::vector image_infos; - bool need_more_data = true; - RETURN_IF_NOT_OK(attr_info_queue_->PopFront(&image_infos)); - while (!image_infos.empty() && need_more_data) { - for (uint32_t index = 0; index < image_infos.size(); index++) { - std::string image_info = image_infos[index]; - std::vector split = Split(image_info); - std::pair> image_labels; - - Path path(folder_path_); - Path file_path = path / split[0]; - if (!extensions_.empty() && extensions_.find(file_path.Extension()) == extensions_.end()) { - MS_LOG(WARNING) << "Unsupported file found at " << file_path.ToString().c_str() << ", its extension is " - << file_path.Extension().c_str() << "."; - continue; - } - image_labels.first = split[0]; - for (uint32_t label_index = 1; label_index < split.size(); label_index++) { - int32_t value; - try { - value = std::stoi(split[label_index]); - } catch (std::invalid_argument &e) { - RETURN_STATUS_UNEXPECTED("Invalid label index, the label index in " + file_path.ToString() + - " should be numeric, but got: " + split[label_index] + "."); - } catch (std::out_of_range &e) { - RETURN_STATUS_UNEXPECTED("Invalid label index, the label index in " + file_path.ToString() + - " is out of range, index is " + split[label_index] + "."); - } - image_labels.second.push_back(value); - } - - image_labels_vec_.push_back(image_labels); - } - - RETURN_IF_NOT_OK(attr_info_queue_->PopFront(&image_infos)); - } - - num_rows_ = image_labels_vec_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, CelebADataset API can't read the data file (interface mismatch or no data found). " - "Check file path: " + - folder_path_); - } - MS_LOG(DEBUG) << "Celeba dataset rows number is " << num_rows_ << "."; - return Status::OK(); -} - -std::vector CelebAOp::Split(const std::string &line) { - std::string str = line; - std::string::size_type pos; - std::vector split; - str += " "; - int size = str.size(); - for (uint32_t index = 0; index < size;) { - pos = str.find(" ", index); - if (pos != index) { // skip space - std::string s = str.substr(index, pos - index); - split.push_back(s); - } - index = pos + 1; - } - - return split; -} - -Status CelebAOp::LoadTensorRow(row_id_type row_id, TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - std::pair> &image_label = image_labels_vec_[row_id]; - std::shared_ptr image; - std::shared_ptr label; - - Path path(folder_path_); - Path image_path = path / image_label.first; - -#ifdef ENABLE_PYTHON - RETURN_IF_NOT_OK(MappableLeafOp::ImageDecrypt(image_path.ToString(), &image, decrypt_)); -#else - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_path.ToString(), &image)); -#endif - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - image = nullptr; - std::string err_msg = - "Invalid image, " + image_path.ToString() + " decode failed, the image is broken or permission denied."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - RETURN_IF_NOT_OK( - Tensor::CreateEmpty(TensorShape({1, (uint32_t)image_label.second.size()}), data_schema_->Column(1).Type(), &label)); - RETURN_IF_NOT_OK(label->Zero()); - for (uint32_t index = 0; index < image_label.second.size(); index++) { - if (image_label.second[index] == 1) { - RETURN_IF_NOT_OK(label->SetItemAt({0, static_cast(index)}, 1)); - } else { - RETURN_IF_NOT_OK(label->SetItemAt({0, static_cast(index)}, 0)); - } - } - label->Squeeze(); - - (*row) = TensorRow(row_id, {std::move(image), std::move(label)}); - // Add file path info - row->setPath({image_path.ToString(), attr_file_}); - return Status::OK(); -} - -void CelebAOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nceleba dir: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status CelebAOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t index = 0; index < data_schema_->NumColumns(); index++) { - column_name_id_map_[data_schema_->Column(index).Name()] = index; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status CelebAOp::InitPullMode() { - if (!image_labels_vec_.empty()) { - return Status::OK(); - } - if (attr_info_queue_->empty()) { - RETURN_IF_NOT_OK(ParseAttrFile()); - } - return PrepareData(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h deleted file mode 100644 index 829568c29..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. -*/ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CELEBA_OP_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CELEBA_OP_H - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" - -#define CLOSE_FILE(attr_file, partition_file) \ - do { \ - (attr_file).close(); \ - if ((partition_file).is_open()) { \ - (partition_file).close(); \ - } \ - } while (false) - -namespace mindspore { -namespace dataset { -class CelebAOp : public MappableLeafOp { - public: -#ifdef ENABLE_PYTHON - // Constructor - // @param int32_t - num_workers - Num of workers reading images in parallel - // @param std::string - dir directory of celeba dataset - // @param int32_t queueSize - connector queue size - // @param bool decode - decode the images after reading - // @param std::string usage - specify the train, valid, test part or all parts of dataset - // @param std::set exts - list of file extensions to be included in the dataset - // @param std::unique_ptr schema - path to the JSON schema file or schema object - // @param std::unique_ptr sampler - sampler tells CelebAOp what to read - // @param py::function decrypt - Image decryption function, which accepts the path of the encrypted image file - // and returns the decrypted bytes data. Default: None, no decryption. - CelebAOp(int32_t num_workers, const std::string &dir, int32_t queue_size, bool decode, const std::string &usage, - const std::set &exts, std::unique_ptr schema, std::shared_ptr sampler, - py::function decrypt = py::none()); -#else - // Constructor - // @param int32_t - num_workers - Num of workers reading images in parallel - // @param std::string - dir directory of celeba dataset - // @param int32_t queueSize - connector queue size - // @param bool decode - decode the images after reading - // @param std::string usage - specify the train, valid, test part or all parts of dataset - // @param std::set exts - list of file extensions to be included in the dataset - // @param std::unique_ptr schema - path to the JSON schema file or schema object - // @param std::unique_ptr sampler - sampler tells CelebAOp what to read - CelebAOp(int32_t num_workers, const std::string &dir, int32_t queue_size, bool decode, const std::string &usage, - const std::set &exts, std::unique_ptr schema, std::shared_ptr sampler); -#endif - - ~CelebAOp() override = default; - - // A print method typically used for debugging - // @param out - // @param show_all - void Print(std::ostream &out, bool show_all) const override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "CelebAOp"; } - - protected: - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - Status InitPullMode() override; - - private: - // Called first when function is called - // @return - // Called first when function is called - // @return - Status RegisterAndLaunchThreads() override; - - /// Parse attribute file - /// @return - Status ParseAttrFile(); - - /// Parse each image line in attribute file - /// @return - Status PrepareData() override; - - /// Split attribute info with space - /// @param std::string - line - Line from att or partition file - /// @return std::vector - string after split - std::vector Split(const std::string &line); - - // Load a tensor row according to a pair - // @param row_id_type row_id - id for this tensor row - // @param std::pair - > - // @param TensorRow row - image & label read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// Check if need read according to dataset type - /// @return bool - if need read - bool CheckDatasetTypeValid(); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - std::string folder_path_; // directory of celeba folder - bool decode_; - std::set extensions_; /// extensions allowed - std::unique_ptr data_schema_; - std::unique_ptr>> attr_info_queue_; - int64_t num_rows_in_attr_file_; /// rows number specified in attr file - std::vector>> image_labels_vec_; - std::string usage_; - std::ifstream partition_file_; - std::string attr_file_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CELEBA_OP_H diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.cc deleted file mode 100644 index 02e4617e3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.cc +++ /dev/null @@ -1,405 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { - -constexpr uint32_t kCifarImageHeight = 32; -constexpr uint32_t kCifarImageWidth = 32; -constexpr uint32_t kCifarImageChannel = 3; -constexpr uint32_t kCifarBlockImageNum = 5; -constexpr uint32_t kCifarImageSize = kCifarImageHeight * kCifarImageWidth * kCifarImageChannel; -CifarOp::CifarOp(CifarType type, const std::string &usage, int32_t num_works, const std::string &file_dir, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_works, queue_size, std::move(sampler)), - cifar_type_(type), - usage_(usage), - folder_path_(file_dir), - data_schema_(std::move(data_schema)) { - constexpr uint64_t kUtilQueueSize = 512; - cifar_raw_data_block_ = std::make_unique>>(kUtilQueueSize); -} - -Status CifarOp::RegisterAndLaunchThreads() { - RETURN_IF_NOT_OK(ParallelOp::RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(cifar_raw_data_block_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "Get cifar data block", std::bind(&CifarOp::ReadCifarBlockDataAsync, this), nullptr, id())); - return Status::OK(); -} - -// Load 1 TensorRow (image,label). 1 function call produces 1 TensorTow -Status CifarOp::LoadTensorRow(row_id_type index, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr label; - std::shared_ptr fine_label; - std::shared_ptr ori_image = cifar_image_label_pairs_[index].first; - std::shared_ptr copy_image; - uint64_t path_index = static_cast(std::ceil(index / kCifarBlockImageNum)); - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(ori_image, ©_image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(cifar_image_label_pairs_[index].second[0], &label)); - - if (cifar_image_label_pairs_[index].second.size() > 1) { - RETURN_IF_NOT_OK(Tensor::CreateScalar(cifar_image_label_pairs_[index].second[1], &fine_label)); - (*trow) = TensorRow(index, {copy_image, std::move(label), std::move(fine_label)}); - // Add file path info - trow->setPath({path_record_[path_index], path_record_[path_index], path_record_[path_index]}); - } else { - (*trow) = TensorRow(index, {copy_image, std::move(label)}); - // Add file path info - trow->setPath({path_record_[path_index], path_record_[path_index]}); - } - - return Status::OK(); -} - -void CifarOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nCifar directory: " << folder_path_ << "\n\n"; - } -} - -Status CifarOp::ReadCifarBlockDataAsync() { - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(GetCifarFiles()); - if (cifar_type_ == kCifar10) { - RETURN_IF_NOT_OK(ReadCifar10BlockData()); - } else { - RETURN_IF_NOT_OK(ReadCifar100BlockData()); - } - - return Status::OK(); -} - -Status CifarOp::ReadCifar10BlockData() { - // CIFAR 10 has 6 bin files. data_batch_1.bin ... data_batch_5.bin and 1 test_batch.bin file - // each of the file has exactly 10K images and labels and size is 30,730 KB - // each image has the dimension of 32 x 32 x 3 = 3072 plus 1 label (label has 10 classes) so each row has 3073 bytes - constexpr uint32_t num_cifar10_records = 10000; - uint32_t block_size = (kCifarImageSize + 1) * kCifarBlockImageNum; // about 2M - std::vector image_data(block_size * sizeof(unsigned char), 0); - for (auto &file : cifar_files_) { - // check the validity of the file path - Path file_path(file); - CHECK_FAIL_RETURN_UNEXPECTED(file_path.Exists() && !file_path.IsDirectory(), - "Invalid cifar10 file, " + file + " does not exist or is a directory."); - std::string file_name = file_path.Basename(); - - if (usage_ == "train") { - if (file_name.find("data_batch") == std::string::npos) { - continue; - } - } else if (usage_ == "test") { - if (file_name.find("test_batch") == std::string::npos) { - continue; - } - } else { // get all the files that contain the word batch, aka any cifar 100 files - if (file_name.find("batch") == std::string::npos) { - continue; - } - } - - std::ifstream in(file, std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED( - in.is_open(), "Invalid cifar10 file, failed to open " + file + ", the file is damaged or permission denied."); - - for (uint32_t index = 0; index < num_cifar10_records / kCifarBlockImageNum; ++index) { - (void)in.read(reinterpret_cast(&(image_data[0])), block_size * sizeof(unsigned char)); - if (in.fail()) { - in.close(); - RETURN_STATUS_UNEXPECTED("Invalid cifar10 file, failed to read data from: " + file + - ", re-download dataset(make sure it is CIFAR-10 binary version)."); - } - RETURN_IF_NOT_OK(cifar_raw_data_block_->EmplaceBack(image_data)); - // Add file path info - path_record_.push_back(file); - } - in.close(); - } - RETURN_IF_NOT_OK(cifar_raw_data_block_->EmplaceBack(std::vector())); // end block - - return Status::OK(); -} - -Status CifarOp::ReadCifar100BlockData() { - // CIFAR 100 has 2 bin files. train.bin (60K imgs) 153,700KB and test.bin (30,740KB) (10K imgs) - // each img has two labels. Each row then is 32 * 32 *5 + 2 = 3,074 Bytes - uint32_t num_cifar100_records = 0; // test:10000, train:50000 - constexpr uint32_t num_cifar100_test_records = 10000; - constexpr uint32_t num_cifar100_train_records = 50000; - uint32_t block_size = (kCifarImageSize + 2) * kCifarBlockImageNum; // about 2M - std::vector image_data(block_size * sizeof(unsigned char), 0); - for (auto &file : cifar_files_) { - // check the validity of the file path - Path file_path(file); - CHECK_FAIL_RETURN_UNEXPECTED(file_path.Exists() && !file_path.IsDirectory(), - "Invalid cifar100 file, " + file + " does not exist or is a directory."); - std::string file_name = file_path.Basename(); - - // if usage is train/test, get only these 2 files - if (usage_ == "train" && file_name.find("train") == std::string::npos) { - continue; - } - if (usage_ == "test" && file_name.find("test") == std::string::npos) { - continue; - } - - if (file_name.find("test") != std::string::npos) { - num_cifar100_records = num_cifar100_test_records; - } else if (file_name.find("train") != std::string::npos) { - num_cifar100_records = num_cifar100_train_records; - } else { - RETURN_STATUS_UNEXPECTED("Invalid cifar100 file, Cifar100 train/test file is missing in: " + file_name); - } - - std::ifstream in(file, std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED( - in.is_open(), "Invalid cifar100 file, failed to open " + file + ", the file is damaged or permission denied."); - - for (uint32_t index = 0; index < num_cifar100_records / kCifarBlockImageNum; index++) { - (void)in.read(reinterpret_cast(&(image_data[0])), block_size * sizeof(unsigned char)); - if (in.fail()) { - in.close(); - RETURN_STATUS_UNEXPECTED("Invalid cifar100 file, failed to read data from: " + file + - ", re-download dataset(make sure it is CIFAR-100 binary version)."); - } - RETURN_IF_NOT_OK(cifar_raw_data_block_->EmplaceBack(image_data)); - // Add file path info - path_record_.push_back(file); - } - in.close(); - } - RETURN_IF_NOT_OK(cifar_raw_data_block_->EmplaceBack(std::vector())); // block end - return Status::OK(); -} - -Status CifarOp::GetCifarFiles() { - const std::string extension = ".bin"; - Path dir_path(folder_path_); - auto dirIt = Path::DirIterator::OpenDirectory(&dir_path); - if (dirIt) { - while (dirIt->HasNext()) { - Path file = dirIt->Next(); - if (file.Extension() == extension) { - cifar_files_.push_back(file.ToString()); - } - } - } else { - RETURN_STATUS_UNEXPECTED("Invalid directory, " + dir_path.ToString() + " is not a directory or permission denied."); - } - CHECK_FAIL_RETURN_UNEXPECTED(!cifar_files_.empty(), - "Invalid cifar folder, cifar(.bin) files are missing under " + folder_path_); - std::sort(cifar_files_.begin(), cifar_files_.end()); - return Status::OK(); -} - -Status CifarOp::PrepareData() { - std::vector block; - RETURN_IF_NOT_OK(cifar_raw_data_block_->PopFront(&block)); - uint32_t cur_block_index = 0; - while (!block.empty()) { - for (uint32_t index = 0; index < kCifarBlockImageNum; ++index) { - std::vector labels; - uint32_t label = block[cur_block_index++]; - labels.push_back(label); - if (cifar_type_ == kCifar100) { - uint32_t fine_label = block[cur_block_index++]; - labels.push_back(fine_label); - } - - std::shared_ptr image_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({kCifarImageHeight, kCifarImageWidth, kCifarImageChannel}), - data_schema_->Column(0).Type(), &image_tensor)); - auto itr = image_tensor->begin(); - uint32_t total_pix = kCifarImageHeight * kCifarImageWidth; - for (uint32_t pix = 0; pix < total_pix; ++pix) { - for (uint32_t ch = 0; ch < kCifarImageChannel; ++ch) { - *itr = block[cur_block_index + ch * total_pix + pix]; - ++itr; - } - } - cur_block_index += total_pix * kCifarImageChannel; - cifar_image_label_pairs_.emplace_back(std::make_pair(image_tensor, labels)); - } - RETURN_IF_NOT_OK(cifar_raw_data_block_->PopFront(&block)); - cur_block_index = 0; - } - cifar_image_label_pairs_.shrink_to_fit(); - num_rows_ = cifar_image_label_pairs_.size(); - if (num_rows_ == 0) { - std::string api = cifar_type_ == kCifar10 ? "Cifar10Dataset" : "Cifar100Dataset"; - RETURN_STATUS_UNEXPECTED("Invalid data, " + api + - " API can't read the data file (interface mismatch or no data found). " - "Check file in directory:" + - folder_path_); - } - cifar_raw_data_block_->Reset(); - return Status::OK(); -} - -// Derived from RandomAccessOp -Status CifarOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty()) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - - for (uint64_t index = 0; index < cifar_image_label_pairs_.size(); ++index) { - uint32_t label = (cifar_image_label_pairs_[index].second)[0]; - (*cls_ids)[label].push_back(index); - } - - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status CifarOp::CountTotalRows(const std::string &dir, const std::string &usage, bool isCIFAR10, int64_t *count) { - // the logic of counting the number of samples is copied from ReadCifar100Block() and ReadCifar10Block() - // Note that this count logic is flawed, should be able to copy the sampler of original CifarOp without state - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - CifarType type = isCIFAR10 ? kCifar10 : kCifar100; - // build a new unique schema object - auto new_schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - if (type == kCifar10) { - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - } else { - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("coarse_label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - TensorShape another_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("fine_label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &another_scalar))); - } - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - std::shared_ptr op = std::make_shared(type, usage, num_workers, dir, op_connect_size, - std::move(new_schema), std::move(new_sampler)); - - RETURN_IF_NOT_OK(op->GetCifarFiles()); - if (op->cifar_type_ == kCifar10) { - constexpr int64_t num_cifar10_records = 10000; - for (auto &file : op->cifar_files_) { - Path file_path(file); - CHECK_FAIL_RETURN_UNEXPECTED(file_path.Exists() && !file_path.IsDirectory(), - "Invalid cifar10 file, " + file + " does not exist or is a directory."); - std::string file_name = file_path.Basename(); - - if (op->usage_ == "train") { - if (file_name.find("data_batch") == std::string::npos) { - continue; - } - } else if (op->usage_ == "test") { - if (file_name.find("test_batch") == std::string::npos) { - continue; - } - } else { // get all the files that contain the word batch, aka any cifar 100 files - if (file_name.find("batch") == std::string::npos) { - continue; - } - } - - std::ifstream in(file, std::ios::in | std::ios::binary); - - CHECK_FAIL_RETURN_UNEXPECTED( - in.is_open(), "Invalid cifar10 file, failed to open " + file + ", the file is damaged or permission denied."); - *count = *count + num_cifar10_records; - in.close(); - } - return Status::OK(); - } else { - const uint32_t kCifar100RecordsPerTestFile = 10000; - const uint32_t kCifar100RecordsPerTrainFile = 50000; - int64_t num_cifar100_records = 0; - for (auto &file : op->cifar_files_) { - Path file_path(file); - std::string file_name = file_path.Basename(); - - CHECK_FAIL_RETURN_UNEXPECTED(file_path.Exists() && !file_path.IsDirectory(), - "Invalid cifar100 file, " + file + " does not exist or is a directory."); - - if (op->usage_ == "train" && file_path.Basename().find("train") == std::string::npos) { - continue; - } - if (op->usage_ == "test" && file_path.Basename().find("test") == std::string::npos) { - continue; - } - - if (file_name.find("test") != std::string::npos) { - num_cifar100_records += kCifar100RecordsPerTestFile; - } else if (file_name.find("train") != std::string::npos) { - num_cifar100_records += kCifar100RecordsPerTrainFile; - } - std::ifstream in(file, std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED( - in.is_open(), "Invalid cifar100 file, failed to open " + file + ", the file is damaged or permission denied."); - in.close(); - } - *count = num_cifar100_records; - return Status::OK(); - } -} - -Status CifarOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status CifarOp::InitPullMode() { - RETURN_IF_NOT_OK(cifar_raw_data_block_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - "Get cifar data block", std::bind(&CifarOp::ReadCifarBlockDataAsync, this), nullptr, id())); - return PrepareData(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h deleted file mode 100644 index 13b1bcf58..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CIFAR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CIFAR_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class CifarOp : public MappableLeafOp { - public: - enum CifarType { kCifar10, kCifar100 }; - - // Constructor - // @param CifarType type - Cifar10 or Cifar100 - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all' - // @param uint32_t numWorks - Num of workers reading images in parallel - // @param std::string - dir directory of cifar dataset - // @param uint32_t - queueSize - connector queue size - // @param std::unique_ptr sampler - sampler tells ImageFolderOp what to read - CifarOp(CifarType type, const std::string &usage, int32_t num_works, const std::string &file_dir, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - // Destructor. - ~CifarOp() = default; - - // A print method typically used for debugging - // @param out - // @param show_all - void Print(std::ostream &out, bool show_all) const override; - - /// Function to count the number of samples in the CIFAR dataset - /// @param dir path to the CIFAR directory - /// @param isCIFAR10 true if CIFAR10 and false if CIFAR100 - /// @param count output arg that will hold the actual dataset size - /// @return - static Status CountTotalRows(const std::string &dir, const std::string &usage, bool isCIFAR10, int64_t *count); - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "CifarOp"; } - - protected: - // Called first when function is called - // @return - Status RegisterAndLaunchThreads() override; - - // Parse cifar data - // @return - Status PrepareData() override; - - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - Status InitPullMode() override; - - private: - // Load a tensor row according to a pair - // @param uint64_t index - index need to load - // @param TensorRow row - image & label read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - // Read block data from cifar file - // @return - Status ReadCifarBlockDataAsync(); - - /// Get cifar files in dir - /// @return - Status GetCifarFiles(); - - /// Read cifar10 data as block - /// @return - Status ReadCifar10BlockData(); - - /// Read cifar100 data as block - /// @return - Status ReadCifar100BlockData(); - - /// Method derived from RandomAccess Op, enable Sampler to get all ids for each class - /// @param (std::map> *cls_ids - val all ids for this class - /// @return Status The status code returned - Status GetClassIds(std::map> *cls_ids) const override; - - /// Private function for computing the assignment of the column name map. - /// @return - Status - Status ComputeColMap() override; - - CifarType cifar_type_; - std::string folder_path_; - std::unique_ptr data_schema_; - - const std::string usage_; // can only be either "train" or "test" - std::unique_ptr>> cifar_raw_data_block_; - std::vector cifar_files_; - std::vector path_record_; - std::vector, std::vector>> cifar_image_label_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif /// MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CIFAR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.cc deleted file mode 100644 index 37c083fe1..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.cc +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h" - -#include -#include -#include -#include -#include - -#include "utils/file_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr char taskSuffix[] = "polygon"; - -CityscapesOp::CityscapesOp(int32_t num_workers, const std::string &dataset_dir, const std::string &usage, - const std::string &quality_mode, const std::string &task, bool decode, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - usage_(usage), - quality_mode_(quality_mode), - task_(task), - decode_(decode), - data_schema_(std::move(data_schema)) {} - -// Load 1 TensorRow (image, task) using 1 ImageLabelPair. 1 function call produces 1 TensorTow -Status CityscapesOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair data = image_task_pairs_[static_cast(row_id)]; - std::shared_ptr image; - std::shared_ptr task; - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.first, &image)); - - if (task_ != taskSuffix) { - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.second, &task)); - } else { - std::ifstream file_handle(data.second, std::ifstream::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + data.second + - ", the json is damaged or permission denied."); - } - std::string contents((std::istreambuf_iterator(file_handle)), std::istreambuf_iterator()); - nlohmann::json contents_js = nlohmann::json::parse(contents); - Status rc = Tensor::CreateScalar(contents_js.dump(), &task); - if (rc.IsError()) { - file_handle.close(); - return rc; - } - file_handle.close(); - } - - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = - "Invalid image, failed to decode " + data.first + ", the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - if (task_ != taskSuffix) { - Status rc_t = Decode(task, &task); - if (rc_t.IsError()) { - std::string err_t = - "Invalid image, failed to decode " + data.second + ", the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err_t); - } - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(task)}); - trow->setPath({data.first, data.second}); - return Status::OK(); -} - -void CityscapesOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nCityscapes DatasetDir: " << dataset_dir_ << "\nTask: " << task_ - << "\nQualityMode: " << quality_mode_ << "\nUsage: " << usage_ << "\nDecode: " << (decode_ ? "yes" : "no") - << "\n\n"; - } -} - -Status CityscapesOp::PrepareData() { - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_dataset_dir.has_value()) { - MS_LOG(ERROR) << "Invalid file path, Cityscapes Dataset dir: " << dataset_dir_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, Cityscapes Dataset dir: " + dataset_dir_ + " does not exist."); - } - - Path dataset_dir(real_dataset_dir.value()); - std::string real_quality_mode = quality_mode_ == "fine" ? "gtFine" : "gtCoarse"; - if (usage_ == "all" && quality_mode_ == "fine") { - std::vector all_usage_fine = {"train", "test", "val"}; - for (auto item : all_usage_fine) { - std::string images_dir_fine = (dataset_dir / "leftImg8bit" / item).ToString(); - std::string task_dir_fine = (dataset_dir / real_quality_mode / item).ToString(); - RETURN_IF_NOT_OK(GetCityscapesDataByUsage(images_dir_fine, task_dir_fine, real_quality_mode)); - } - } else if (usage_ == "all" && quality_mode_ == "coarse") { - std::vector all_usage_coarse = {"train", "train_extra", "val"}; - for (auto item : all_usage_coarse) { - std::string images_dir_coarse = (dataset_dir / "leftImg8bit" / item).ToString(); - std::string task_dir_coarse = (dataset_dir / real_quality_mode / item).ToString(); - RETURN_IF_NOT_OK(GetCityscapesDataByUsage(images_dir_coarse, task_dir_coarse, real_quality_mode)); - } - } else { - std::string images_dir = (dataset_dir / "leftImg8bit" / usage_).ToString(); - std::string task_dir = (dataset_dir / real_quality_mode / usage_).ToString(); - RETURN_IF_NOT_OK(GetCityscapesDataByUsage(images_dir, task_dir, real_quality_mode)); - } - RETURN_IF_NOT_OK(CountDatasetInfo()); // Count the total rows - return Status::OK(); -} - -Status CityscapesOp::GetCityscapesDataByUsage(const std::string &images_dir, const std::string &task_dir, - const std::string &real_quality_mode) { - const std::string kExtension = ".png"; - std::string img_file_name; - std::map image_task_map_; - - Path images_dir_p(images_dir); - if (!images_dir_p.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid path, Cityscapes Dataset image dir: " + images_dir_p.ToString() + - " is not a directory path."); - } - Path task_dir_p(task_dir); - if (!task_dir_p.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid path, Cityscapes Dataset task dir: " + task_dir_p.ToString() + - " is not a directory path."); - } - std::shared_ptr d_it = Path::DirIterator::OpenDirectory(&images_dir_p); - if (d_it == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to open Cityscapes Dataset image directory: " + - images_dir_p.ToString()); - } - - while (d_it->HasNext()) { - try { - Path city_dir = d_it->Next(); - if (!city_dir.IsDirectory()) { - continue; - } - - Path img_city_dir = images_dir_p / city_dir.Basename(); - Path task_city_dir = task_dir_p / city_dir.Basename(); - std::shared_ptr img_city_it = Path::DirIterator::OpenDirectory(&img_city_dir); - if (img_city_it == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to open Cityscapes Dataset image city directory: " + - img_city_dir.ToString()); - } - - while (img_city_it->HasNext()) { - Path img_file = img_city_it->Next(); - if (img_file.Extension() != kExtension) { - continue; - } - - Path image_file_path = img_city_dir / img_file.Basename(); - img_file_name = img_file.Basename(); - Path task_file_path = task_city_dir / (img_file_name.substr(0, img_file_name.find("_leftImg8bit")) + "_" + - GetTaskSuffix(task_, real_quality_mode)); - if (!task_file_path.Exists()) { - RETURN_STATUS_UNEXPECTED("Invalid file, Cityscapes Dataset task file: " + task_file_path.ToString() + - " does not exist."); - } - - image_task_map_[image_file_path.ToString()] = task_file_path.ToString(); - } - } catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to load Cityscapes Dataset from " + dataset_dir_ + ": " + - std::string(err.what())); - } - } - - for (auto item : image_task_map_) { - image_task_pairs_.emplace_back(std::make_pair(item.first, item.second)); - } - return Status::OK(); -} - -std::string CityscapesOp::GetTaskSuffix(const std::string &task, const std::string &real_quality_mode) { - std::string task_suffix; - if (task == "instance") { - task_suffix = real_quality_mode + "_instanceIds.png"; - } else if (task == "semantic") { - task_suffix = real_quality_mode + "_labelIds.png"; - } else if (task == "color") { - task_suffix = real_quality_mode + "_color.png"; - } else { - task_suffix = real_quality_mode + "_polygons.json"; - } - return task_suffix; -} - -Status CityscapesOp::CountDatasetInfo() { - num_rows_ = static_cast(image_task_pairs_.size()); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API 'CityscapesDataset'. Please check dataset API or file " - "path: " + - dataset_dir_ + "."); - } - return Status::OK(); -} - -Status CityscapesOp::CountTotalRows(const std::string &dir, const std::string &usage, const std::string &quality_mode, - const std::string &task, int64_t *count) { - // the logic of counting the number of samples is copied from PrepareData() - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - // build a new unique schema object - auto new_schema = std::make_unique(); - RETURN_IF_NOT_OK(new_schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - if (task == "polygon") { - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("task", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - } else { - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("task", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 0, &scalar))); - } - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - std::shared_ptr op = std::make_shared( - num_workers, dir, usage, quality_mode, task, false, op_connect_size, std::move(new_schema), std::move(new_sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = static_cast(op->image_task_pairs_.size()); - return Status::OK(); -} - -Status CityscapesOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h deleted file mode 100644 index 5af37d71e..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CITYSCAPES_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CITYSCAPES_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class CityscapesOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] int32_t num_workers - num of workers reading images in parallel. - /// \param[in] std::string dataset_dir - dir directory of Cityscapes dataset. - /// \param[in] std::string usage - the type of dataset. Acceptable usages include "train", "test", "val" or "all" if - /// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". - /// \param[in] std::string quality_mode - the quality mode of processed image. Acceptable quality_modes include - /// "fine" or "coarse". - /// \param[in] std::string task - the type of task which is used to select output data. Acceptable tasks include - /// "instance", "semantic", "polygon" or "color". - /// \param[in] bool decode - decode the images after reading. - /// \param[in] int32_t queue_size - connector queue size. - /// \param[in] DataSchema data_schema - the schema of each column in output data. - /// \param[in] std::unique_ptr sampler - sampler tells ImageFolderOp what to read. - CityscapesOp(int32_t num_workers, const std::string &dataset_dir, const std::string &usage, - const std::string &quality_mode, const std::string &task, bool decode, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~CityscapesOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out. - /// \param[in] show_all. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the Cityscapes dataset. - /// \param[in] dir - path to the Cityscapes directory. - /// \param[in] usage - the type of dataset. Acceptable usages include "train", "test", "val" or "all" if - /// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". - /// \param[in] quality_mode - the quality mode of processed image. Acceptable quality_modes include - /// "fine" or "coarse". - /// \param[in] task - the type of task which is used to select output data. Acceptable tasks include - /// "instance", "semantic", "polygon" or "color". - /// \param[out] count - output arg that will hold the actual dataset size. - /// \return Status - The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, const std::string &quality_mode, - const std::string &task, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "CityscapesOp"; } - - protected: - /// \brief Parse Cityscapes data. - /// \return Status - The status code returned. - Status PrepareData() override; - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] uint64_t index - index need to load. - /// \param[out] TensorRow row - image & task read into this tensor row. - /// \return Status - The status code returned. - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - /// \brief Get Cityscapes data by usage. - /// \param[in] images_dir - path to the images in the dataset. - /// \param[in] task_dir - path to the given task file. - /// \param[in] real_quality_mode - the real quality mode of image in dataset. - /// \return Status - The status code returned. - Status GetCityscapesDataByUsage(const std::string &images_dir, const std::string &task_dir, - const std::string &real_quality_mode); - - /// \brief Count label index, num rows and num samples. - /// \return Status - The status code returned. - Status CountDatasetInfo(); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status - The status code returned. - Status ComputeColMap() override; - - /// \brief Private function for get the task suffix. - /// \param[in] task - the type of task which is used to select output data. - /// \param[in] real_quality_mode - the real quality mode of image in dataset. - /// \return std::string - the suffix of task file. - std::string GetTaskSuffix(const std::string &task, const std::string &real_quality_mode); - - std::string dataset_dir_; - std::string usage_; - std::string quality_mode_; - std::string task_; - bool decode_; - std::unique_ptr data_schema_; - - std::vector> image_task_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CITYSCAPES_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.cc deleted file mode 100644 index 0d78fef56..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.cc +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h" - -#include -#include -#include -#include -#include - -#include "utils/file_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -ClueOp::ClueOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, ColKeyMap cols_to_keyword, - std::vector clue_files_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id) - : NonMappableLeafOp(num_workers, worker_connector_size, num_samples, op_connector_size, shuffle_files, num_devices, - device_id), - clue_files_list_(std::move(clue_files_list)), - cols_to_keyword_(std::move(cols_to_keyword)) {} - -Status ClueOp::Init() { - RETURN_IF_NOT_OK(filename_index_->insert(clue_files_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(clue_files_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - - return Status::OK(); -} - -Status ClueOp::GetValue(const nlohmann::json &js, std::vector key_chain, std::shared_ptr *t) { - nlohmann::json cursor = js; - for (int i = 0; i < key_chain.size(); i++) { - if (cursor.find(key_chain[i]) != cursor.end()) { - cursor = cursor[key_chain[i]]; - } else { - RETURN_STATUS_UNEXPECTED("Invalid json file, in given JSON file, failed to find key: " + key_chain[i]); - } - } - std::string final_str = key_chain.back(); - switch (cursor.type()) { - case nlohmann::detail::value_t::string: - RETURN_IF_NOT_OK(Tensor::CreateScalar(cursor.get(), t)); - break; - case nlohmann::detail::value_t::number_integer: - RETURN_IF_NOT_OK(Tensor::CreateScalar(cursor.get(), t)); - break; - case nlohmann::detail::value_t::number_unsigned: - RETURN_IF_NOT_OK(Tensor::CreateScalar(cursor.get(), t)); - break; - case nlohmann::detail::value_t::number_float: - RETURN_IF_NOT_OK(Tensor::CreateScalar(cursor.get(), t)); - break; - case nlohmann::detail::value_t::array: - RETURN_IF_NOT_OK(Tensor::CreateFromVector(cursor.get>(), t)); - break; - default: - break; - } - return Status::OK(); -} - -Status ClueOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - std::string err_msg = "Invalid file path, " + file + " does not exist."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + file + ", the file is damaged or permission denied."); - } - - int64_t rows_total = 0; - std::string line; - - while (getline(handle, line)) { - if (line.empty()) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - nlohmann::json js; - try { - js = nlohmann::json::parse(line); - } catch (const std::exception &err) { - // Catch any exception and convert to Status return code - handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid json, failed to parse " + file + ", " + std::string(err.what())); - } - int cols_count = cols_to_keyword_.size(); - TensorRow t_row(cols_count, nullptr); - // Add file path info - std::vector file_path(cols_count, file); - t_row.setPath(file_path); - int cout = 0; - for (auto &p : cols_to_keyword_) { - std::shared_ptr tensor; - auto s = GetValue(js, p.second, &tensor); - if (s != Status::OK()) { - handle.close(); - return s; - } - t_row[cout] = std::move(tensor); - cout++; - } - - rows_total++; - auto s = jagged_rows_connector_->Add(worker_id, std::move(t_row)); - if (s != Status::OK()) { - handle.close(); - return s; - } - } - handle.close(); - - return Status::OK(); -} - -// A print method typically used for debugging -void ClueOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nClue files list:\n"; - for (int i = 0; i < clue_files_list_.size(); ++i) { - out << " " << clue_files_list_[i]; - } - out << "\n\n"; - } -} - -Status ClueOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -Status ClueOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count = CountTotalRows(it.value()); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (int i = 0; i < clue_files_list_.size(); ++i) { - ss << " " << clue_files_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, 'CLUEDataset' API can't read the data file (interface mismatch or no data found). " - "Check file path:" + - file_list); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -int64_t CountTotalRowsPerFile(const std::string &file) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file, " << file << " does not exist."; - return 0; - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - MS_LOG(ERROR) << "Invalid file, failed to open " << file << ": the file is damaged or permission denied."; - return 0; - } - - std::string line; - int64_t count = 0; - while (getline(handle, line)) { - if (!line.empty()) { - count++; - } - } - handle.close(); - - return count; -} - -int64_t ClueOp::CountTotalRows(const std::string &file) { return CountTotalRowsPerFile(file); } - -Status ClueOp::CountAllFileRows(const std::vector &files, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - std::shared_ptr op; - *count = 0; - for (auto file : files) { - *count += CountTotalRowsPerFile(file); - } - return Status::OK(); -} - -Status ClueOp::ComputeColMap() { - // Set the column name mapping (base class field) - if (column_name_id_map_.empty()) { - int count = 0; - for (auto &p : cols_to_keyword_) { - column_name_id_map_[p.first] = count; - count++; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h deleted file mode 100644 index 4bedf3f4b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CLUE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CLUE_OP_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/auto_index.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { -using StringIndex = AutoIndexObj; -using ColKeyMap = std::map>; - -class JaggedConnector; - -class ClueOp : public NonMappableLeafOp { - public: - // Constructor of ClueOp - ClueOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, ColKeyMap cols_to_keyword, - std::vector clue_files_list, int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id); - - // Default destructor - ~ClueOp() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // Instantiates the internal queues and connectors - // @return Status - the error code returned - Status Init() override; - - // Get total rows in files. - // @param files - all clue files. - // @param count - number of rows. - // @return Status - the error coed returned. - static Status CountAllFileRows(const std::vector &files, int64_t *count); - - // File names getter - // @return Vector of the input file names - std::vector FileNames() { return clue_files_list_; } - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "CLUEOp"; } - - private: - // Reads a clue file and loads the data into multiple TensorRows. - // @param file - the file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - // Fill the IOBlockQueue. - // @para i_keys - keys of file to fill to the IOBlockQueue - // @return Status - the error code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - Status CalculateNumRowsPerShard() override; - - // Count number of rows in each file. - // @param filename - clue file name. - // @return int64_t - the total number of rows in file. - int64_t CountTotalRows(const std::string &file); - - // @return Status - the error code returned. - Status GetValue(const nlohmann::json &js, std::vector key_chain, std::shared_ptr *t); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - std::vector clue_files_list_; - ColKeyMap cols_to_keyword_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CLUE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.cc deleted file mode 100644 index 71ab0e23b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.cc +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -const char kDataDirectory[] = "wav"; -const char kLabelDirectory[] = "etc"; -const char kLabelFileName[] = "txt.done.data"; -const char kDataFilePrefix[] = "cmu_us_"; -const char kDataFileSuffix[] = "_arctic"; - -CMUArcticOp::CMUArcticOp(const std::string &dataset_dir, const std::string &name, int32_t num_workers, - int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(dataset_dir), - name_(name), - data_schema_(std::move(data_schema)) {} - -Status CMUArcticOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - const uint32_t sample_rate = 16000; - const std::string wav_suffix = ".wav"; - size_t pos = label_pairs_[row_id].first.find_last_of('_'); - CHECK_FAIL_RETURN_UNEXPECTED( - pos != std::string::npos && pos + 1 < label_pairs_[row_id].first.size(), - "Invalid utterance id, please check if it is in valid format: " + label_pairs_[row_id].first); - std::string utterance_id_t = label_pairs_[row_id].first.substr(pos + 1); - std::string full_name_path = kDataFilePrefix + name_ + kDataFileSuffix; - std::string file_name = label_pairs_[row_id].first + wav_suffix; - Path root_folder(real_path_); - Path wav_file_path = root_folder / full_name_path / kDataDirectory / file_name; - std::shared_ptr waveform, rate, transcript, utterance_id; - RETURN_IF_NOT_OK(ReadAudio(wav_file_path.ToString(), &waveform)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &rate)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(label_pairs_[row_id].second, &transcript)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(utterance_id_t, &utterance_id)); - (*trow) = TensorRow(row_id, {std::move(waveform), std::move(rate), std::move(transcript), std::move(utterance_id)}); - Path label_dir = root_folder / full_name_path / kLabelDirectory / kLabelFileName; - trow->setPath({wav_file_path.ToString(), wav_file_path.ToString(), label_dir.ToString(), label_dir.ToString()}); - return Status::OK(); -} - -void CMUArcticOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nCMUArctic directory: " << folder_path_ << "\n\n"; - } -} - -Status CMUArcticOp::CountTotalRows(const std::string &dir, const std::string &name, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_utterance = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("transcript", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance))); - TensorShape scalar_utterance_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("utterance_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance_id))); - std::shared_ptr cfg = GlobalContext::config_manager(); - - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = - std::make_shared(dir, name, num_workers, op_connect_size, std::move(schema), std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = op->label_pairs_.size(); - return Status::OK(); -} - -Status CMUArcticOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status CMUArcticOp::ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform) { - RETURN_UNEXPECTED_IF_NULL(waveform); - const int32_t kWavFileSampleRate = 16000; - int32_t sample_rate = 0; - std::vector waveform_vec; - RETURN_IF_NOT_OK(ReadWaveFile(audio_dir, &waveform_vec, &sample_rate)); - CHECK_FAIL_RETURN_UNEXPECTED( - sample_rate == kWavFileSampleRate, - "Invalid file, sampling rate of CMUArctic wav file must be 16000, file path: " + audio_dir); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, waveform)); - RETURN_IF_NOT_OK((*waveform)->ExpandDim(0)); - return Status::OK(); -} - -Status CMUArcticOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, CMUArctic Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, CMUArctic Dataset dir: " + folder_path_ + " does not exist."); - } - real_path_ = realpath.value(); - Path dir(real_path_); - std::string full_name_path = kDataFilePrefix + name_ + kDataFileSuffix; - Path label_dir = dir / full_name_path / kLabelDirectory / kLabelFileName; - CHECK_FAIL_RETURN_UNEXPECTED(label_dir.Exists() && !label_dir.IsDirectory(), - "Invalid file, failed to find label file: " + label_dir.ToString()); - std::ifstream label_reader(label_dir.ToString(), std::ifstream::in); - CHECK_FAIL_RETURN_UNEXPECTED(label_reader.is_open(), - "Invalid file, failed to open label file: " + label_dir.ToString() + - ", make sure file not damaged or permission denied."); - std::string line = ""; - while (getline(label_reader, line)) { - size_t quot_inx[2] = {0}; - size_t quot_num = 0; - size_t quot_exact = 2; - for (size_t i = 0; quot_num < quot_exact && i < line.size(); i++) { - if (line[i] == '"') { - quot_inx[quot_num++] = i; - } - } - if (quot_num != quot_exact) { - label_reader.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, the file may not be a CMUArctic dataset file: " + label_dir.ToString()); - } - label_pairs_.push_back( - {line.substr(2, quot_inx[0] - 3), line.substr(quot_inx[0] + 1, quot_inx[1] - quot_inx[0] - 1)}); - } - label_reader.close(); - num_rows_ = label_pairs_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0, "Invalid data, no valid data found in path: " + folder_path_); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h deleted file mode 100644 index 7def4366a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CMU_ARCTIC_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CMU_ARCTIC_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class CMUArcticOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Directory of CMUArctic. - /// \param[in] name Part of this dataset, can be "aew", "ahw", "aup", "awb", "axb", "bdl", - /// "clb", "eey", "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt" - /// \param[in] num_workers Number of workers reading audios in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema The schema of the CMUArctic dataset. - /// \param[in] sampler Sampler tells CMUArcticOp what to read. - CMUArcticOp(const std::string &dataset_dir, const std::string &name, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~CMUArcticOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the CMUArctic dataset. - /// \param[in] dir Path to the CMUArctic directory. - /// \param[in] name Choose the subset of CMUArctic dataset. - /// \param[out] count Output arg that will hold the minimum of the actual dataset size and numSamples. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &name, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "CMUArcticOp"; } - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] row_id Id for this tensor row. - /// \param[out] row Audio & label read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Parse a single wav file. - /// \param[in] audio_dir Audio file path. - /// \param[out] waveform The output waveform tensor. - /// \return Status The status code returned. - Status ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform); - - /// \brief Prepare all data in the directory. - /// \return Status The status code returned. - Status PrepareData(); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status. - Status ComputeColMap() override; - - const std::string name_; - std::string folder_path_; - std::string real_path_; - std::unique_ptr data_schema_; - std::vector> label_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CMU_ARCTIC_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.cc deleted file mode 100644 index 2ab13b0e3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.cc +++ /dev/null @@ -1,636 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h" - -#include -#include -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const char kJsonImages[] = "images"; -const char kJsonImagesFileName[] = "file_name"; -const char kJsonId[] = "id"; -const char kJsonAnnotations[] = "annotations"; -const char kJsonAnnoSegmentation[] = "segmentation"; -const char kJsonAnnoCaption[] = "caption"; -const char kJsonAnnoCounts[] = "counts"; -const char kJsonAnnoSegmentsInfo[] = "segments_info"; -const char kJsonAnnoIscrowd[] = "iscrowd"; -const char kJsonAnnoBbox[] = "bbox"; -const char kJsonAnnoArea[] = "area"; -const char kJsonAnnoImageId[] = "image_id"; -const char kJsonAnnoNumKeypoints[] = "num_keypoints"; -const char kJsonAnnoKeypoints[] = "keypoints"; -const char kJsonAnnoCategoryId[] = "category_id"; -const char kJsonCategories[] = "categories"; -const char kJsonCategoriesIsthing[] = "isthing"; -const char kJsonCategoriesName[] = "name"; -const float kDefaultPadValue = -1.0; -const unsigned int kPadValueZero = 0; - -#ifdef ENABLE_PYTHON -CocoOp::CocoOp(const TaskType &task_type, const std::string &image_folder_path, const std::string &annotation_path, - int32_t num_workers, int32_t queue_size, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, bool extra_metadata, py::function decrypt) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - decode_(decode), - task_type_(task_type), - image_folder_path_(image_folder_path), - annotation_path_(annotation_path), - data_schema_(std::move(data_schema)), - extra_metadata_(extra_metadata), - decrypt_(std::move(decrypt)) {} -#else -CocoOp::CocoOp(const TaskType &task_type, const std::string &image_folder_path, const std::string &annotation_path, - int32_t num_workers, int32_t queue_size, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, bool extra_metadata) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - decode_(decode), - task_type_(task_type), - image_folder_path_(image_folder_path), - annotation_path_(annotation_path), - data_schema_(std::move(data_schema)), - extra_metadata_(extra_metadata) {} -#endif - -void CocoOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nCOCO Directory: " << image_folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status CocoOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string image_id = image_ids_[row_id]; - std::shared_ptr image; - auto real_path = FileUtils::GetRealPath(image_folder_path_.c_str()); - if (!real_path.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, COCO dataset image folder: " + image_folder_path_ + - " does not exist."); - } - Path image_folder(real_path.value()); - Path kImageFile = image_folder / image_id; - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile.ToString(), &image)); - if (task_type_ == TaskType::Captioning) { - std::shared_ptr captions; - auto itr = captions_map_.find(image_id); - if (itr == captions_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the attribute of 'image_id': " + image_id + - " is missing from image node in annotation file: " + annotation_path_); - } - auto captions_str = itr->second; - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(captions_str, TensorShape({static_cast(captions_str.size()), 1}), &captions)); - RETURN_IF_NOT_OK(LoadCaptioningTensorRow(row_id, image_id, image, captions, trow)); - return Status::OK(); - } - std::shared_ptr coordinate; - auto itr = coordinate_map_.find(image_id); - if (itr == coordinate_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the attribute of 'image_id': " + image_id + - " is missing from image node in annotation file: " + annotation_path_); - } - - auto bboxRow = itr->second; - std::vector bbox_row; - auto bbox_row_num = static_cast(bboxRow.size()); - dsize_t bbox_column_num = 0; - std::for_each(bboxRow.begin(), bboxRow.end(), [&](const auto &bbox) { - if (static_cast(bbox.size()) > bbox_column_num) { - bbox_column_num = static_cast(bbox.size()); - } - }); - - for (auto bbox : bboxRow) { - bbox_row.insert(bbox_row.end(), bbox.begin(), bbox.end()); - dsize_t pad_len = bbox_column_num - static_cast(bbox.size()); - if (pad_len > 0) { - for (dsize_t i = 0; i < pad_len; i++) { - bbox_row.push_back(kDefaultPadValue); - } - } - } - - std::vector bbox_dim = {bbox_row_num, bbox_column_num}; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(bbox_row, TensorShape(bbox_dim), &coordinate)); - - if (task_type_ == TaskType::Detection) { - RETURN_IF_NOT_OK(LoadDetectionTensorRow(row_id, image_id, image, coordinate, trow)); - } else if (task_type_ == TaskType::Stuff || task_type_ == TaskType::Keypoint) { - RETURN_IF_NOT_OK(LoadSimpleTensorRow(row_id, image_id, image, coordinate, trow)); - } else if (task_type_ == TaskType::Panoptic) { - RETURN_IF_NOT_OK(LoadMixTensorRow(row_id, image_id, image, coordinate, trow)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid parameter, task type should be Detection, Stuff, Panoptic or Captioning."); - } - - return Status::OK(); -} - -Status CocoOp::LoadDetectionTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr category_id, iscrowd; - std::vector category_id_row; - std::vector iscrowd_row; - auto itr_item = simple_item_map_.find(image_id); - if (itr_item == simple_item_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the attribute of 'image_id': " + image_id + - " is missing in the node of image from annotation file: " + annotation_path_ + "."); - } - - std::vector annotation = itr_item->second; - for (int64_t i = 0; i < annotation.size(); i++) { - if (i % 2 == 0) { - category_id_row.push_back(annotation[i]); - } else if (i % 2 == 1) { - iscrowd_row.push_back(annotation[i]); - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector( - category_id_row, TensorShape({static_cast(category_id_row.size()), 1}), &category_id)); - - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(iscrowd_row, TensorShape({static_cast(iscrowd_row.size()), 1}), &iscrowd)); - - (*trow) = TensorRow(row_id, {std::move(image), std::move(coordinate), std::move(category_id), std::move(iscrowd)}); - Path image_folder(image_folder_path_); - Path image_full_path = image_folder / image_id; - std::vector path_list = {image_full_path.ToString(), annotation_path_, annotation_path_, - annotation_path_}; - if (extra_metadata_) { - std::string img_id; - size_t pos = image_id.find('.'); - if (pos == std::string::npos) { - RETURN_STATUS_UNEXPECTED("Invalid image, 'image_id': " + image_id + " should be with suffix like \".jpg\""); - } - (void)std::copy(image_id.begin(), image_id.begin() + pos, std::back_inserter(img_id)); - std::shared_ptr filename; - RETURN_IF_NOT_OK(Tensor::CreateScalar(img_id, &filename)); - trow->push_back(std::move(filename)); - path_list.push_back(image_full_path.ToString()); - } - trow->setPath(path_list); - return Status::OK(); -} - -Status CocoOp::LoadSimpleTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr item; - std::vector item_queue; - Path image_folder(image_folder_path_); - auto itr_item = simple_item_map_.find(image_id); - if (itr_item == simple_item_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid image_id, the attribute of 'image_id': " + image_id + - " is missing in the node of 'image' from annotation file: " + annotation_path_); - } - - item_queue = itr_item->second; - std::vector bbox_dim = {static_cast(item_queue.size()), 1}; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(item_queue, TensorShape(bbox_dim), &item)); - - (*trow) = TensorRow(row_id, {std::move(image), std::move(coordinate), std::move(item)}); - Path image_full_path = image_folder / image_id; - std::vector path_list = {image_full_path.ToString(), annotation_path_, annotation_path_}; - if (extra_metadata_) { - std::string img_id; - size_t pos = image_id.find('.'); - if (pos == std::string::npos) { - RETURN_STATUS_UNEXPECTED("Invalid image, 'image_id': " + image_id + " should be with suffix like \".jpg\""); - } - (void)std::copy(image_id.begin(), image_id.begin() + pos, std::back_inserter(img_id)); - std::shared_ptr filename; - RETURN_IF_NOT_OK(Tensor::CreateScalar(img_id, &filename)); - trow->push_back(std::move(filename)); - path_list.push_back(image_full_path.ToString()); - } - trow->setPath(path_list); - return Status::OK(); -} - -Status CocoOp::LoadCaptioningTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr captions, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - (*trow) = TensorRow(row_id, {std::move(image), std::move(captions)}); - Path image_folder(image_folder_path_); - Path image_full_path = image_folder / image_id; - std::vector path_list = {image_full_path.ToString(), annotation_path_}; - if (extra_metadata_) { - std::string img_id; - size_t pos = image_id.find('.'); - if (pos == std::string::npos) { - RETURN_STATUS_UNEXPECTED("Invalid image, 'image_id': " + image_id + " should be with suffix like \".jpg\"."); - } - (void)std::copy(image_id.begin(), image_id.begin() + pos, std::back_inserter(img_id)); - std::shared_ptr filename; - RETURN_IF_NOT_OK(Tensor::CreateScalar(img_id, &filename)); - trow->push_back(std::move(filename)); - path_list.push_back(image_full_path.ToString()); - } - trow->setPath(path_list); - return Status::OK(); -} - -Status CocoOp::LoadMixTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr category_id, iscrowd, area; - std::vector category_id_row; - std::vector iscrowd_row; - std::vector area_row; - auto itr_item = simple_item_map_.find(image_id); - if (itr_item == simple_item_map_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid image_id, the attribute of 'image_id': " + image_id + - " is missing in the node of 'image' from annotation file: " + annotation_path_); - } - - std::vector annotation = itr_item->second; - for (int64_t i = 0; i < annotation.size(); i++) { - if (i % 3 == 0) { - category_id_row.push_back(annotation[i]); - } else if (i % 3 == 1) { - iscrowd_row.push_back(annotation[i]); - } else if (i % 3 == 2) { - area_row.push_back(annotation[i]); - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector( - category_id_row, TensorShape({static_cast(category_id_row.size()), 1}), &category_id)); - - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(iscrowd_row, TensorShape({static_cast(iscrowd_row.size()), 1}), &iscrowd)); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(area_row, TensorShape({static_cast(area_row.size()), 1}), &area)); - - (*trow) = TensorRow( - row_id, {std::move(image), std::move(coordinate), std::move(category_id), std::move(iscrowd), std::move(area)}); - Path image_folder(image_folder_path_); - Path image_full_path = image_folder / image_id; - std::vector path_list = {image_full_path.ToString(), annotation_path_, annotation_path_, - annotation_path_, annotation_path_}; - if (extra_metadata_) { - std::string img_id; - size_t pos = image_id.find('.'); - if (pos == std::string::npos) { - RETURN_STATUS_UNEXPECTED("Invalid image, " + image_id + " should be with suffix like \".jpg\""); - } - (void)std::copy(image_id.begin(), image_id.begin() + pos, std::back_inserter(img_id)); - std::shared_ptr filename; - RETURN_IF_NOT_OK(Tensor::CreateScalar(img_id, &filename)); - trow->push_back(std::move(filename)); - path_list.push_back(image_full_path.ToString()); - } - trow->setPath(path_list); - return Status::OK(); -} - -template -Status CocoOp::SearchNodeInJson(const nlohmann::json &input_tree, std::string node_name, T *output_node) { - RETURN_UNEXPECTED_IF_NULL(output_node); - auto node = input_tree.find(node_name); - CHECK_FAIL_RETURN_UNEXPECTED(node != input_tree.end(), "Invalid annotation, the attribute of '" + node_name + - "' is missing in annotation file: " + annotation_path_ + - "."); - (*output_node) = *node; - return Status::OK(); -} - -Status CocoOp::PrepareData() { - nlohmann::json js; - auto realpath = FileUtils::GetRealPath(annotation_path_.c_str()); - if (!realpath.has_value()) { - std::string err_msg = "Invalid file path, Coco Dataset annotation file: " + annotation_path_ + " does not exist."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::ifstream in(realpath.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(in.is_open(), "Invalid annotation file, Coco Dataset annotation file: " + - annotation_path_ + " open failed, permission denied!"); - try { - in >> js; - in.close(); - } catch (const std::exception &err) { - in.close(); - RETURN_STATUS_UNEXPECTED("Invalid annotation file, Coco Dataset annotation file:" + annotation_path_ + - " load failed, error description: " + std::string(err.what())); - } - - std::vector image_que; - nlohmann::json image_list; - RETURN_IF_NOT_OK(SearchNodeInJson(js, std::string(kJsonImages), &image_list)); - RETURN_IF_NOT_OK(ImageColumnLoad(image_list, &image_que)); - if (task_type_ == TaskType::Detection || task_type_ == TaskType::Panoptic) { - nlohmann::json node_categories; - RETURN_IF_NOT_OK(SearchNodeInJson(js, std::string(kJsonCategories), &node_categories)); - RETURN_IF_NOT_OK(CategoriesColumnLoad(node_categories)); - } - nlohmann::json annotations_list; - RETURN_IF_NOT_OK(SearchNodeInJson(js, std::string(kJsonAnnotations), &annotations_list)); - for (const auto &annotation : annotations_list) { - int32_t image_id = 0, id = 0; - std::string file_name; - RETURN_IF_NOT_OK(SearchNodeInJson(annotation, std::string(kJsonAnnoImageId), &image_id)); - auto itr_file = image_index_.find(image_id); - CHECK_FAIL_RETURN_UNEXPECTED(itr_file != image_index_.end(), - "Invalid annotation, the attribute of 'image_id': " + std::to_string(image_id) + - " is missing in the node of 'image' from annotation file: " + annotation_path_); - file_name = itr_file->second; - switch (task_type_) { - case TaskType::Detection: - RETURN_IF_NOT_OK(SearchNodeInJson(annotation, std::string(kJsonId), &id)); - RETURN_IF_NOT_OK(DetectionColumnLoad(annotation, file_name, id)); - break; - case TaskType::Stuff: - RETURN_IF_NOT_OK(SearchNodeInJson(annotation, std::string(kJsonId), &id)); - RETURN_IF_NOT_OK(StuffColumnLoad(annotation, file_name, id)); - break; - case TaskType::Keypoint: - RETURN_IF_NOT_OK(SearchNodeInJson(annotation, std::string(kJsonId), &id)); - RETURN_IF_NOT_OK(KeypointColumnLoad(annotation, file_name, id)); - break; - case TaskType::Panoptic: - RETURN_IF_NOT_OK(PanopticColumnLoad(annotation, file_name, image_id)); - break; - case TaskType::Captioning: - RETURN_IF_NOT_OK(SearchNodeInJson(annotation, std::string(kJsonId), &id)); - RETURN_IF_NOT_OK(CaptionColumnLoad(annotation, file_name, id)); - break; - default: - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, task type should be Detection, Stuff, Keypoint, Panoptic or Captioning."); - } - } - if (task_type_ == TaskType::Captioning) { - for (const auto &img : image_que) { - if (captions_map_.find(img) != captions_map_.end()) { - image_ids_.push_back(img); - } - } - } else { - for (const auto &img : image_que) { - if (coordinate_map_.find(img) != coordinate_map_.end()) { - image_ids_.push_back(img); - } - } - } - num_rows_ = image_ids_.size(); - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows_ != 0, - "Invalid data, 'CocoDataset' API can't read the data file (interface mismatch or no data found). " - "Check file in directory: " + - image_folder_path_ + "."); - return Status::OK(); -} - -Status CocoOp::ImageColumnLoad(const nlohmann::json &image_tree, std::vector *image_vec) { - RETURN_UNEXPECTED_IF_NULL(image_vec); - if (image_tree.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the 'image' node is missing in annotation file: " + annotation_path_ + - "."); - } - for (const auto &img : image_tree) { - std::string file_name; - int32_t id = 0; - RETURN_IF_NOT_OK(SearchNodeInJson(img, std::string(kJsonImagesFileName), &file_name)); - RETURN_IF_NOT_OK(SearchNodeInJson(img, std::string(kJsonId), &id)); - - image_index_[id] = file_name; - image_vec->push_back(file_name); - } - return Status::OK(); -} - -Status CocoOp::DetectionColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &unique_id) { - std::vector bbox; - nlohmann::json node_bbox; - uint32_t category_id = 0, iscrowd = 0; - RETURN_IF_NOT_OK(SearchNodeInJson(annotation_tree, std::string(kJsonAnnoBbox), &node_bbox)); - RETURN_IF_NOT_OK(SearchNodeInJson(annotation_tree, std::string(kJsonAnnoCategoryId), &category_id)); - auto search_category = category_set_.find(category_id); - if (search_category == category_set_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the attribute of 'category_id': " + std::to_string(category_id) + - " is missing in the node of 'categories' from annotation file: " + annotation_path_); - } - auto node_iscrowd = annotation_tree.find(kJsonAnnoIscrowd); - if (node_iscrowd != annotation_tree.end()) { - iscrowd = *node_iscrowd; - } - bbox.insert(bbox.end(), node_bbox.begin(), node_bbox.end()); - coordinate_map_[image_file].push_back(bbox); - simple_item_map_[image_file].push_back(category_id); - simple_item_map_[image_file].push_back(iscrowd); - return Status::OK(); -} - -Status CocoOp::StuffColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &unique_id) { - uint32_t iscrowd = 0; - std::vector bbox; - RETURN_IF_NOT_OK(SearchNodeInJson(annotation_tree, std::string(kJsonAnnoIscrowd), &iscrowd)); - simple_item_map_[image_file].push_back(iscrowd); - nlohmann::json segmentation; - RETURN_IF_NOT_OK(SearchNodeInJson(annotation_tree, std::string(kJsonAnnoSegmentation), &segmentation)); - if (iscrowd == 0) { - for (auto item : segmentation) { - if (!bbox.empty()) { - bbox.clear(); - } - bbox.insert(bbox.end(), item.begin(), item.end()); - coordinate_map_[image_file].push_back(bbox); - } - } else if (iscrowd == 1) { - nlohmann::json segmentation_count; - RETURN_IF_NOT_OK(SearchNodeInJson(segmentation, std::string(kJsonAnnoCounts), &segmentation_count)); - bbox.insert(bbox.end(), segmentation_count.begin(), segmentation_count.end()); - coordinate_map_[image_file].push_back(bbox); - } - return Status::OK(); -} - -Status CocoOp::KeypointColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &unique_id) { - auto itr_num_keypoint = annotation_tree.find(kJsonAnnoNumKeypoints); - if (itr_num_keypoint == annotation_tree.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the 'num_keypoint' node is missing in annotation file: " + - annotation_path_ + " where 'image_id': " + std::to_string(unique_id) + "."); - } - simple_item_map_[image_file].push_back(*itr_num_keypoint); - auto itr_keypoint = annotation_tree.find(kJsonAnnoKeypoints); - if (itr_keypoint == annotation_tree.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the 'keypoint' node is missing in annotation file: " + - annotation_path_ + " where 'image_id': " + std::to_string(unique_id) + "."); - } - coordinate_map_[image_file].push_back(*itr_keypoint); - return Status::OK(); -} - -Status CocoOp::PanopticColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &image_id) { - auto itr_segments = annotation_tree.find(kJsonAnnoSegmentsInfo); - if (itr_segments == annotation_tree.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the 'segments_info' node is missing in annotation file: " + - annotation_path_ + " where 'image_id': " + std::to_string(image_id) + "."); - } - for (auto info : *itr_segments) { - std::vector bbox; - uint32_t category_id = 0; - auto itr_bbox = info.find(kJsonAnnoBbox); - if (itr_bbox == info.end()) { - RETURN_STATUS_UNEXPECTED( - "Invalid annotation, the 'bbox' attribute is missing in the node of 'segments_info' where 'image_id': " + - std::to_string(image_id) + " from annotation file: " + annotation_path_ + "."); - } - bbox.insert(bbox.end(), itr_bbox->begin(), itr_bbox->end()); - coordinate_map_[image_file].push_back(bbox); - - RETURN_IF_NOT_OK(SearchNodeInJson(info, std::string(kJsonAnnoCategoryId), &category_id)); - auto search_category = category_set_.find(category_id); - if (search_category == category_set_.end()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation, the attribute of 'category_id': " + std::to_string(category_id) + - " is missing in the node of 'categories' from " + annotation_path_ + "."); - } - auto itr_iscrowd = info.find(kJsonAnnoIscrowd); - if (itr_iscrowd == info.end()) { - RETURN_STATUS_UNEXPECTED( - "Invalid annotation, the attribute of 'iscrowd' is missing in the node of 'segments_info' where 'image_id': " + - std::to_string(image_id) + " from annotation file: " + annotation_path_ + "."); - } - auto itr_area = info.find(kJsonAnnoArea); - if (itr_area == info.end()) { - RETURN_STATUS_UNEXPECTED( - "Invalid annotation, the attribute of 'area' is missing in the node of 'segments_info' where 'image_id': " + - std::to_string(image_id) + " from annotation file: " + annotation_path_ + "."); - } - simple_item_map_[image_file].push_back(category_id); - simple_item_map_[image_file].push_back(*itr_iscrowd); - simple_item_map_[image_file].push_back(*itr_area); - } - return Status::OK(); -} - -Status CocoOp::CaptionColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &unique_id) { - std::string caption; - RETURN_IF_NOT_OK(SearchNodeInJson(annotation_tree, std::string(kJsonAnnoCaption), &caption)); - captions_map_[image_file].push_back(caption); - return Status::OK(); -} - -Status CocoOp::CategoriesColumnLoad(const nlohmann::json &categories_tree) { - if (categories_tree.empty()) { - RETURN_STATUS_UNEXPECTED( - "Invalid annotation, the 'categories' node is missing in annotation file: " + annotation_path_ + "."); - } - for (auto category : categories_tree) { - int32_t id = 0; - std::string name; - std::vector label_info; - auto itr_id = category.find(kJsonId); - if (itr_id == category.end()) { - RETURN_STATUS_UNEXPECTED( - "Invalid annotation, the attribute of 'id' is missing in the node of 'categories' from annotation file: " + - annotation_path_); - } - id = *itr_id; - label_info.push_back(id); - category_set_.insert(id); - - auto itr_name = category.find(kJsonCategoriesName); - CHECK_FAIL_RETURN_UNEXPECTED( - itr_name != category.end(), - "Invalid annotation, the attribute of 'name' is missing in the node of 'categories' where 'id': " + - std::to_string(id)); - name = *itr_name; - - if (task_type_ == TaskType::Panoptic) { - auto itr_isthing = category.find(kJsonCategoriesIsthing); - CHECK_FAIL_RETURN_UNEXPECTED(itr_isthing != category.end(), - "Invalid annotation, the attribute of 'isthing' is missing in the node of " - "'categories' from annotation file: " + - annotation_path_); - label_info.push_back(*itr_isthing); - } - label_index_.emplace_back(std::make_pair(name, label_info)); - } - return Status::OK(); -} - -Status CocoOp::ReadImageToTensor(const std::string &path, std::shared_ptr *tensor) const { -#ifdef ENABLE_PYTHON - RETURN_IF_NOT_OK(MappableLeafOp::ImageDecrypt(path, tensor, decrypt_)); -#else - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); -#endif - - if (decode_) { - Status rc = Decode(*tensor, tensor); - CHECK_FAIL_RETURN_UNEXPECTED( - rc.IsOk(), "Invalid image, failed to decode " + path + ": the image is broken or permission denied."); - } - return Status::OK(); -} - -Status CocoOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - RETURN_IF_NOT_OK(PrepareData()); - *count = static_cast(image_ids_.size()); - return Status::OK(); -} - -Status CocoOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status CocoOp::GetClassIndexing(std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - if ((*output_class_indexing).empty()) { - if ((task_type_ != TaskType::Detection) && (task_type_ != TaskType::Panoptic)) { - MS_LOG(ERROR) << "Invalid task, only 'Detection' and 'Panoptic' task support GetClassIndex."; - RETURN_STATUS_UNEXPECTED("Invalid task, only 'Detection' and 'Panoptic' task support GetClassIndex."); - } - RETURN_IF_NOT_OK(PrepareData()); - for (const auto &label : label_index_) { - (*output_class_indexing).emplace_back(std::make_pair(label.first, label.second)); - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h deleted file mode 100644 index c221f5af4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_COCO_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_COCO_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using CoordinateRow = std::vector>; - -class CocoOp : public MappableLeafOp { - public: - enum class TaskType { Detection = 0, Stuff = 1, Panoptic = 2, Keypoint = 3, Captioning = 4 }; - - class Builder { - public: - // Constructor for Builder class of ImageFolderOp - // @param uint32_t numWrks - number of parallel workers - // @param dir - directory folder got ImageNetFolder - Builder(); - - // Destructor. - ~Builder() = default; - - // Setter method. - // @param const std::string & build_dir - // @return Builder setter method returns reference to the builder. - Builder &SetDir(const std::string &build_dir) { - builder_dir_ = build_dir; - return *this; - } - - // Setter method. - // @param const std::string & build_file - // @return Builder setter method returns reference to the builder. - Builder &SetFile(const std::string &build_file) { - builder_file_ = build_file; - return *this; - } - - // Setter method. - // @param const std::string & task_type - // @return Builder setter method returns reference to the builder. - Builder &SetTask(const std::string &task_type) { - if (task_type == "Detection") { - builder_task_type_ = TaskType::Detection; - } else if (task_type == "Stuff") { - builder_task_type_ = TaskType::Stuff; - } else if (task_type == "Panoptic") { - builder_task_type_ = TaskType::Panoptic; - } else if (task_type == "Keypoint") { - builder_task_type_ = TaskType::Keypoint; - } - return *this; - } - - // Setter method. - // @param int32_t num_workers - // @return Builder setter method returns reference to the builder. - Builder &SetNumWorkers(int32_t num_workers) { - builder_num_workers_ = num_workers; - return *this; - } - - // Setter method. - // @param int32_t op_connector_size - // @return Builder setter method returns reference to the builder. - Builder &SetOpConnectorSize(int32_t op_connector_size) { - builder_op_connector_size_ = op_connector_size; - return *this; - } - - // Setter method. - // @param std::shared_ptr sampler - // @return Builder setter method returns reference to the builder. - Builder &SetSampler(std::shared_ptr sampler) { - builder_sampler_ = std::move(sampler); - return *this; - } - - // Setter method. - // @param bool do_decode - // @return Builder setter method returns reference to the builder. - Builder &SetDecode(bool do_decode) { - builder_decode_ = do_decode; - return *this; - } - - // Check validity of input args - // @return Status The status code returned - Status SanityCheck(); - - // The builder "Build" method creates the final object. - // @param std::shared_ptr *op - DatasetOp - // @return Status The status code returned - Status Build(std::shared_ptr *op); - - private: - bool builder_decode_; - std::string builder_dir_; - std::string builder_file_; - TaskType builder_task_type_; - int32_t builder_num_workers_; - int32_t builder_op_connector_size_; - int32_t builder_rows_per_buffer_; - std::shared_ptr builder_sampler_; - std::unique_ptr builder_schema_; - }; - -#ifdef ENABLE_PYTHON - /// \brief Constructor. - /// \param[in] task_type Task type of Coco. - /// \param[in] image_folder_path Image folder path of Coco. - /// \param[in] annotation_path Annotation json path of Coco. - /// \param[in] num_workers Number of workers reading images in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] num_samples Number of samples to read. - /// \param[in] decode Whether to decode images. - /// \param[in] data_schema The schema of the Coco dataset. - /// \param[in] sampler Sampler tells CocoOp what to read. - /// \param[in] decrypt - Image decryption function, which accepts the path of the encrypted image file - /// and returns the decrypted bytes data. Default: None, no decryption. - CocoOp(const TaskType &task_type, const std::string &image_folder_path, const std::string &annotation_path, - int32_t num_workers, int32_t queue_size, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, bool extra_metadata, py::function decrypt = py::none()); -#else - /// \brief Constructor. - /// \param[in] task_type Task type of Coco. - /// \param[in] image_folder_path Image folder path of Coco. - /// \param[in] annotation_path Annotation json path of Coco. - /// \param[in] num_workers Number of workers reading images in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] num_samples Number of samples to read. - /// \param[in] decode Whether to decode images. - /// \param[in] data_schema The schema of the Coco dataset. - /// \param[in] sampler Sampler tells CocoOp what to read. - CocoOp(const TaskType &task_type, const std::string &image_folder_path, const std::string &annotation_path, - int32_t num_workers, int32_t queue_size, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, bool extra_metadata); -#endif - - /// \brief Destructor. - ~CocoOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \param[out] count Output rows number of CocoDataset. - Status CountTotalRows(int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "CocoOp"; } - - /// \brief Gets the class indexing. - /// \return Status The status code returned. - Status GetClassIndexing(std::vector>> *output_class_indexing) override; - - private: - /// \brief Load a tensor row according to image id. - /// \param[in] row_id Id for this tensor row. - /// \param[out] row Image & target read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Load a tensor row with vector which a vector to a tensor, for "Detection" task. - /// \param[in] row_id Id for this tensor row. - /// \param[in] image_id Image id. - /// \param[in] image Image tensor. - /// \param[in] coordinate Coordinate tensor. - /// \param[out] row Image & target read into this tensor row. - /// \return Status The status code returned. - Status LoadDetectionTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow); - - /// \brief Load a tensor row with vector which a vector to a tensor, for "Stuff/Keypoint" task. - /// \param[in] row_id Id for this tensor row. - /// \param[in] image_id Image id. - /// \param[in] image Image tensor. - /// \param[in] coordinate Coordinate tensor. - /// \param[out] row Image & target read into this tensor row. - /// \return Status The status code returned. - Status LoadSimpleTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow); - - /// \brief Load a tensor row with vector which a vector to multi-tensor, for "Panoptic" task. - /// \param[in] row_id Id for this tensor row. - /// \param[in] image_id Image id. - /// \param[in] image Image tensor. - /// \param[in] coordinate Coordinate tensor. - /// \param[out] row Image & target read into this tensor row. - /// \return Status The status code returned. - Status LoadMixTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr coordinate, TensorRow *trow); - - /// \brief Load a tensor row with vector which a vector to multi-tensor, for "Captioning" task. - /// \param[in] row_id Id for this tensor row. - /// \param[in] image_id Image id. - /// \param[in] image Image tensor. - /// \param[in] captions Captions tensor. - /// \param[out] trow Image & target read into this tensor row. - /// \return Status The status code returned. - Status LoadCaptioningTensorRow(row_id_type row_id, const std::string &image_id, std::shared_ptr image, - std::shared_ptr captions, TensorRow *trow); - - /// \param[in] path Path to the image file. - /// \param[out] tensor Returned tensor. - /// \return Status The status code returned. - Status ReadImageToTensor(const std::string &path, std::shared_ptr *tensor) const; - - /// \brief Read annotation from Annotation folder. - /// \return Status The status code returned. - Status PrepareData() override; - - /// \param[in] image_tree Image tree of json. - /// \param[out] image_vec Image id list of json. - /// \return Status The status code returned. - Status ImageColumnLoad(const nlohmann::json &image_tree, std::vector *image_vec); - - /// \param[in] categories_tree Categories tree of json. - /// \return Status The status code returned. - Status CategoriesColumnLoad(const nlohmann::json &categories_tree); - - /// \param[in] categories_tree Categories tree of json. - /// \param[in] image_file Current image name in annotation. - /// \param[in] id Current unique id of annotation. - /// \return Status The status code returned. - Status DetectionColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, const int32_t &id); - - /// \param[in] categories_tree Categories tree of json. - /// \param[in] image_file Current image name in annotation. - /// \param[in] id Current unique id of annotation. - /// \return Status The status code returned. - Status StuffColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, const int32_t &id); - - /// \param[in] categories_tree Categories tree of json. - /// \param[in] image_file Current image name in annotation. - /// \param[in] id Current unique id of annotation. - /// \return Status The status code returned. - Status KeypointColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, const int32_t &id); - - /// \param[in] categories_tree Categories tree of json. - /// \param[in] image_file Current image name in annotation. - /// \param[in] image_id Current unique id of annotation. - /// \return Status The status code returned. - Status PanopticColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, - const int32_t &image_id); - - /// \brief Function for finding a caption in annotation_tree. - /// \param[in] annotation_tree Annotation tree of json. - /// \param[in] image_file Current image name in annotation. - /// \param[in] id Current unique id of annotation. - /// \return Status The status code returned. - Status CaptionColumnLoad(const nlohmann::json &annotation_tree, const std::string &image_file, const int32_t &id); - - template - Status SearchNodeInJson(const nlohmann::json &input_tree, std::string node_name, T *output_node); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - bool decode_; - std::string image_folder_path_; - std::string annotation_path_; - TaskType task_type_; - std::unique_ptr data_schema_; - bool extra_metadata_; - - std::vector image_ids_; - std::map image_index_; - std::vector>> label_index_; - std::map coordinate_map_; - std::map> simple_item_map_; - std::map> captions_map_; - std::set category_set_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_COCO_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.cc deleted file mode 100644 index 3737f96c0..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.cc +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h" - -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -CoNLL2000Op::CoNLL2000Op(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr schema, const std::vector &conll2000_file_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, total_rows, worker_connector_size, std::move(schema), conll2000_file_list, - op_connector_size, shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void CoNLL2000Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nCoNLL2000 file list:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status CoNLL2000Op::LoadTensor(const std::vector &column, TensorRow *out_row, size_t index) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(column, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -// Function to split string based on a character delimiter. -std::vector CoNLL2000Op::Split(const std::string &s, char delim) { - std::vector res; - std::stringstream ss(s); - std::string item; - - while (getline(ss, item, delim)) { - res.push_back(item); - } - return res; -} - -// Removes excess space before and after the string. -std::string CoNLL2000Op::Strip(const std::string &str) { - std::int64_t strlen = str.size(); - std::int64_t i, j; - i = 0; - while (i < strlen && str[i] == ' ') { - i++; - } - j = strlen - 1; - while (j >= i && str[j] == ' ') { - j--; - } - j++; - if (i == 0 && j == strlen) { - return str; - } else { - return str.substr(i, j - i); - } -} - -Status CoNLL2000Op::Load(const std::vector &word, const std::vector &pos_tag, - const std::vector &chunk_tag, const std::string &file, int32_t worker_id) { - size_t row_line = 3; - TensorRow tRow(row_line, nullptr); - // Add file path info. - std::vector file_path(row_line, file); - tRow.setPath(file_path); - size_t word_index = 0, pos_tag_index = 1, chunk_tag_index = 2; - RETURN_IF_NOT_OK(LoadTensor(word, &tRow, word_index)); - RETURN_IF_NOT_OK(LoadTensor(pos_tag, &tRow, pos_tag_index)); - RETURN_IF_NOT_OK(LoadTensor(chunk_tag, &tRow, chunk_tag_index)); - RETURN_IF_NOT_OK(jagged_rows_connector_->Add(worker_id, std::move(tRow))); - return Status::OK(); -} - -Status CoNLL2000Op::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << DatasetName() << " dataset dir: " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + DatasetName() + " dataset dir: " + file + " does not exist."); - } - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + DatasetName() + ": " + file); - } - int64_t rows_total = 0; - std::string line; - std::vector word_column; - std::vector pos_tag_column; - std::vector chunk_tag_column; - while (getline(handle, line)) { - if (line.empty() && rows_total < start_offset) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - if (word_column.size() != 0) { - Status s = Load(word_column, pos_tag_column, chunk_tag_column, file, worker_id); - if (s.IsError()) { - handle.close(); - return s; - } - } - std::vector().swap(word_column); - std::vector().swap(pos_tag_column); - std::vector().swap(chunk_tag_column); - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - line = Strip(line); - if (line.empty() && rows_total >= start_offset) { - if (word_column.size() != 0) { - Status s = Load(word_column, pos_tag_column, chunk_tag_column, file, worker_id); - if (s.IsError()) { - handle.close(); - return s; - } - } - std::vector().swap(word_column); - std::vector().swap(pos_tag_column); - std::vector().swap(chunk_tag_column); - continue; - } else if (!line.empty() && rows_total >= start_offset) { - std::vector column = Split(line, ' '); - size_t word_index = 0, pos_tag_index = 1, chunk_tag_index = 2; - word_column.push_back(column[word_index]); - pos_tag_column.push_back(column[pos_tag_index]); - chunk_tag_column.push_back(column[chunk_tag_index]); - } - rows_total++; - } - handle.close(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h deleted file mode 100644 index 42b72c1fa..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CONLL2000_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CONLL2000_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -class CoNLL2000Op : public TextFileOp { - public: - /// \Constructor of CoNLL2000Op. - CoNLL2000Op(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, std::unique_ptr, - const std::vector &conll2000_file_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - /// \Default destructor. - ~CoNLL2000Op() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "CoNLL2000Op"; } - - /// \brief brief description DatasetName name getter - /// \param[in] upper Needs to be capitalized or not - /// \return DatasetName of the current Op - std::string DatasetName(bool upper = false) const { return upper ? "CoNLL2000" : "conll2000"; } - - private: - /// \brief Parses a single row and puts the data into multiple TensorRows. - /// \param[in] column The content of the column. - /// \param[in] out_row The tensor table to put the parsed data in. - /// \param[in] index Serial number of column. - /// \return Status The error code returned. - Status LoadTensor(const std::vector &column, TensorRow *out_row, size_t index); - - /// \brief Removes excess space before and after the string. - /// \param[in] str The input string. - /// \return A string. - std::string Strip(const std::string &str); - - /// \brief Split string based on a character delimiter. - /// \param[in] s The input string. - /// \param[in] delim Symbols for separating string. - /// \return A string vector. - std::vector Split(const std::string &s, char delim); - - /// \brief Specify that the corresponding data is translated into Tensor. - /// \param[in] word A list of words in a sentence. - /// \param[in] pos_tag Pos_tag part of speech. - /// \param[in] chunk_tag Chunk_tag part of speech. - /// \param[in] file The file to read. - /// \param[in] worker_id The id of the worker that is executing this function. - /// \return Status The error code returned. - Status Load(const std::vector &word, const std::vector &pos_tag, - const std::vector &chunk_tag, const std::string &file, int32_t worker_id); - - /// \brief Reads a text file and loads the data into multiple TensorRows. - /// \param[in] file The file to read. - /// \param[in] start_offset The start offset of file. - /// \param[in] end_offset The end offset of file. - /// \param[in] worker_id The id of the worker that is executing this function. - /// \return Status The error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_CONLL2000_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.cc deleted file mode 100644 index aa53aa8c6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.cc +++ /dev/null @@ -1,821 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -#include -#include -#include -#include - -#include "utils/file_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { - -CsvOp::CsvOp(const std::vector &csv_files_list, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id) - : NonMappableLeafOp(std::min(num_workers, static_cast(csv_files_list.size())), worker_connector_size, - num_samples, op_connector_size, shuffle_files, num_devices, device_id), - csv_files_list_(std::move(csv_files_list)), - field_delim_(field_delim), - column_default_list_(column_default), - column_name_list_(column_name) {} - -Status CsvOp::Init() { - RETURN_IF_NOT_OK(filename_index_->insert(csv_files_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(csv_files_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - - return Status::OK(); -} - -CsvOp::CsvParser::CsvParser(int32_t worker_id, JaggedConnector *connector, char field_delim, - std::vector> column_default, std::string file_path) - : worker_id_(worker_id), - rows_connector_(connector), - csv_field_delim_(field_delim), - column_default_(std::move(column_default)), - cur_state_(START_OF_FILE), - pos_(0), - cur_col_(0), - total_rows_(0), - start_offset_(0), - end_offset_(std::numeric_limits::max()), - err_message_("unknown"), - file_path_(std::move(file_path)) {} - -void CsvOp::CsvParser::Reset() { - cur_state_ = START_OF_FILE; - pos_ = 0; - cur_col_ = 0; -} - -CsvOp::CsvParser::Message CsvOp::CsvParser::GetMessage(int c) { - if (c == csv_field_delim_) { - return Message::MS_DELIM; - } else if (c == '"') { - return Message::MS_QUOTE; - } else if (c == '\r' || c == '\n') { - return Message::MS_END_OF_LINE; - } else if (c == std::char_traits::eof()) { - return Message::MS_END_OF_FILE; - } else { - return Message::MS_NORMAL; - } -} - -int CsvOp::CsvParser::ProcessMessage(int c) { - Message m = GetMessage(c); - StateDiagram::iterator it = sd.find({cur_state_, m}); - if (it == sd.end()) { - return -1; - } - int ret = it->second.second(*this, c); - cur_state_ = it->second.first; - return ret; -} - -int CsvOp::CsvParser::PutChar(int c) { - if (pos_ >= str_buf_.size()) { - str_buf_.resize(str_buf_.size() * 2); - } - str_buf_[pos_] = c; - pos_++; - return 0; -} - -int CsvOp::CsvParser::PutRecord(int c) { - std::string s = std::string(str_buf_.begin(), str_buf_.begin() + pos_); - std::shared_ptr t; - if (cur_col_ >= column_default_.size()) { - std::stringstream ss; - ss << "Invalid columns, the size of column_names should be less than the size of 'column_defaults', " - << "but got the size of column_names: " << cur_col_ - << ", the size of column_defaults : " << column_default_.size() << "."; - err_message_ = ss.str(); - return -1; - } - Status rc; - switch (column_default_[cur_col_]->type) { - case CsvOp::INT: - rc = Tensor::CreateScalar(std::stoi(s), &t); - if (rc.IsError()) { - err_message_ = rc.ToString(); - return -1; - } - break; - case CsvOp::FLOAT: - rc = Tensor::CreateScalar(std::stof(s), &t); - if (rc.IsError()) { - err_message_ = rc.ToString(); - return -1; - } - break; - default: - rc = Tensor::CreateScalar(s, &t); - if (rc.IsError()) { - err_message_ = rc.ToString(); - return -1; - } - break; - } - if (cur_col_ >= cur_row_.size()) { - std::stringstream ss; - ss << "Invalid columns, the size of column_names should be greater than or equal to the size of columns of " - << "loading data, but got the size of column_names: " << cur_col_ - << ", the size of columns in original loaded dataset: " << column_default_.size() << "."; - err_message_ = ss.str(); - return -1; - } - cur_row_[cur_col_] = std::move(t); - pos_ = 0; - cur_col_++; - return 0; -} - -int CsvOp::CsvParser::PutRow(int c) { - if (total_rows_ < start_offset_) { - total_rows_++; - cur_col_ = 0; - return 0; - } - - if (total_rows_ >= end_offset_) { - cur_col_ = 0; - return 0; - } - - int ret = PutRecord(c); - if (ret < 0) { - return ret; - } - - if (cur_col_ != column_default_.size()) { - std::stringstream ss; - ss << "Invalid columns, the size of column_names should be less than the size of 'column_defaults', " - << "but got the size of column_names: " << cur_col_ - << ", the size of 'column_defaults': " << column_default_.size() << "."; - err_message_ = ss.str(); - return -1; - } - - total_rows_++; - cur_col_ = 0; - - Status s = rows_connector_->Add(worker_id_, std::move(cur_row_)); - if (s.IsError()) { - err_message_ = s.ToString(); - // if error type is interrupted, return error code -2 - if (s.StatusCode() == kMDInterrupted) { - constexpr int error_code = -2; - return error_code; - } - return -1; - } - - return 0; -} - -int CsvOp::CsvParser::AddRow(int c) { - total_rows_++; - return 0; -} - -int CsvOp::CsvParser::EndFile(int c) { - if (cur_col_ > 0) { - int ret = PutRow(c); - if (ret < 0) { - return ret; - } - } - return 0; -} - -int CsvOp::CsvParser::CatchException(int c) { - if (GetMessage(c) == Message::MS_QUOTE && cur_state_ == State::UNQUOTE) { - err_message_ = "Invalid csv file, unexpected quote in unquote field from " + file_path_ + "."; - } else if (GetMessage(c) == Message::MS_END_OF_FILE && cur_state_ == State::QUOTE) { - err_message_ = "Invalid csv file, reach the end of file in quote field, check " + file_path_ + "."; - } else if (GetMessage(c) == Message::MS_NORMAL && cur_state_ == State::SECOND_QUOTE) { - err_message_ = "Invalid csv file, receive unquote char in quote field, check " + file_path_ + "."; - } - return -1; -} - -int CsvOp::CsvParser::CountRows(int c) { - Message m; - if (c == '"') { - m = Message::MS_QUOTE; - } else if (c == '\r' || c == '\n' || c == std::char_traits::eof()) { - m = Message::MS_END_OF_LINE; - } else { - m = Message::MS_NORMAL; - } - StateDiagram::iterator it = sdl.find({cur_state_, m}); - if (it == sdl.end()) { - return -1; - } - cur_state_ = it->second.first; - return it->second.second(*this, c); -} - -Status CsvOp::CsvParser::InitCsvParser() { - str_buf_.resize(CSV_BUFFER_SIZE); - InitSDL(); - InitSD(); - return Status::OK(); -} - -void CsvOp::CsvParser::InitSDL() { - // State diagram for counting rows - sdl = {// START_OF_FILE - // |---------------------------------------| - // | abc | " | \n | - // |---------------------------------------| - // | UNQUOTE | QUOTE | START_OF_FILE | - // |---------------------------------------| - // | NullFunc | NullFunc | NullFunc | - // |---------------------------------------| - {{State::START_OF_FILE, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::NullFunc}}, - {{State::START_OF_FILE, Message::MS_QUOTE}, {State::QUOTE, &CsvParser::NullFunc}}, - {{State::START_OF_FILE, Message::MS_END_OF_LINE}, {State::START_OF_FILE, &CsvParser::NullFunc}}, - - // UNQUOTE - // |-------------------------------------| - // | abc | " | \n | - // |-------------------------------------| - // | UNQUOTE | QUOTE | END_OF_LINE | - // |-------------------------------------| - // | NullFunc | NullFunc | AddRow | - // |-------------------------------------| - {{State::UNQUOTE, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::NullFunc}}, - {{State::UNQUOTE, Message::MS_QUOTE}, {State::QUOTE, &CsvParser::NullFunc}}, - {{State::UNQUOTE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::AddRow}}, - - // QUOTE - // |--------------------------------------| - // | abc | " | \n | - // |--------------------------------------| - // | QUOTE | SECOND_QUOTE | QUOTE | - // |--------------------------------------| - // | NullFunc | NullFunc | NullFunc | - // |--------------------------------------| - {{State::QUOTE, Message::MS_NORMAL}, {State::QUOTE, &CsvParser::NullFunc}}, - {{State::QUOTE, Message::MS_QUOTE}, {State::SECOND_QUOTE, &CsvParser::NullFunc}}, - {{State::QUOTE, Message::MS_END_OF_LINE}, {State::QUOTE, &CsvParser::NullFunc}}, - - // SECOND_QUOTE - // |-------------------------------------| - // | abc | " | \n | - // |-------------------------------------| - // | UNQUOTE | QUOTE | END_OF_LINE | - // |-------------------------------------| - // | NullFunc | NullFunc | AddRow | - // |-------------------------------------| - {{State::SECOND_QUOTE, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::NullFunc}}, - {{State::SECOND_QUOTE, Message::MS_QUOTE}, {State::QUOTE, &CsvParser::NullFunc}}, - {{State::SECOND_QUOTE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::AddRow}}, - - // END_OF_LINE - // |-------------------------------------| - // | abc | " | \n | - // |-------------------------------------| - // | UNQUOTE | QUOTE | END_OF_LINE | - // |-------------------------------------| - // | NullFunc | NullFunc | NullFunc | - // |-------------------------------------| - {{State::END_OF_LINE, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::NullFunc}}, - {{State::END_OF_LINE, Message::MS_QUOTE}, {State::QUOTE, &CsvParser::NullFunc}}, - {{State::END_OF_LINE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::NullFunc}}}; -} - -void CsvOp::CsvParser::InitSD() { - // State diagram for CSV parser - sd = {// START_OF_FILE - // |-------------------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |-------------------------------------------------------------------| - // | UNQUOTE | DELIM | QUOTE | START_OF_FILE | END_OF_FILE | - // |-------------------------------------------------------------------| - // | lambda | lambda | lambda | NullFunc | NullFunc | - // |-------------------------------------------------------------------| - {{State::START_OF_FILE, Message::MS_NORMAL}, - {State::UNQUOTE, - [this](CsvParser &, char c) -> int { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - this->str_buf_[0] = c; - this->pos_ = 1; - return 0; - }}}, - {{State::START_OF_FILE, Message::MS_DELIM}, - {State::DELIM, - [this](CsvParser &, char c) -> int { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - return this->PutRecord(c); - }}}, - {{State::START_OF_FILE, Message::MS_QUOTE}, - {State::QUOTE, - [this](CsvParser &, char c) -> int { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - this->pos_ = 0; - return 0; - }}}, - {{State::START_OF_FILE, Message::MS_END_OF_LINE}, {State::START_OF_FILE, &CsvParser::NullFunc}}, - {{State::START_OF_FILE, Message::MS_END_OF_FILE}, {State::END_OF_FILE, &CsvParser::NullFunc}}, - - // UNQUOTE - // |-------------------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |-------------------------------------------------------------------| - // | UNQUOTE | DELIM | EXCEPTION | END_OF_LINE | END_OF_FILE | - // |-------------------------------------------------------------------| - // | PutChar | PutRecord | exception | PutRow | EndFile | - // |-------------------------------------------------------------------| - {{State::UNQUOTE, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::PutChar}}, - {{State::UNQUOTE, Message::MS_DELIM}, {State::DELIM, &CsvParser::PutRecord}}, - {{State::UNQUOTE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::PutRow}}, - {{State::UNQUOTE, Message::MS_END_OF_FILE}, {State::END_OF_FILE, &CsvParser::EndFile}}, - // UNQUOTE-Exception - {{State::UNQUOTE, Message::MS_QUOTE}, {State::EXCEPTION, &CsvParser::CatchException}}, - - // DELIM - // |-------------------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |-------------------------------------------------------------------| - // | UNQUOTE | DELIM | QUOTE | END_OF_LINE | END_OF_FILE | - // |-------------------------------------------------------------------| - // | PutChar | PutRecord | lambda | PutRow | EndFile | - // |-------------------------------------------------------------------| - {{State::DELIM, Message::MS_NORMAL}, {State::UNQUOTE, &CsvParser::PutChar}}, - {{State::DELIM, Message::MS_DELIM}, {State::DELIM, &CsvParser::PutRecord}}, - {{State::DELIM, Message::MS_QUOTE}, - {State::QUOTE, - [this](CsvParser &, char c) -> int { - this->pos_ = 0; - return 0; - }}}, - {{State::DELIM, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::PutRow}}, - {{State::DELIM, Message::MS_END_OF_FILE}, {State::END_OF_FILE, &CsvParser::EndFile}}, - - // QUOTE - // |-----------------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |-----------------------------------------------------------------| - // | QUOTE | QUOTE | SECOND_QUOTE | QUOTE | EXCEPTION | - // |-----------------------------------------------------------------| - // | PutChar | PutChar | NullFunc | PutChar | exception | - // |-----------------------------------------------------------------| - {{State::QUOTE, Message::MS_NORMAL}, {State::QUOTE, &CsvParser::PutChar}}, - {{State::QUOTE, Message::MS_DELIM}, {State::QUOTE, &CsvParser::PutChar}}, - {{State::QUOTE, Message::MS_QUOTE}, {State::SECOND_QUOTE, &CsvParser::NullFunc}}, - {{State::QUOTE, Message::MS_END_OF_LINE}, {State::QUOTE, &CsvParser::PutChar}}, - // QUOTE-Exception - {{State::QUOTE, Message::MS_END_OF_FILE}, {State::EXCEPTION, &CsvParser::CatchException}}, - - // SECOND_QUOTE - // |------------------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |------------------------------------------------------------------| - // | EXCEPTION | DELIM | QUOTE | END_OF_LINE | END_OF_FILE | - // |------------------------------------------------------------------| - // | exception | PutRecord | PutChar | PutRow | EndFile | - // |------------------------------------------------------------------| - {{State::SECOND_QUOTE, Message::MS_QUOTE}, {State::QUOTE, &CsvParser::PutChar}}, - {{State::SECOND_QUOTE, Message::MS_DELIM}, {State::DELIM, &CsvParser::PutRecord}}, - {{State::SECOND_QUOTE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::PutRow}}, - {{State::SECOND_QUOTE, Message::MS_END_OF_FILE}, {State::END_OF_FILE, &CsvParser::EndFile}}, - // SECOND_QUOTE-Exception - {{State::SECOND_QUOTE, Message::MS_NORMAL}, {State::EXCEPTION, &CsvParser::CatchException}}, - - // END_OF_LINE - // |-------------------------------------------------------| - // | abc | , | " | \n | EOF | - // |-------------------------------------------------------| - // | UNQUOTE | DELIM | QUOTE | END_OF_LINE | END_OF_FILE | - // |-------------------------------------------------------| - // | lambda | lambda | lambda | NullFunc | EndFile | - // |-------------------------------------------------------| - {{State::END_OF_LINE, Message::MS_NORMAL}, - {State::UNQUOTE, - [this](CsvParser &, char c) -> int { - if (this->total_rows_ > this->start_offset_ && this->total_rows_ <= this->end_offset_) { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - } - this->str_buf_[0] = c; - this->pos_ = 1; - return 0; - }}}, - {{State::END_OF_LINE, Message::MS_DELIM}, - {State::DELIM, - [this](CsvParser &, char c) -> int { - if (this->total_rows_ > this->start_offset_ && this->total_rows_ <= this->end_offset_) { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - } - return this->PutRecord(c); - }}}, - {{State::END_OF_LINE, Message::MS_QUOTE}, - {State::QUOTE, - [this](CsvParser &, char c) -> int { - if (this->total_rows_ > this->start_offset_ && this->total_rows_ <= this->end_offset_) { - TensorRow row(column_default_.size(), nullptr); - std::vector file_path(column_default_.size(), file_path_); - row.setPath(file_path); - this->cur_row_ = std::move(row); - } - return 0; - }}}, - {{State::END_OF_LINE, Message::MS_END_OF_LINE}, {State::END_OF_LINE, &CsvParser::NullFunc}}, - {{State::END_OF_LINE, Message::MS_END_OF_FILE}, {State::END_OF_FILE, &CsvParser::EndFile}}}; -} - -Status CsvOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - CsvParser csv_parser(worker_id, jagged_rows_connector_.get(), field_delim_, column_default_list_, file); - RETURN_IF_NOT_OK(csv_parser.InitCsvParser()); - csv_parser.SetStartOffset(start_offset); - csv_parser.SetEndOffset(end_offset); - - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file + " does not exist."); - } - - std::ifstream ifs; - ifs.open(realpath.value(), std::ifstream::in); - if (!ifs.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + file + ", the file is damaged or permission denied."); - } - if (column_name_list_.empty()) { - std::string tmp; - getline(ifs, tmp); - } - csv_parser.Reset(); - try { - while (ifs.good()) { - // when ifstream reaches the end of file, the function get() return std::char_traits::eof() - // which is a 32-bit -1, it's not equal to the 8-bit -1 on Euler OS. So instead of char, we use - // int to receive its return value. - int chr = ifs.get(); - int err = csv_parser.ProcessMessage(chr); - if (err != 0) { - // if error code is -2, the returned error is interrupted - if (err == -2) { - ifs.close(); - return Status(kMDInterrupted); - } - ifs.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse csv file: " + file + " at line " + - std::to_string(csv_parser.GetTotalRows() + 1) + - ". Error message: " + csv_parser.GetErrorMessage()); - } - } - } catch (std::invalid_argument &ia) { - ifs.close(); - std::string err_row = std::to_string(csv_parser.GetTotalRows() + 1); - RETURN_STATUS_UNEXPECTED("Invalid csv, csv file: " + file + " parse failed at line " + err_row + - ", type does not match."); - } catch (std::out_of_range &oor) { - ifs.close(); - std::string err_row = std::to_string(csv_parser.GetTotalRows() + 1); - RETURN_STATUS_UNEXPECTED("Invalid csv, " + file + " parse failed at line " + err_row + " : value out of range."); - } - ifs.close(); - return Status::OK(); -} - -// A print method typically used for debugging -void CsvOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\n" - << DatasetName(true) << " files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} - -Status CsvOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -Status CsvOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count = CountTotalRows(it.value()); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (int i = 0; i < csv_files_list_.size(); ++i) { - ss << " " << csv_files_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + file_list + "."); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -int64_t CsvOp::CountTotalRows(const std::string &file) { - CsvParser csv_parser(0, jagged_rows_connector_.get(), field_delim_, column_default_list_, file); - Status rc = csv_parser.InitCsvParser(); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR], failed to initialize " + DatasetName(true) + " Parser. Error description:" - << rc; - return 0; - } - - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, csv file: " << file << " does not exist."; - return 0; - } - - std::ifstream ifs; - ifs.open(realpath.value(), std::ifstream::in); - if (!ifs.is_open()) { - return 0; - } - if (column_name_list_.empty()) { - std::string tmp; - getline(ifs, tmp); - } - csv_parser.Reset(); - while (ifs.good()) { - int chr = ifs.get(); - if (csv_parser.CountRows(chr) != 0) { - break; - } - } - ifs.close(); - - return csv_parser.GetTotalRows(); -} - -Status CsvOp::CountAllFileRows(const std::vector &files, bool csv_header, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - int32_t num_workers = GlobalContext::config_manager()->num_parallel_workers(); - int32_t op_connector_size = GlobalContext::config_manager()->op_connector_size(); - int32_t worker_connector_size = GlobalContext::config_manager()->worker_connector_size(); - const int32_t device_id = 0; - const int32_t num_devices = 1; - const int64_t num_samples = 0; - bool shuffle_files = false; - std::vector> column_list; - std::vector column_name_list; - char field_delim = ','; - std::shared_ptr op; - *count = 0; - if (!csv_header) { - (void)column_name_list.emplace_back(""); - } - op = std::make_shared(files, field_delim, column_list, column_name_list, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id); - RETURN_IF_NOT_OK(op->Init()); - for (auto file : files) { - *count += op->CountTotalRows(file); - } - return Status::OK(); -} - -std::vector CsvOp::split(const std::string &s, char delim) { - std::vector res; - std::stringstream ss(s); - std::string item; - - while (getline(ss, item, delim)) { - res.push_back(item); - } - return res; -} - -Status CsvOp::ComputeColMap() { - // Set the column name mapping (base class field) - if (column_name_id_map_.empty()) { - if (!ColumnNameValidate()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to obtain column name from input " + DatasetName() + - " file list."); - } - - for (auto &csv_file : csv_files_list_) { - Status rc = ColMapAnalyse(csv_file); - - /* Process exception if ERROR in column name solving */ - if (!rc.IsOk()) { - MS_LOG(ERROR) << "Invalid file, failed to get column name list from csv file: " + csv_file; - RETURN_STATUS_UNEXPECTED("Invalid file, failed to get column name list from csv file: " + csv_file); - } - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - - if (column_default_list_.size() < column_name_id_map_.size()) { - for (int32_t i = column_default_list_.size(); i < column_name_id_map_.size(); i++) { - column_default_list_.push_back(std::make_shared>(CsvOp::STRING, "")); - } - } - - if (column_default_list_.size() != column_name_id_map_.size()) { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, the size of column_names should be equal to the size of 'column_defaults', but got " - " size of 'column_defaults': " + - std::to_string(column_default_list_.size()) + - ", size of column_names: " + std::to_string(column_name_id_map_.size())); - } - - return Status::OK(); -} - -Status CsvOp::ColMapAnalyse(const std::string &csv_file_name) { - if (column_name_list_.empty()) { - // Actually we only deal with the first file, because the column name set in other files must remain the same - if (!check_flag_) { - auto realpath = FileUtils::GetRealPath(csv_file_name.c_str()); - if (!realpath.has_value()) { - std::string err_msg = "Invalid file path, csv file: " + csv_file_name + " does not exist."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::string line; - std::ifstream handle(realpath.value(), std::ios::in); - - getline(handle, line); - std::vector col_names = split(line, field_delim_); - - for (int32_t i = 0; i < col_names.size(); i++) { - // consider the case of CRLF on windows - col_names[i].erase(col_names[i].find_last_not_of('\r') + 1); - - if (column_name_id_map_.find(col_names[i]) == column_name_id_map_.end()) { - column_name_id_map_[col_names[i]] = i; - } else { - handle.close(); - MS_LOG(ERROR) << "Invalid parameter, duplicate column " << col_names[i] << " for csv file: " << csv_file_name; - RETURN_STATUS_UNEXPECTED("Invalid parameter, duplicate column " + col_names[i] + - " for csv file: " + csv_file_name); - } - } - handle.close(); - check_flag_ = true; - } - } else { - if (!check_flag_) { // Case the first CSV file, validate the column names - for (int32_t i = 0; i < column_name_list_.size(); ++i) { - if (column_name_id_map_.find(column_name_list_[i]) == column_name_id_map_.end()) { - column_name_id_map_[column_name_list_[i]] = i; - } else { - MS_LOG(ERROR) << "Invalid parameter, duplicate column " << column_name_list_[i] - << " for csv file: " << csv_file_name << "."; - RETURN_STATUS_UNEXPECTED("Invalid parameter, duplicate column " + column_name_list_[i] + - " for csv file: " + csv_file_name + "."); - } - } - check_flag_ = true; - } - } - return Status::OK(); -} - -bool CsvOp::ColumnNameValidate() { - /* Case 1: Users specify the column_names */ - if (!column_name_list_.empty()) { - return true; - } - - /* Case 2: Inferring the column_names from the first row of CSV files - \\ record: the column name set in first CSV file. - \\ match_file: First file same */ - std::vector record; - std::string match_file; - - for (auto &csv_file : csv_files_list_) { - auto realpath = FileUtils::GetRealPath(csv_file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, csv file: " << csv_file << " does not exist."; - return false; - } - - std::string line; - std::ifstream handle(realpath.value(), std::ios::in); - - // Parse the csv_file into column name set - getline(handle, line); - std::vector col_names = split(line, field_delim_); - - /* Analyse the column name and draw a conclusion */ - if (record.empty()) { // Case the first file - record = col_names; - match_file = csv_file; - } else { // Case the other files - if (col_names != record) { - handle.close(); - MS_LOG(ERROR) << "Invalid parameter, every column name should be equal the record from csv, but got column: " - << col_names << ", csv record: " << record << ". Check " + match_file + " and " + csv_file + "."; - return false; - } - } - handle.close(); - } - return true; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h deleted file mode 100644 index 783968b93..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h +++ /dev/null @@ -1,246 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef DATASET_ENGINE_DATASETOPS_SOURCE_CSV_OP_H_ -#define DATASET_ENGINE_DATASETOPS_SOURCE_CSV_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/auto_index.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { - -const size_t CSV_BUFFER_SIZE = 4096; -using StringIndex = AutoIndexObj; -class JaggedConnector; - -class CsvOp : public NonMappableLeafOp { - public: - enum RecordType : uint8_t { INT = 0, FLOAT, STRING }; - - struct BaseRecord { - public: - BaseRecord() = default; - explicit BaseRecord(RecordType t) : type(t) {} - virtual ~BaseRecord() {} - RecordType type; - }; - - template - class Record : public BaseRecord { - public: - Record() = default; - Record(RecordType t, T v) : BaseRecord(t), value(v) {} - ~Record() {} - T value; - }; - - /// CsvParser is a class that parsing CSV file. - /// We design a state machine to implement CSV syntactic analysis. It contains two state diagram,'sd' and 'sdl'. - /// The 'sd' is used for parsing CSV syntactic, it's complete and complicate. - /// The 'sdl' is used for counting the record rows, it's concise and it runs fast. - struct CsvParser { - public: - CsvParser() = delete; - - CsvParser(int32_t worker_id, JaggedConnector *connector, char field_delim, - std::vector> column_default, std::string file_path); - - ~CsvParser() = default; - - void Reset(); - - void SetStartOffset(int64_t start_offset) { start_offset_ = start_offset; } - - void SetEndOffset(int64_t end_offset) { end_offset_ = end_offset; } - - int ProcessMessage(int c); - - int CountRows(int c); - - Status InitCsvParser(); - - int64_t GetTotalRows() { return total_rows_; } - - std::string GetErrorMessage() { return err_message_; } - - private: - enum State : uint8_t { - START_OF_FILE = 0, - UNQUOTE, - DELIM, - QUOTE, - SECOND_QUOTE, - END_OF_LINE, - END_OF_FILE, - EXCEPTION - }; - - enum Message : uint8_t { - MS_NORMAL = 0, - MS_DELIM, - MS_QUOTE, - MS_END_OF_LINE, - MS_END_OF_FILE, - }; - - typedef std::pair StateMessagePair; - typedef std::pair> StateActionPair; - typedef std::map StateDiagram; - - Message GetMessage(int c); - - int NullFunc(int c) { return 0; } - - int PutChar(int c); - - int PutRecord(int c); - - int PutRow(int c); - - int EndFile(int c); - - int AddRow(int c); - - int CatchException(int c); - - void InitSDL(); - - void InitSD(); - - int32_t worker_id_; - JaggedConnector *rows_connector_; - const char csv_field_delim_; - std::vector> column_default_; - State cur_state_; - size_t pos_; - int cur_col_; - int64_t total_rows_; - int64_t start_offset_; - int64_t end_offset_; - StateDiagram sd; - StateDiagram sdl; - std::vector str_buf_; - TensorRow cur_row_; - std::string err_message_; - std::string file_path_; - }; - - /// Constructor of CsvOp - CsvOp() = delete; - - CsvOp(const std::vector &csv_files_list, char field_delim, - const std::vector> &column_default, const std::vector &column_name, - int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id); - - /// Default destructor - ~CsvOp() = default; - - /// A print method typically used for debugging - /// @param out - The output stream to write output to - /// @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // Instantiates the internal queues and connectors - // @return Status - the error code returned - Status Init() override; - - /// Get total rows in files. - /// @param files - all csv files. - /// @param csv_header - a bool that indicates csv file include header line - /// @param count - number of rows. - /// @return Status - the error coed returned. - static Status CountAllFileRows(const std::vector &files, bool csv_header, int64_t *count); - - /// File names getter - /// @return Vector of the input file names - std::vector FileNames() { return csv_files_list_; } - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "CSVOp"; } - - // DatasetName name getter - // \return DatasetName of the current Op - virtual std::string DatasetName(bool upper = false) const { return upper ? "CSV" : "csv"; } - - protected: - // Parses a single row and puts the data into a tensor table. - // @param line - the content of the row. - // @param tensor_table - the tensor table to put the parsed data in. - // @param row - the id of the row filled in the tensor table. - // @return Status - the error code returned. - Status LoadTensor(const std::string &line, std::unique_ptr *tensor_table, int64_t row); - - // Reads a csv file and loads the data into multiple tensors. - // @param file - the file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - // Fill the IOBlockQueue. - // @para i_keys - keys of file to fill to the IOBlockQueue - // @return Status - the error code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - Status CalculateNumRowsPerShard() override; - - /// Count number of rows in each file. - /// @param filename - csv file name. - /// @return int64_t - the total number of rows in file. - int64_t CountTotalRows(const std::string &file); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - /// Split string based on a character delimiter - /// @param str - the input string - /// @param str - the delimiter - /// @return - the a string vector - virtual std::vector split(const std::string &s, char delim); - - // Private function for analysing the column name in every CSV file - // @return - Status - Status ColMapAnalyse(const std::string &csv_file_name); - - // Private function for validating whether the column name set in every CSV file remain the same - // @return bool - whether column name identical in all CSV files - bool ColumnNameValidate(); - - std::vector csv_files_list_; - char field_delim_; - std::vector> column_default_list_; - std::vector column_name_list_; - bool check_flag_ = false; -}; -} // namespace dataset -} // namespace mindspore -#endif // DATASET_ENGINE_DATASETOPS_SOURCE_CSV_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.cc deleted file mode 100644 index ce04c80f4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.cc +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h" - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -DBpediaOp::DBpediaOp(const std::vector &dataset_files_list, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id) - : CsvOp(dataset_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id) {} - -void DBpediaOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nDBpedia files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h deleted file mode 100644 index 4b6a15907..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DBPEDIA_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DBPEDIA_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class DBpediaOp : public CsvOp { - public: - /// \brief Constructor. - /// \param[in] dataset_files_list - List of file paths for the dataset files. - /// \param[in] field_delim - A char that indicates the delimiter to separate fields. - /// \param[in] column_default - List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name - List of column names of the dataset file. - /// \param[in] num_workers - Num of workers reading files in parallel. - /// \param[in] num_samples - The number of samples to be included in the dataset. - /// \param[in] worker_connector_size - Size of each internal queue. - /// \param[in] op_connector_size - Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files - Whether or not to shuffle the files before reading data. - /// \param[in] num_devices - Number of devices that the dataset should be divided into. - /// \param[in] device_id - The device ID within num_devices. - DBpediaOp(const std::vector &dataset_files_list, char field_delim, - const std::vector> &column_default, const std::vector &column_name, - int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id); - - /// \brief Destructor. - ~DBpediaOp() = default; - - /// A print method typically used for debugging - /// @param out - The output stream to write output to - /// @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "DBpedia" : "dbpedia"; } - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "DBpediaOp"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DBPEDIA_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.cc deleted file mode 100644 index 6c1f47e43..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.cc +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const std::map DatasetPramMap = {{"train_hr", "DIV2K_train_HR"}, - {"valid_hr", "DIV2K_valid_HR"}, - {"train_bicubic_x2", "DIV2K_train_LR_bicubic"}, - {"train_unknown_x2", "DIV2K_train_LR_unknown"}, - {"valid_bicubic_x2", "DIV2K_valid_LR_bicubic"}, - {"valid_unknown_x2", "DIV2K_valid_LR_unknown"}, - {"train_bicubic_x3", "DIV2K_train_LR_bicubic"}, - {"train_unknown_x3", "DIV2K_train_LR_unknown"}, - {"valid_bicubic_x3", "DIV2K_valid_LR_bicubic"}, - {"valid_unknown_x3", "DIV2K_valid_LR_unknown"}, - {"train_bicubic_x4", "DIV2K_train_LR_bicubic"}, - {"train_unknown_x4", "DIV2K_train_LR_unknown"}, - {"valid_bicubic_x4", "DIV2K_valid_LR_bicubic"}, - {"valid_unknown_x4", "DIV2K_valid_LR_unknown"}, - {"train_bicubic_x8", "DIV2K_train_LR_x8"}, - {"valid_bicubic_x8", "DIV2K_valid_LR_x8"}, - {"train_mild_x4", "DIV2K_train_LR_mild"}, - {"valid_mild_x4", "DIV2K_valid_LR_mild"}, - {"train_difficult_x4", "DIV2K_train_LR_difficult"}, - {"valid_difficult_x4", "DIV2K_valid_LR_difficult"}, - {"train_wild_x4", "DIV2K_train_LR_wild"}, - {"valid_wild_x4", "DIV2K_valid_LR_wild"}}; - -DIV2KOp::DIV2KOp(int32_t num_workers, const std::string &dataset_dir, const std::string &usage, - const std::string &downgrade, int32_t scale, bool decode, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - usage_(usage), - downgrade_(downgrade), - scale_(scale), - decode_(decode), - data_schema_(std::move(data_schema)) {} - -// Load 1 TensorRow (hr_image, lr_image) using 1 ImageLabelPair. 1 function call produces 1 TensorTow. -Status DIV2KOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair data = image_hr_lr_pairs_[static_cast(row_id)]; - std::shared_ptr hr_image; - std::shared_ptr lr_image; - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.first, &hr_image)); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.second, &lr_image)); - - if (decode_ == true) { - Status hr_rc = Decode(hr_image, &hr_image); - if (hr_rc.IsError()) { - std::string err = - "Invalid image, failed to decode " + data.first + ", the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - - Status lr_rc = Decode(lr_image, &lr_image); - if (lr_rc.IsError()) { - std::string err = - "Invalid image, failed to decode " + data.second + ", the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(hr_image), std::move(lr_image)}); - trow->setPath({data.first, data.second}); - return Status::OK(); -} - -void DIV2KOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nDIV2K DatasetDir: " << dataset_dir_ << "\nUsage: " << usage_ - << "\nScale: " << scale_ << "\nDowngrade: " << downgrade_ << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status DIV2KOp::PrepareData() { - std::string hr_dir_key; - std::string lr_dir_key; - - if (usage_ == "all") { - std::vector usage_all = {"train", "valid"}; - for (auto &item : usage_all) { - hr_dir_key = item + "_hr"; - lr_dir_key = item + "_" + downgrade_ + "_x" + std::to_string(scale_); - RETURN_IF_NOT_OK(GetDIV2KLRDirRealName(hr_dir_key, lr_dir_key)); - RETURN_IF_NOT_OK(GetDIV2KDataByUsage()); - } - } else { - hr_dir_key = usage_ + "_hr"; - lr_dir_key = usage_ + "_" + downgrade_ + "_x" + std::to_string(scale_); - RETURN_IF_NOT_OK(GetDIV2KLRDirRealName(hr_dir_key, lr_dir_key)); - RETURN_IF_NOT_OK(GetDIV2KDataByUsage()); - } - RETURN_IF_NOT_OK(CountDatasetInfo()); // Count the total rows - return Status::OK(); -} - -Status DIV2KOp::GetDIV2KLRDirRealName(const std::string &hr_dir_key, const std::string &lr_dir_key) { - std::set downgrade_2017 = {"bicubic", "unknown"}; - std::set scale_2017 = {2, 3, 4}; - - hr_dir_real_name_ = DatasetPramMap.find(hr_dir_key)->second; - auto lr_it = DatasetPramMap.find(lr_dir_key); - if (lr_it == DatasetPramMap.end()) { - std::string out_str = "{\n"; - std::for_each(DatasetPramMap.begin(), DatasetPramMap.end(), - [&out_str](const std::pair &item) -> void { - out_str += ("\t" + item.first + ": " + item.second + ",\n"); - }); - out_str += "\n}"; - RETURN_STATUS_UNEXPECTED("Invalid param, dir: " + lr_dir_key + " not found under div2k dataset dir, " + out_str); - } - - if (downgrade_2017.find(downgrade_) != downgrade_2017.end() && scale_2017.find(scale_) != scale_2017.end()) { - Path ntire_2017(lr_it->second); - lr_dir_real_name_ = (ntire_2017 / ("X" + std::to_string(scale_))).ToString(); - } else { - lr_dir_real_name_ = lr_it->second; - } - return Status::OK(); -} - -Status DIV2KOp::GetDIV2KDataByUsage() { - const std::string kExtension = ".png"; - - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_dataset_dir.has_value()) { - MS_LOG(ERROR) << "Invalid file path, div2k dataset dir: " << dataset_dir_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, div2k dataset dir: " + dataset_dir_ + " does not exist."); - } - - Path dataset_dir(real_dataset_dir.value()); - Path hr_images_dir = dataset_dir / hr_dir_real_name_; - Path lr_images_dir = dataset_dir / lr_dir_real_name_; - - if (!hr_images_dir.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid path, div2k hr image dir: " + hr_images_dir.ToString() + " is not a directory."); - } - if (!lr_images_dir.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid path, div2k lr image dir: " + lr_images_dir.ToString() + " is not a directory."); - } - auto hr_it = Path::DirIterator::OpenDirectory(&hr_images_dir); - if (hr_it == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to open div2k hr image dir: " + hr_images_dir.ToString() + - ", permission denied."); - } - - std::string image_name; - std::string image_id_scale; - std::string lr_image_file_path_; - std::map image_hr_lr_map_; - std::map downgrade_2018 = {{"mild", "m"}, {"difficult", "d"}, {"wild", "w"}}; - - while (hr_it->HasNext()) { - try { - Path hr_img_file = hr_it->Next(); - if (hr_img_file.Extension() != kExtension) { - continue; - } - - image_name = hr_img_file.Basename(); - image_id_scale = image_name.substr(0, image_name.find_last_of(".")) + "x" + std::to_string(scale_); - Path hr_image_file_path = hr_images_dir / image_name; - auto lr_it = downgrade_2018.find(downgrade_); - if (lr_it != downgrade_2018.end()) { - lr_image_file_path_ = (lr_images_dir / (image_id_scale + lr_it->second + kExtension)).ToString(); - } else { - lr_image_file_path_ = (lr_images_dir / (image_id_scale + kExtension)).ToString(); - } - - Path lr_image_file_path(lr_image_file_path_); - if (!lr_image_file_path.Exists()) { - RETURN_STATUS_UNEXPECTED("Invalid file, div2k image file: " + lr_image_file_path.ToString() + - " does not exist."); - } - - image_hr_lr_map_[hr_image_file_path.ToString()] = lr_image_file_path.ToString(); - } catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to load DIV2K Dataset from " + dataset_dir_ + ": " + - std::string(err.what())); - } - } - for (auto item : image_hr_lr_map_) { - image_hr_lr_pairs_.emplace_back(std::make_pair(item.first, item.second)); - } - return Status::OK(); -} - -Status DIV2KOp::CountDatasetInfo() { - num_rows_ = static_cast(image_hr_lr_pairs_.size()); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API 'DIV2KDataset'. Please check dataset API or file path: " + - dataset_dir_ + "."); - } - return Status::OK(); -} - -Status DIV2KOp::CountTotalRows(const std::string &dir, const std::string &usage, const std::string &downgrade, - int32_t scale, int64_t *count) { - // the logic of counting the number of samples is copied from PrepareData() - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - // build a new unique schema object - auto new_schema = std::make_unique(); - RETURN_IF_NOT_OK(new_schema->AddColumn(ColDescriptor("hr_image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("lr_image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 0, &scalar))); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - std::shared_ptr op = std::make_shared( - num_workers, dir, usage, downgrade, scale, false, op_connect_size, std::move(new_schema), std::move(new_sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = static_cast(op->image_hr_lr_pairs_.size()); - return Status::OK(); -} - -Status DIV2KOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h deleted file mode 100644 index d4850ac92..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DIV2K_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DIV2K_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class DIV2KOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] int32_t num_workers - num of workers reading images in parallel. - /// \param[in] std::string dataset_dir - dir directory of DIV2K dataset. - /// \param[in] std::string usage - the type of dataset. Acceptable usages include "train", "valid" or "all". - /// \param[in] std::string downgrade - the mode of downgrade. Acceptable downgrades include "bicubic", "unknown", - /// "mild", "difficult" or "wild". - /// \param[in] int32_t scale - the scale of downgrade. Acceptable scales include 2, 3, 4 or 8. - /// \param[in] bool decode - decode the images after reading. - /// \param[in] int32_t queue_size - connector queue size. - /// \param[in] DataSchema data_schema - the schema of each column in output data. - /// \param[in] std::unique_ptr sampler - sampler tells ImageFolderOp what to read. - DIV2KOp(int32_t num_workers, const std::string &dataset_dir, const std::string &usage, const std::string &downgrade, - int32_t scale, bool decode, int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler); - - /// \brief Destructor. - ~DIV2KOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out - /// \param[in] show_all - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the DIV2K dataset. - /// \param[in] dir - path to the DIV2K directory. - /// \param[in] usage - the type of dataset. Acceptable usages include "train", "valid" or "all". - /// \param[in] downgrade - the mode of downgrade. Acceptable downgrades include "bicubic", "unknown", - /// "mild", "difficult" or "wild". - /// \param[in] scale - the scale of downgrade. Acceptable scales include 2, 3, 4 or 8. - /// \param[out] count - output arg that will hold the actual dataset size. - /// \return Status - The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, const std::string &downgrade, - int32_t scale, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "DIV2KOp"; } - - protected: - /// \brief Parse DIV2K data. - /// \return Status - The status code returned. - Status PrepareData() override; - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] uint64_t index - index need to load. - /// \param[out] TensorRow row - image & label read into this tensor row. - /// \return Status - The status code returned. - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - /// \brief Get the real name of high resolution images and low resolution images dir in DIV2K dataset. - /// \param[in] hr_dir_key - the key of high resolution images dir. - /// \param[in] lr_dir_key - the key of high resolution images dir. - /// \return Status - The status code returned. - Status GetDIV2KLRDirRealName(const std::string &hr_dir_key, const std::string &lr_dir_key); - - /// \brief Get DIV2K data by usage. - /// \return Status - The status code returned. - Status GetDIV2KDataByUsage(); - - /// \brief Count label index,num rows and num samples. - /// \return Status - The status code returned. - Status CountDatasetInfo(); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status - The status code returned. - Status ComputeColMap() override; - - std::string dataset_dir_; - std::string usage_; - int32_t scale_; - std::string downgrade_; - bool decode_; - std::unique_ptr data_schema_; - - std::vector> image_hr_lr_pairs_; - std::string hr_dir_real_name_; - std::string lr_dir_real_name_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_DIV2K_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.cc deleted file mode 100644 index 5432784d6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.cc +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h" - -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -EMnistOp::EMnistOp(const std::string &name, const std::string &usage, int32_t num_workers, - const std::string &folder_path, int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MnistOp(usage, num_workers, folder_path, queue_size, std::move(data_schema), std::move(sampler)), name_(name) {} - -void EMnistOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows:" << num_rows_ << "\n" - << DatasetName(true) << " directory: " << folder_path_ << "\nName: " << name_ << "\nUsage: " << usage_ - << "\n\n"; - } -} - -Status EMnistOp::WalkAllFiles() { - const std::string img_ext = "-images-idx3-ubyte"; - const std::string lbl_ext = "-labels-idx1-ubyte"; - const std::string train_prefix = "-train"; - const std::string test_prefix = "-test"; - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(realpath.has_value(), "Invalid file path, " + folder_path_ + " does not exist."); - Path dir(realpath.value()); - auto dir_it = Path::DirIterator::OpenDirectory(&dir); - if (dir_it == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid path, failed to open emnist dataset dir: " + dir.ToString() + - ", the directory is not a directory or permission denied."); - } - std::string prefix; - prefix = "emnist-" + name_; // used to match usage == "all". - if (usage_ == "train" || usage_ == "test") { - prefix += (usage_ == "test" ? test_prefix : train_prefix); - } - if (dir_it != nullptr) { - while (dir_it->HasNext()) { - Path file = dir_it->Next(); - std::string fname = file.Basename(); // name of the emnist file. - if ((fname.find(prefix) != std::string::npos) && (fname.find(img_ext) != std::string::npos)) { - image_names_.push_back(file.ToString()); - MS_LOG(INFO) << DatasetName(true) << " operator found image file at " << fname << "."; - } else if ((fname.find(prefix) != std::string::npos) && (fname.find(lbl_ext) != std::string::npos)) { - label_names_.push_back(file.ToString()); - MS_LOG(INFO) << DatasetName(true) << " operator found label file at " << fname << "."; - } - } - } else { - MS_LOG(WARNING) << DatasetName(true) << " operator unable to open directory " << dir.ToString() << "."; - } - - std::sort(image_names_.begin(), image_names_.end()); - std::sort(label_names_.begin(), label_names_.end()); - CHECK_FAIL_RETURN_UNEXPECTED(image_names_.size() == label_names_.size(), - "Invalid data, num of image files should be equal to num of label files under " + - realpath.value() + ", but got num of images: " + std::to_string(image_names_.size()) + - ", num of labels: " + std::to_string(label_names_.size()) + "."); - - return Status::OK(); -} - -Status EMnistOp::CountTotalRows(const std::string &dir, const std::string &name, const std::string &usage, - int64_t *count) { - // the logic of counting the number of samples is copied from ParseEMnistData() and uses CheckReader(). - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = - std::make_shared(name, usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - - RETURN_IF_NOT_OK(op->WalkAllFiles()); - - for (size_t i = 0; i < op->image_names_.size(); ++i) { - std::ifstream image_reader; - image_reader.open(op->image_names_[i], std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(image_reader.is_open(), "Invalid file, failed to open " + op->image_names_[i] + - ": the image file is damaged or permission denied."); - std::ifstream label_reader; - label_reader.open(op->label_names_[i], std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(label_reader.is_open(), "Invalid file, failed to open " + op->label_names_[i] + - ": the label file is damaged or permission denied."); - uint32_t num_images; - Status s = op->CheckImage(op->image_names_[i], &image_reader, &num_images); - image_reader.close(); - RETURN_IF_NOT_OK(s); - - uint32_t num_labels; - s = op->CheckLabel(op->label_names_[i], &label_reader, &num_labels); - label_reader.close(); - RETURN_IF_NOT_OK(s); - - CHECK_FAIL_RETURN_UNEXPECTED( - (num_images == num_labels), - "Invalid data, num of images should be equal to num of labels, but got num of images: " + - std::to_string(num_images) + ", num of labels: " + std::to_string(num_labels) + "."); - *count = *count + num_images; - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h deleted file mode 100644 index 559791b54..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EMNIST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EMNIST_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -class EMnistOp : public MnistOp { - public: - // Constructor. - // @param const std::string &name - Class of this dataset, can be - // "byclass","bymerge","balanced","letters","digits","mnist". - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all'. - // @param int32_t num_workers - Number of workers reading images in parallel. - // @param const std::string &folder_path - Dir directory of emnist. - // @param int32_t queue_size - Connector queue size. - // @param std::unique_ptr data_schema - The schema of the Emnist dataset. - // @param std::shared_ptr sampler - Sampler tells EMnistOp what to read. - EMnistOp(const std::string &name, const std::string &usage, int32_t num_workers, const std::string &folder_path, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler); - - // Destructor. - ~EMnistOp() = default; - - // A print method typically used for debugging. - // @param std::ostream &out - Out stream. - // @param bool show_all - Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the EMNIST dataset. - // @param const std::string &dir - Path to the EMNIST directory. - // @param const std::string &name - Class of this dataset, can be - // "byclass","bymerge","balanced","letters","digits","mnist". - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all'. - // @param int64_t *count - Output arg that will hold the minimum of the actual dataset size and numSamples. - // @return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &name, const std::string &usage, - int64_t *count); - - // Op name getter. - // @return Name of the current Op. - std::string Name() const override { return "EMnistOp"; } - - // DatasetName name getter. - // \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const override { return upper ? "EMnist" : "emnist"; } - - private: - // Read all files in the directory. - // @return Status The status code returned. - Status WalkAllFiles() override; - - const std::string name_; // can be "byclass", "bymerge", "balanced", "letters", "digits", "mnist". -}; - -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EMNIST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.cc deleted file mode 100644 index bdf6b04c9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.cc +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h" - -#include -#include - -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -EnWik9Op::EnWik9Op(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr data_schema, const std::vector &file_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, total_rows, worker_connector_size, std::move(data_schema), file_list, op_connector_size, - shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void EnWik9Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nEnWik9 file path:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - // Print the name of per file path. - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status EnWik9Op::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file + " does not exist."); - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file + - ". Check if the file is damaged or permission denied."); - } - - int64_t rows_total = 0; - std::string line; - - while (getline(handle, line)) { - if (line.empty()) { - line = ""; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - TensorRow tRow(1, nullptr); - tRow.setPath({file}); - auto s = LoadTensor(line, &tRow); - if (s != Status::OK()) { - handle.close(); - return s; - } - s = jagged_rows_connector_->Add(worker_id, std::move(tRow)); - if (s != Status::OK()) { - handle.close(); - return s; - } - - rows_total++; - } - handle.close(); - - return Status::OK(); -} - -int64_t EnWik9Op::CountTotalRows(const std::string &file) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file, " << file << " does not exist."; - return 0; - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - MS_LOG(ERROR) << "Invalid file, failed to open file: " << file - << ". Check if the file is damaged or permission denied."; - return 0; - } - - std::string line; - int64_t count = 0; - while (getline(handle, line)) { - count++; - } - handle.close(); - - return count; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h deleted file mode 100644 index 13893c89c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EN_WIK9_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EN_WIK9_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" - -namespace mindspore { -namespace dataset { -class EnWik9Op : public TextFileOp { - public: - /// \brief Constructor. - /// \param[in] num_workers The number of worker threads reading data from enwiki files. - /// \param[in] total_rows The number of rows to read. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] data_schema The data schema object. - /// \param[in] files_list List of file paths for the dataset files. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices The number of devices. - /// \param[in] device_id Id of device. - EnWik9Op(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr data_schema, const std::vector &file_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id); - - /// \brief Default destructor. - ~EnWik9Op() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "EnWik9Op"; } - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you need upper DatasetName. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const override { return upper ? "EnWik9" : "enwik9"; } - - /// \brief Reads a text file and loads the data into multiple TensorRows. - /// \param[in] file The file to read. - /// \param[in] start_offset - the start offset of file. - /// \param[in] end_offset - the end offset of file. - /// \param[in] The id of the worker that is executing this function. - /// \return Status The error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - private: - /// \brief Count number of rows in each file. - /// \param[in] file Txt file name. - /// \return int64_t The total number of rows in file. - int64_t CountTotalRows(const std::string &file) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_EN_WIK9_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.cc deleted file mode 100644 index 8c5231f1c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.cc +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -FakeImageOp::FakeImageOp(int32_t num_images, const std::vector &image_size, int32_t num_classes, - int32_t base_seed, int32_t num_workers, int32_t op_connector_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, op_connector_size, std::move(sampler)), - num_images_(num_images), - base_seed_(base_seed), - image_size_(image_size), - num_classes_(num_classes), - data_schema_(std::move(data_schema)), - image_total_size_(0), - label_list_({}), - image_tensor_({}) {} - -// Load 1 TensorRow (image, label) using 1 trow. -Status FakeImageOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr image, label; - - auto images_buf = std::make_unique(image_total_size_); - auto pixels = &images_buf[0]; - { - std::unique_lock lock(access_mutex_); - if (image_tensor_[row_id] == nullptr) { - rand_gen_.seed(base_seed_ + row_id); // set seed for random generator. - std::normal_distribution distribution(0.0, 1.0); - for (int i = 0; i < image_total_size_; ++i) { - pixels[i] = distribution(rand_gen_); // generate the Gaussian distribution pixel. - if (pixels[i] < 0) { - pixels[i] = 0; - } - if (pixels[i] > 255) { - pixels[i] = 255; - } - } - TensorShape img_tensor_shape = TensorShape({image_size_[0], image_size_[1], image_size_[2]}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(img_tensor_shape, data_schema_->Column(0).Type(), - reinterpret_cast(pixels), &image)); - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(image, &image_tensor_[row_id])); - } else { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(image_tensor_[row_id], &image)); - } - } - RETURN_IF_NOT_OK(Tensor::CreateScalar(label_list_[row_id], &label)); - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - return Status::OK(); -} - -// A print method typically used for debugging. -void FakeImageOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of images: " << num_images_ << "\nNumber of classes: " << num_classes_ - << "\nBase seed: " << base_seed_ << "\n\n"; - } -} - -// Derived from RandomAccessOp. -Status FakeImageOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || label_list_.empty()) { - if (label_list_.empty()) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] No image found in dataset. Check if image was generated successfully."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for storing image-index pair is nullptr or has been set in other place, " - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < label_list_.size(); ++i) { - (*cls_ids)[label_list_[i]].push_back(i); - } - for (auto &pr : (*cls_ids)) { - pr.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status FakeImageOp::GetItem(int32_t index) { - // generate one target label according to index and save it in label_list_. - rand_gen_.seed(base_seed_ + index); // set seed for random generator. - std::uniform_int_distribution dist(0, num_classes_ - 1); - uint32_t target = dist(rand_gen_); // generate the target. - label_list_.emplace_back(target); - - return Status::OK(); -} - -Status FakeImageOp::PrepareData() { - // FakeImage generate image with Gaussian distribution. - image_total_size_ = image_size_[0] * image_size_[1] * image_size_[2]; - - for (size_t i = 0; i < num_images_; ++i) { - RETURN_IF_NOT_OK(GetItem(i)); - } - - label_list_.shrink_to_fit(); - num_rows_ = label_list_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0, "Invalid data, generate fake data failed, please check dataset API."); - image_tensor_.clear(); - image_tensor_.resize(num_rows_); - return Status::OK(); -} - -Status FakeImageOp::ComputeColMap() { - // Extract the column name mapping from the schema and save it in the class. - if (column_name_id_map_.empty()) { - RETURN_IF_NOT_OK(data_schema_->GetColumnNameMap(&(column_name_id_map_))); - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h deleted file mode 100644 index 3f266bded..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FAKE_IMAGE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FAKE_IMAGE_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { - -class FakeImageOp : public MappableLeafOp { - public: - // Constructor. - // @param int32_t num_images - Number of generated fake images. - // @param const std::vector &image_size - The size of fake image. - // @param int32_t num_classes - Number of classes in fake images. - // @param int32_t base_seed - A base seed which is used in generating fake image randomly. - // @param int32_t num_workers - Number of workers reading images in parallel. - // @param int32_t op_connector_size - Connector queue size. - // @param std::unique_ptr data_schema - The schema of the fake image dataset. - // @param td::unique_ptr sampler - Sampler tells FakeImageOp what to read. - FakeImageOp(int32_t num_images, const std::vector &image_size, int32_t num_classes, int32_t base_seed, - int32_t num_workers, int32_t op_connector_size, std::unique_ptr data_schema, - std::shared_ptr sampler); - - // Destructor. - ~FakeImageOp() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class. - // @param std::map> *cls_ids - Key label, val all ids for this class. - // @return Status The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging. - // @param out - The output stream to write output to. - // @param show_all - A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the FakeImage dataset. - // @return Number of images. - int64_t GetTotalRows() const { return num_images_; } - - // Op name getter. - // @return Name of the current Op. - std::string Name() const override { return "FakeImageOp"; } - - // Get a image from index - // @param int32_t index - Generate one image according to index. - Status GetItem(int32_t index); - - private: - // Load a tensor row according to a lable_list. - // @param row_id_type row_id - Id for this tensor row. - // @param TensorRow *row - Image & label read into this tensor row. - // @return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - // Generate all labels of FakeImage dataset - // @return Status The status code returned. - Status PrepareData(); - - // Private function for computing the assignment of the column name map. - // @return Status The status code returned. - Status ComputeColMap() override; - - int32_t num_images_; - int32_t base_seed_; - std::vector image_size_; - int32_t num_classes_; - - std::unique_ptr data_schema_; - - int32_t image_total_size_; - std::vector label_list_; - std::vector> image_tensor_; - std::mt19937 rand_gen_; - std::mutex access_mutex_; -}; - -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FAKE_IMAGE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.cc deleted file mode 100644 index 3372b9685..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -FashionMnistOp::FashionMnistOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, - int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MnistOp(usage, num_workers, folder_path, queue_size, std::move(data_schema), std::move(sampler)) {} - -Status FashionMnistOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - // the logic of counting the number of samples is copied from ParseMnistData() and uses CheckReader(). - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = - std::make_shared(usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - - RETURN_IF_NOT_OK(op->WalkAllFiles()); - - for (size_t i = 0; i < op->image_names_.size(); ++i) { - auto image_realpath = FileUtils::GetRealPath(op->image_names_[i].c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path"); - } - auto label_realpath = FileUtils::GetRealPath(op->label_names_[i].c_str()); - if (!label_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path"); - } - std::ifstream image_reader; - image_reader.open(image_realpath.value(), std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(image_reader.is_open(), "Invalid file, failed to open " + op->image_names_[i] + - ": the image file is damaged or permission denied."); - std::ifstream label_reader; - label_reader.open(label_realpath.value(), std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(label_reader.is_open(), "Invalid file, failed to open " + op->label_names_[i] + - ": the label file is damaged or permission denied."); - uint32_t num_images; - Status s = op->CheckImage(op->image_names_[i], &image_reader, &num_images); - image_reader.close(); - RETURN_IF_NOT_OK(s); - - uint32_t num_labels; - s = op->CheckLabel(op->label_names_[i], &label_reader, &num_labels); - label_reader.close(); - RETURN_IF_NOT_OK(s); - - CHECK_FAIL_RETURN_UNEXPECTED( - (num_images == num_labels), - "Invalid data, num of images should be equal to num of labels, but got num of images: " + - std::to_string(num_images) + ", num of labels: " + std::to_string(num_labels) + "."); - *count = *count + num_images; - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h deleted file mode 100644 index f6c54c406..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FASHION_MNIST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FASHION_MNIST_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" - -namespace mindspore { -namespace dataset { -/// \brief Forward declares. -template -class Queue; - -class FashionMnistOp : public MnistOp { - public: - /// \brief Constructor. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] num_workers Number of workers reading images in parallel. - /// \param[in] folder_path Dir directory of fashionmnist. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema The schema of the fashionmnist dataset. - /// \param[in] Sampler Tells FashionMnistOp what to read. - FashionMnistOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~FashionMnistOp() = default; - - /// \brief Function to count the number of samples in the Fashion-MNIST dataset. - /// \param[in] dir Path to the Fashion-MNIST directory. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] count Output arg that will hold the minimum of the actual dataset size and numSamples. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "FashionMnistOp"; } - - /// \brief Dataset name getter. - /// \param[in] upper Whether to get upper name. - /// \return Dataset name of the current Op. - std::string DatasetName(bool upper = false) const override { return upper ? "FashionMnist" : "fashion mnist"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FASHION_MNIST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.cc deleted file mode 100644 index 1fb719330..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.cc +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -FlickrOp::FlickrOp(int32_t num_workers, const std::string &dataset_dir, const std::string &file_path, bool decode, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - file_path_(file_path), - decode_(decode), - data_schema_(std::move(data_schema)) {} - -// Load 1 TensorRow (image, annotations) using 1 ImageLabelPair. 1 function call produces 1 TensorTow -Status FlickrOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair> data = image_annotation_pairs_[static_cast(row_id)]; - std::shared_ptr image; - std::shared_ptr annotations; - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.first, &image)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data.second, &annotations)); - - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = - "Invalid image, failed to decode " + data.first + ": the image is damaged or permission denied!"; - RETURN_STATUS_UNEXPECTED(err); - } - } - - (*trow) = TensorRow(row_id, {std::move(image), std::move(annotations)}); - trow->setPath({data.first, file_path_}); - return Status::OK(); -} - -void FlickrOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nFlickr DatasetDir: " << dataset_dir_ - << "\nAnnotationFile: " << file_path_ << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status FlickrOp::PrepareData() { - auto real_file_path = FileUtils::GetRealPath(file_path_.c_str()); - if (!real_file_path.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file_path_ + " does not exist."); - } - - std::ifstream file_handle(real_file_path.value(), std::ios::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid annotation file, failed to open " + file_path_ + - " : the file is damaged or permission denied."); - } - - std::string line; - int32_t flag_idx; - std::string sub_str_flag = "\t"; - std::string image_file_path; - std::string image_name; - std::map> image_annotation_map_; - Path dataset_dir(dataset_dir_); - while (getline(file_handle, line)) { - try { - if (line.empty()) { - continue; - } - - flag_idx = line.find_first_of(sub_str_flag); - image_name = line.substr(0, flag_idx - 2); // -2 because "#[0-4]\t" - if (image_name.empty()) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, the attribute of image_name is missing in flickr dataset file: " + - file_path_ + ", line: " + line); - } - - image_file_path = (dataset_dir / image_name).ToString(); - std::string annotation = line.substr(flag_idx + 1); - if (annotation.empty()) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, the attribute of annotation is missing in flickr dataset file: " + - file_path_ + ", line: " + line); - } - - bool valid = false; - Status type_check = CheckImageType(image_file_path, &valid); - if (type_check.IsError()) { - file_handle.close(); - RETURN_IF_NOT_OK(type_check); - } - if (!valid) { - continue; - } - - image_annotation_map_[image_file_path].emplace_back(annotation); - } catch (const std::exception &err) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse flickr dataset file: " + file_path_ + ": " + - std::string(err.what())); - } - } - - for (auto item : image_annotation_map_) { - image_annotation_pairs_.emplace_back(std::make_pair(item.first, item.second)); - } - - file_handle.close(); - RETURN_IF_NOT_OK(CountDatasetInfo()); // Count the total rows - return Status::OK(); -} - -// Only support JPEG/PNG/GIF/BMP -// Optimization: Could take in a tensor -// This function does not return status because we want to just skip bad input, not crash -Status FlickrOp::CheckImageType(const std::string &file_name, bool *valid) { - auto real_file_name = FileUtils::GetRealPath(file_name.c_str()); - if (!real_file_name.has_value()) { - MS_LOG(ERROR) << "Invalid file path, flickr dataset file: " << file_name << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, flickr dataset file: " + file_name + " does not exist."); - } - - std::ifstream file_handle; - constexpr int read_num = 3; - *valid = false; - file_handle.open(real_file_name.value(), std::ios::binary | std::ios::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid flickr file, failed to open " + file_name + - ": the file is damaged or permission denied."); - } - unsigned char file_type[read_num]; - (void)file_handle.read(reinterpret_cast(file_type), read_num); - - if (file_handle.fail()) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid flickr file, failed to read " + file_name + - ": the file is damaged or the file content is incomplete."); - } - file_handle.close(); - if (file_type[0] == 0xff && file_type[1] == 0xd8 && file_type[2] == 0xff) { - // Normal JPEGs start with \xff\xd8\xff\xe0 - // JPEG with EXIF stats with \xff\xd8\xff\xe1 - // Use \xff\xd8\xff to cover both. - *valid = true; - } else if (file_type[0] == 0x89 && file_type[1] == 0x50 && file_type[2] == 0x4e) { - // It's a PNG - *valid = true; - } else if (file_type[0] == 0x47 && file_type[1] == 0x49 && file_type[2] == 0x46) { - // It's a GIF - *valid = true; - } else if (file_type[0] == 0x42 && file_type[1] == 0x4d) { - // It's a BMP - *valid = true; - } - return Status::OK(); -} - -Status FlickrOp::CountDatasetInfo() { - num_rows_ = static_cast(image_annotation_pairs_.size()); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API FlickrDataset. Please check file path or dataset API."); - } - return Status::OK(); -} - -Status FlickrOp::CountTotalRows(const std::string &dir, const std::string &file, int64_t *count) { - // the logic of counting the number of samples is copied from ParseFlickrData() - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - // build a new unique schema object - auto new_schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("annotation", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - std::shared_ptr op = std::make_shared(num_workers, dir, file, false, op_connect_size, - std::move(new_schema), std::move(new_sampler)); - - RETURN_IF_NOT_OK(op->PrepareData()); - *count = static_cast(op->image_annotation_pairs_.size()); - return Status::OK(); -} - -Status FlickrOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h deleted file mode 100644 index 52cf903fd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FLICKR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FLICKR_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class FlickrOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] int32_t num_workers - Num of workers reading images in parallel - /// \param[in] std::string dataset_dir - dir directory of Flickr dataset - /// \param[in] std::string annotation_file - dir directory of annotation file - /// \param[in] int32_t queue_size - connector queue size - /// \param[in] std::unique_ptr sampler - sampler tells ImageFolderOp what to read - FlickrOp(int32_t num_workers, const std::string &dataset_dir, const std::string &annotation_file, bool decode, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~FlickrOp() = default; - - /// \brief A print method typically used for debugging - /// \param[out] out - /// \param[in] show_all - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the Flickr dataset - /// \param[in] dir - path to the Flickr directory - /// \param[in] file - path to the annotation file - /// \param[out] count - output arg that will hold the actual dataset size - /// \return Status - The status code returned - static Status CountTotalRows(const std::string &dir, const std::string &file, int64_t *count); - - /// \brief Op name getter - /// \return Name of the current Op - std::string Name() const override { return "FlickrOp"; } - - protected: - /// \brief Parse Flickr data - /// \return Status - The status code returned - Status PrepareData() override; - - private: - /// \brief Load a tensor row according to a pair - /// \param[in] uint64_t index - index need to load - /// \param[out] TensorRow row - image & annotation read into this tensor row - /// \return Status - The status code returned - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - /// \brief Check if image ia valid.Only support JPEG/PNG/GIF/BMP - /// \param[in] std::string file_name - image file name need to be checked - /// \param[out] bool valid - whether the image type is valid - /// \return Status - The status code returned - Status CheckImageType(const std::string &file_name, bool *valid); - - /// \brief Count annotation index,num rows and num samples - /// \return Status - The status code returned - Status CountDatasetInfo(); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status - The status code returned - Status ComputeColMap() override; - - std::string dataset_dir_; - std::string file_path_; - bool decode_; - std::unique_ptr data_schema_; - - std::vector>> image_annotation_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FLICKR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.cc deleted file mode 100644 index 07eaac11d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.cc +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. -*/ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -Food101Op::Food101Op(const std::string &folder_path, const std::string &usage, int32_t num_workers, int32_t queue_size, - bool decode, std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(folder_path), - decode_(decode), - usage_(usage), - data_schema_(std::move(data_schema)) {} - -Status Food101Op::PrepareData() { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, Food101 dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, Food101 dataset dir: " + folder_path_ + " does not exist."); - } - - std::string image_root_path = (Path(realpath.value()) / Path("images")).ToString(); - std::string train_list_txt_ = (Path(realpath.value()) / Path("meta") / Path("train.txt")).ToString(); - std::string test_list_txt_ = (Path(realpath.value()) / Path("meta") / Path("test.txt")).ToString(); - - Path img_folder(image_root_path); - CHECK_FAIL_RETURN_UNEXPECTED(img_folder.Exists(), - "Invalid path, Food101 image path: " + image_root_path + " does not exist."); - CHECK_FAIL_RETURN_UNEXPECTED(img_folder.IsDirectory(), - "Invalid path, Food101 image path: " + image_root_path + " is not a folder."); - std::shared_ptr img_folder_itr = Path::DirIterator::OpenDirectory(&img_folder); - - RETURN_UNEXPECTED_IF_NULL(img_folder_itr); - int32_t dirname_offset_ = img_folder.ToString().length() + 1; - - while (img_folder_itr->HasNext()) { - Path sub_dir = img_folder_itr->Next(); - if (sub_dir.IsDirectory()) { - classes_.insert(sub_dir.ToString().substr(dirname_offset_)); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!classes_.empty(), - "Invalid path, no subfolder found under path: " + img_folder.ToString()); - - if (usage_ == "test") { - RETURN_IF_NOT_OK(GetAllImageList(test_list_txt_)); - } else if (usage_ == "train") { - RETURN_IF_NOT_OK(GetAllImageList(train_list_txt_)); - } else { - RETURN_IF_NOT_OK(GetAllImageList(train_list_txt_)); - RETURN_IF_NOT_OK(GetAllImageList(test_list_txt_)); - } - CHECK_FAIL_RETURN_UNEXPECTED(!all_img_lists_.empty(), - "No valid train.txt or test.txt file under path: " + image_root_path); - all_img_lists_.shrink_to_fit(); - num_rows_ = all_img_lists_.size(); - - return Status::OK(); -} - -void Food101Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nFood101 dataset dir: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status Food101Op::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string img_name = (Path(folder_path_) / Path("images") / Path(all_img_lists_[row_id])).ToString() + ".jpg"; - std::shared_ptr image; - std::shared_ptr label; - std::string label_str; - for (auto it : classes_) { - if (all_img_lists_[row_id].find(it) != all_img_lists_[row_id].npos) { - label_str = it; - break; - } - } - RETURN_IF_NOT_OK(Tensor::CreateScalar(label_str, &label)); - RETURN_IF_NOT_OK(ReadImageToTensor(img_name, &image)); - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({img_name, std::string("")}); - return Status::OK(); -} - -Status Food101Op::ReadImageToTensor(const std::string &image_path, std::shared_ptr *tensor) { - RETURN_UNEXPECTED_IF_NULL(tensor); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_path, tensor)); - if (decode_) { - Status rc = Decode(*tensor, tensor); - CHECK_FAIL_RETURN_UNEXPECTED( - rc.IsOk(), "Invalid file, failed to decode image, the image may be broken or permission denied: " + image_path); - } - return Status::OK(); -} - -// Get dataset size. -Status Food101Op::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - if (all_img_lists_.size() == 0) { - RETURN_IF_NOT_OK(PrepareData()); - } - *count = static_cast(all_img_lists_.size()); - return Status::OK(); -} - -Status Food101Op::GetAllImageList(const std::string &file_path) { - std::ifstream handle(file_path, std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open text:" + file_path + - ", the file is damaged or permission denied."); - } - - std::string line; - while (getline(handle, line)) { - if (!line.empty()) { - all_img_lists_.push_back(line); - } - } - handle.close(); - return Status::OK(); -} - -Status Food101Op::ComputeColMap() { - // Set the column name map (base class field). - if (column_name_id_map_.empty()) { - for (int32_t index = 0; index < data_schema_->NumColumns(); index++) { - column_name_id_map_[data_schema_->Column(index).Name()] = index; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h deleted file mode 100644 index c7d9581d3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FOOD101_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FOOD101_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class Food101Op : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] folder_path Directory of Food101 dataset. - /// \param[in] usage Usage. - /// \param[in] num_workers Number of workers reading images in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] decode Whether to decode images. - /// \param[in] schema Data schema of Food101 dataset. - /// \param[in] sampler Sampler tells Food101 what to read. - Food101Op(const std::string &folder_path, const std::string &usage, int32_t num_workers, int32_t queue_size, - bool decode, std::unique_ptr schema, std::shared_ptr sampler); - - /// \brief Deconstructor. - ~Food101Op() override = default; - - /// A print method typically used for debugging. - /// \param[out] out Out stream. - /// \param[in] show_all Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "Food101Op"; } - - /// Function to count the number of samples in the Food101 dataset. - /// \param[in] count Output arg that will hold the actual dataset size. - /// \return The status code returned. - Status CountTotalRows(int64_t *count); - - private: - /// Load a tensor row. - /// \param[in] row_id Row id. - /// \param[in] row Read all features into this tensor row. - /// \return The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \param[in] image_path Path of image data. - /// \param[in] tensor Get image tensor. - /// \return The status code returned. - Status ReadImageToTensor(const std::string &image_path, std::shared_ptr *tensor); - - /// Called first when function is called. Get file_name, img_path info from ".txt" files. - /// \return Status - The status code returned. - Status PrepareData(); - - /// Private function for computing the assignment of the column name map. - /// \return Status-the status code returned. - Status ComputeColMap() override; - - /// Private function for getting all the image files; - /// \param[in] file_path The path of the dataset. - /// \return Status-the status code returned. - Status GetAllImageList(const std::string &file_path); - - std::string folder_path_; // directory of Food101 folder. - std::string usage_; - bool decode_; - std::unique_ptr data_schema_; - std::set classes_; - std::vector all_img_lists_; - std::map class_index_; - std::map> annotation_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_FOOD101_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.cc deleted file mode 100644 index 8eada8750..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.cc +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/python_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -GeneratorOp::GeneratorOp(const py::function &generator_function, std::vector column_names, - std::vector column_types, int32_t prefetch_size, int32_t connector_size, - std::shared_ptr sampler, int32_t num_parallel_workers, bool has_batch_sampler) - : PipelineOp(connector_size, std::move(sampler)), - column_names_(std::move(column_names)), - column_types_(std::move(column_types)), - prefetch_size_(prefetch_size), - generator_counter_(0), - num_parallel_workers_(num_parallel_workers), - num_rows_sampled_{0}, - has_batch_sampler_(has_batch_sampler), - num_samples_collected_(0), - size_of_this_batch_(-1), - batch_sizes_of_epoch_({}) { - py::gil_scoped_acquire gil_acquire; - generator_function_ = generator_function; -} - -GeneratorOp::~GeneratorOp() { - // we need to acquire gil before release python object - py::gil_scoped_acquire gil_acquire; - generator_ = py::object(); - generator_function_ = py::object(); -} - -void GeneratorOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nColumn names:\n"; - for (int i = 0; i < column_names_.size(); ++i) { - out << "\n " << column_names_[i]; - } - out << "\n\n"; - } -} -// hand shake with Sampler, allow Sampler to call RandomAccessOp's functions to get NumRows -Status GeneratorOp::InitSampler() { - if (sampler_ != nullptr) { - // Let the sampler know if we are resetting the pipeline to a specific epoch (op_current_repeats_ > 0) - // to mimic the behaviour in that state and have repeatability. - // Note that number of repeats is used since in each epoch we may reset sampler multiple times. - return sampler_->HandshakeRandomAccessOp(this, op_current_repeats_); - } - return Status::OK(); -} - -// Invoke the generatorFunction to get generator object -Status GeneratorOp::CreateGeneratorObject() { - Status ret; - { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized."); - } - try { - py::array sample_ids; - if (sampler_ != nullptr) { - // Sampler is not null which means the source is RandomAccessible - // get all samples and pass it to the Generator function - RETURN_IF_NOT_OK(sampler_->GetAllIdsThenReset(&sample_ids)); - // If sampler is a user-defined python sampler, sample_ids will flow from python to c++ and back to python - generator_ = generator_function_(sample_ids); - } else { - generator_ = generator_function_(); - } - } catch (const py::error_already_set &e) { - ret = Status(StatusCode::kMDPyFuncException, e.what()); - } - } - return ret; -} - -Status GeneratorOp::Launch() { - // Launch the python multiprocessing - if (python_multiprocessing_runtime_) { - py::gil_scoped_acquire gil_acquire; - MS_LOG(DEBUG) << "Launch Python Multiprocessing for GeneratorOp: " << id(); - try { - python_multiprocessing_runtime_->launch(id()); - } catch (py::error_already_set &e) { - std::string traceback; - try { - // Construct python-like traceback - py::list tb = py::module::import("traceback").attr("format_tb")(e.trace()); - traceback = "Traceback (most recent call last):\n"; - for (auto t : tb) { - traceback += py::reinterpret_borrow(t); - } - traceback += e.what(); - } catch (std::exception &) { - // Back to original exception - traceback = e.what(); - } - - // Restore exception to python - e.restore(); - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, traceback); - } - } - return DatasetOp::Launch(); -} - -Status GeneratorOp::Terminate() { - // Terminate the python multiprocessing - if (python_multiprocessing_runtime_) { - MS_LOG(INFO) << "Terminate Python Multiprocessing for GeneratorOp: " << id(); - python_multiprocessing_runtime_->terminate(); - } - return Status::OK(); -} - -// Reentrant init method. -Status GeneratorOp::Init() { - RETURN_IF_NOT_OK(InitSampler()); - return CreateGeneratorObject(); -} - -Status GeneratorOp::PyRowToTensorRow(py::object py_data, TensorRow *tensor_row) { - if (!py::isinstance(py_data)) { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, - "Invalid Python function, the 'source' of 'GeneratorDataset' should return a tuple of NumPy " - "arrays, but got " + - std::string(py_data.get_type().str())); - } - py::tuple py_row = py_data.cast(); - // Check if returned number of columns matches with column names - if (py_row.size() != column_names_.size()) { - RETURN_STATUS_ERROR( - StatusCode::kMDPyFuncException, - "Invalid Python function, the 'source' of 'GeneratorDataset' should return same number of NumPy arrays as " - "specified in column_names, the size of column_names is:" + - std::to_string(column_names_.size()) + - " and number of returned NumPy array is:" + std::to_string(py_row.size())); - } - // Iterate over two containers simultaneously for memory copy - for (int i = 0; i < py_row.size(); ++i) { - py::object ret_py_ele = py_row[i]; - if (!py::isinstance(ret_py_ele) && !py::isinstance(ret_py_ele)) { - RETURN_STATUS_ERROR( - StatusCode::kMDPyFuncException, - "Invalid Python function, 'GeneratorDataset' should return a tuple of NumPy arrays or dictionaries, " - "but got " + - std::string(ret_py_ele.get_type().str())); - } - std::shared_ptr tensor; - if (py::isinstance(ret_py_ele)) { - RETURN_IF_NOT_OK(Tensor::CreateFromPythonObject(ret_py_ele.cast(), &tensor)); - } else { - RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(ret_py_ele.cast(), &tensor)); - } - if ((!column_types_.empty()) && (column_types_[i] != DataType::DE_UNKNOWN) && - (column_types_[i] != tensor->type())) { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, - "Invalid Python function, type of returned data in 'GeneratorDataset' should be same with " - "specified column_types, but the type of returned data: " + - std::string(ret_py_ele.get_type().str()) + - ", specified column type: " + column_types_[i].ToString()); - } - tensor_row->push_back(tensor); - } - return Status::OK(); -} - -Status GeneratorOp::CheckNumSamples() const { - if (num_rows_sampled_ != -1 && num_rows_sampled_ != generator_counter_) { - if (generator_counter_ == 0) { - std::string msg = - "Unable to fetch data from GeneratorDataset, try iterate the source function of GeneratorDataset or check" - " value of num_epochs when create iterator."; - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, msg); - } - std::stringstream ss; - ss << "The actual amount of data read from generator " << generator_counter_ << " is different from generator.len " - << num_rows_sampled_ << ", you should adjust generator.len to make them match."; - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, ss.str()); - } - return Status::OK(); -} - -Status GeneratorOp::CalculatedSampleId() { - RETURN_IF_NOT_OK(InitSampler()); - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized."); - } - try { - py::array sample_ids; - RETURN_IF_NOT_OK(sampler_->GetAllIdsThenReset(&sample_ids)); - auto buf_info = sample_ids.request(); - auto ptr_sample = static_cast(buf_info.ptr); - for (size_t i = 0; i < buf_info.size; i++) { - sample_ids_.push_back(ptr_sample[i]); - } - } catch (const py::error_already_set &e) { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, e.what()); - } - return Status::OK(); -} - -Status GeneratorOp::GetMappedIndex(int64_t index, int64_t *out_index) { - if (index > sample_ids_.size() - 1) { - RETURN_STATUS_UNEXPECTED("Index [" + std::to_string(index) + "] exceeded the number of data samples."); - } - if (index < 0) { - RETURN_STATUS_UNEXPECTED("Index [" + std::to_string(index) + "] can not be a negative number."); - } - *out_index = sample_ids_[index]; - return Status::OK(); -} - -Status GeneratorOp::GetNextBatchSize() { - if (has_batch_sampler_) { - if (!batch_sizes_of_epoch_.empty()) { - size_of_this_batch_ = batch_sizes_of_epoch_.front(); - batch_sizes_of_epoch_.pop_front(); - CHECK_FAIL_RETURN_UNEXPECTED(size_of_this_batch_ > 0, "Batch sampler should return a list, but got an integer."); - } - } - return Status::OK(); -} - -Status GeneratorOp::GetNextEpochBatchSizes() { - if (has_batch_sampler_) { - auto batch_sampler = std::dynamic_pointer_cast(sampler_); - CHECK_FAIL_RETURN_UNEXPECTED(batch_sampler != nullptr, - "Batch sampler of GeneratorDataset should be a Python sampler."); - RETURN_IF_NOT_OK(batch_sampler->GetBatchSizes(&batch_sizes_of_epoch_)); - CHECK_FAIL_RETURN_UNEXPECTED(!batch_sizes_of_epoch_.empty(), "Batch sizes should not be empty."); - } - return Status::OK(); -} - -Status GeneratorOp::CheckAndSendEOB() { - if (has_batch_sampler_) { - if (num_samples_collected_ == size_of_this_batch_) { - generator_counter_++; - MS_LOG(DEBUG) << "Generator operator sends out EOB."; - RETURN_IF_NOT_OK(out_connector_->SendEOB()); - num_samples_collected_ = 0; - } - } - return Status::OK(); -} - -// Entry point for Generator, called by launch() -// Note that this function is very easy to break because of the Python GIL mechanism -// The master thread has the following workflow -// -// while !eof: -// Try: -// Prepare one data row GIL, Can throw -// Catch: -// Fetch Python Exception GIL -// Check if Exception is StopIteration (EOE) GIL -// Restore Python Exception GIL -// If not StopIteration: -// Return Status PyFuncException -// -// Push data buffer to connector Block -// -// if EOE -// Push EOE Block -// if more epoch: -// Block until next epoch Block -// else: -// Push EOF Block -// eof = true -// Return Status OK -// -// Note that any modification of this function need to guarantee: -// 1. All "Require GIL" operations are protected by GIL -// SegFault / Deadlock will occur if this condition is not fulfilled. -// 2. All "Block" operations are free from GIL, all block target are registered with tree. -// Deadlock will occur if this condition is not fulfilled -// 3. No Python GC should be triggered outside of GIL. -// SegFault will occur is this condition is not fulfilled -// -Status GeneratorOp::operator()() { - // Handshake with TaskManager to synchronize thread creation - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(wp_.Register(tree_->AllTasks())); - num_rows_sampled_ = sampler_ ? sampler_->CalculateNumSamples(num_rows_) : num_rows_; - RETURN_IF_NOT_OK(Init()); - - RETURN_IF_NOT_OK(GetNextEpochBatchSizes()); - RETURN_IF_NOT_OK(GetNextBatchSize()); - - bool eof = false; - while (!eof) { - // Create new row each iteration - bool eoe = false; - TensorRow new_row; - { - double row_timer_start = GetMilliTimeStamp(); - uint64_t start_time = GetSyscnt(); - py::gil_scoped_acquire gil_acquire; - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "AcquireGIL", start_time)); - try { - auto start = ProfilingTime::GetCurMilliSecond(); - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(PyRowToTensorRow(generator_.attr("__next__")(), &new_row)); - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "__next__", start_time)); - new_row.TimerRecord(NameWithID(), RowTimer::kWorkerTime, {GetMilliTimeStamp() - row_timer_start}); - auto end = ProfilingTime::GetCurMilliSecond(); - if ((end - start) / num_parallel_workers_ > kGetItemTimeOutMilliSeconds) { - MS_LOG(WARNING) << "Bad performance attention, it takes more than " + - std::to_string(kGetItemTimeOutMilliSeconds / 1000) + - " seconds to generator.__next__ new row, " - "which might cause `GetNext` timeout problem when sink_mode=True. You can increase the " - "parameter num_parallel_workers in GeneratorDataset / optimize the efficiency of " - "obtaining samples in the user-defined generator function."; - } - if (has_batch_sampler_) { - num_samples_collected_++; - } else { - generator_counter_++; - } - } catch (py::error_already_set &e) { - eoe = e.matches(PyExc_StopIteration); - // Pop up non StopIteration Python Exception - if (!eoe) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "__next__", start_time, {{"TensorRowFlags", "Exception"}})); - std::string traceback; - try { - // Construct python-like traceback - py::list tb = py::module::import("traceback").attr("format_tb")(e.trace()); - traceback = "Traceback (most recent call last):\n"; - for (auto t : tb) { - traceback += py::reinterpret_borrow(t); - } - traceback += e.what(); - } catch (std::exception &) { - // Back to original exception - traceback = e.what(); - } - - // Restore exception to python - e.restore(); - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, traceback); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "__next__", start_time, {{"TensorRowFlags", "StopIteration"}})); - // Restore exception to python - e.restore(); - - // Check whether the number of samples is sufficient only when the first epoch - if (op_current_repeats_ == 0) { - RETURN_IF_NOT_OK(CheckNumSamples()); - } - } - } - if (!new_row.empty()) { - RETURN_IF_NOT_OK(out_connector_->Add(std::move(new_row))); - } - - RETURN_IF_NOT_OK(CheckAndSendEOB()); - - if (eoe) { - // Push out EOE upon StopIteration exception from generator - MS_LOG(DEBUG) << "Generator operator sends out EOE."; - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - if (has_batch_sampler_) { - CHECK_FAIL_RETURN_UNEXPECTED(num_samples_collected_ == 0, - "The sum of batch sizes should be equal to the total num samples."); - } - if (IsLastIteration()) { - // If last repeat or not repeated, push out EOF and exit master loop - MS_LOG(DEBUG) << "Generator operator sends out EOF."; - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - MS_LOG(DEBUG) << "Generator operator main execution loop complete."; - eof = true; - } else { - // Waiting for repeatOp to start new epoch - // If Reset() is called first by repeat op, this wait() will return right away. - // If Reset() is not called yet, this wait() will block until reset. - if (this->GetOpTotalRepeats() < 0) { - RETURN_IF_NOT_OK(wp_.Wait()); - // Clear the status of the wait post - wp_.Clear(); - } else { - // Self-reset to start a new iteration - RETURN_IF_NOT_OK(Reset()); - } - RETURN_IF_NOT_OK(GetNextEpochBatchSizes()); - } - UpdateRepeatAndEpochCounter(); - } - if (num_samples_collected_ == 0 && !eof) { - RETURN_IF_NOT_OK(GetNextBatchSize()); - } - } - return Status::OK(); -} - -Status GeneratorOp::Reset() { - // Reset Op state - MS_LOG(DEBUG) << Name() << " performing a self-reset."; - // Create new generator object - RETURN_IF_NOT_OK(CreateGeneratorObject()); - // Once the master thread is waked up, that means a new epoch is started, - // so the counter must be reset before master thread starts increasing it. - generator_counter_ = 0; - if (this->GetOpTotalRepeats() < 0) { - // Wake up master thread - wp_.Set(); - } - return Status::OK(); -} - -void GeneratorOp::SetPythonMp(std::shared_ptr python_multiprocessing_runtime) { - python_multiprocessing_runtime_ = std::move(python_multiprocessing_runtime); -} - -Status GeneratorOp::ComputeColMap() { - // Setup column names map (base class field) - if (column_name_id_map_.empty()) { - for (size_t i = 0; i < column_names_.size(); ++i) { - column_name_id_map_[column_names_[i]] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status GeneratorOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - if (!prepared_data_) { - num_rows_sampled_ = sampler_ ? sampler_->CalculateNumSamples(num_rows_) : num_rows_; - RETURN_IF_NOT_OK(Init()); - MS_LOG(DEBUG) << "num_rows_sampled: " << num_rows_sampled_; - RETURN_IF_NOT_OK(GetNextEpochBatchSizes()); - prepared_data_ = true; - } - - if (eof_received_) { - *row = TensorRow(TensorRow::kFlagEOF); - return Status::OK(); - } - - if (has_batch_sampler_) { - if (num_samples_collected_ == 0) { - RETURN_IF_NOT_OK(GetNextBatchSize()); - } - if (num_samples_collected_ == size_of_this_batch_) { - generator_counter_++; - num_samples_collected_ = 0; - *row = TensorRow(TensorRow::kFlagEOB); - return Status::OK(); - } - } - - bool eoe = false; - TensorRow new_row; - { - py::gil_scoped_acquire gil_acquire; - try { - RETURN_IF_NOT_OK(PyRowToTensorRow(generator_.attr("__next__")(), &new_row)); - if (has_batch_sampler_) { - num_samples_collected_++; - } else { - generator_counter_++; - } - } catch (py::error_already_set &e) { - eoe = e.matches(PyExc_StopIteration); - // Pop up non StopIteration Python Exception - if (!eoe) { - std::string traceback; - try { - // Construct python-like traceback - py::list tb = py::module::import("traceback").attr("format_tb")(e.trace()); - traceback = "Traceback (most recent call last):\n"; - for (auto t : tb) { - traceback += py::reinterpret_borrow(t); - } - traceback += e.what(); - } catch (std::exception &) { - // Back to original exception - traceback = e.what(); - } - - // Restore exception to python - e.restore(); - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, traceback); - } - - // Check whether the number of samples is sufficient only when the first epoch - if (op_current_repeats_ == 0) { - RETURN_IF_NOT_OK(CheckNumSamples()); - } - } - } - if (!new_row.empty()) { - *row = std::move(new_row); - return Status::OK(); - } - - if (eoe) { - *row = TensorRow(TensorRow::kFlagEOE); - if (has_batch_sampler_) { - CHECK_FAIL_RETURN_UNEXPECTED(num_samples_collected_ == 0, - "The sum of batch sizes should be equal to the total num samples."); - } - if (IsLastIteration()) { - eof_received_ = true; - } else { - // Self-reset to start a new iteration - RETURN_IF_NOT_OK(Reset()); - RETURN_IF_NOT_OK(GetNextEpochBatchSizes()); - UpdateRepeatAndEpochCounter(); - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h deleted file mode 100644 index eeb90163c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GENERATOR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GENERATOR_OP_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "pybind11/pybind11.h" - -namespace py = pybind11; - -namespace mindspore { -namespace dataset { -#ifndef _MSC_VER -#pragma GCC visibility push(hidden) -#endif - -constexpr int32_t kGetItemTimeOutMilliSeconds = 60000; - -class GeneratorOp : public PipelineOp, public RandomAccessOp { - public: - GeneratorOp(const py::function &generator_function, std::vector column_names, - std::vector column_types, int32_t prefetch_size, int32_t connector_size, - std::shared_ptr sampler, int32_t num_parallel_workers, bool has_batch_sampler); - - ~GeneratorOp() override; - - /// A print method typically used for debugging - /// \param out - The output stream to write output to - /// \param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// << Stream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param out - reference to the output stream being overloaded - /// \param generator_op - reference to the GeneratorOp to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const GeneratorOp &generator_op) { - generator_op.Print(out, false); - return out; - } - - /// Class functor operator () override. - /// All DatasetOps operate by launching a thread (see ExecutionTree). This class functor will - /// provide the master loop that drives the logic for performing the work. - /// \return Status The status code returned - Status operator()() override; - - /// Method of calculating sample ids - /// @return Status The status code returned - Status CalculatedSampleId(); - - /// Get sample ids - /// @return Status The status code returned - Status GetMappedIndex(int64_t index, int64_t *out_index); - - /// Overrides base class reset method. When an operator does a reset, it cleans up any state - /// info from it's previous execution and then initializes itself so that it can be executed - /// again. - /// \return Status The status code returned - Status Reset() override; - - /// Op name getter - /// \return Name of the current Op - std::string Name() const override { return "GeneratorOp"; } - - bool IsPython() const override { return true; } - - /// Number of parallel workers getter - /// \return Number of parallel workers of the current Op - int32_t NumWorkers() const override { return num_parallel_workers_; } - - /// Set the instance of python multiprocessing which will passed from python - /// \param python_multiprocessing_runtime PythonMultiprocessingRuntime - void SetPythonMp(std::shared_ptr python_multiprocessing_runtime); - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - /// Used by independent dataset mode to stop the subprocess - /// \return Status The status code returned - Status Terminate() override; - - protected: - /// \brief Launch subprocesses in multiprocessing mode. - /// \return Status Status code. - Status Launch() override; - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - py::function generator_function_; - std::vector column_names_; - std::vector column_types_; - int32_t prefetch_size_; - int64_t generator_counter_; - int32_t num_parallel_workers_; - int64_t num_rows_sampled_; - bool has_batch_sampler_; - int64_t num_samples_collected_; - int64_t size_of_this_batch_; - std::deque batch_sizes_of_epoch_; - - py::object generator_; - std::vector sample_ids_; - - WaitPost wp_; - - bool prepared_data_{false}; // flag to indicate whether the data is prepared before taking for pull mode - bool eof_received_{false}; // flag to indicate whether end of epoch signal is reached in pull mode - - std::shared_ptr python_multiprocessing_runtime_; // python multiprocessing instance - - Status PyRowToTensorRow(py::object py_data, TensorRow *tensor_row); - - /// Private function for computing the assignment of the column name map. - /// \return - Status - Status ComputeColMap() override; - - /// Initialize Sampler, calls sampler->Init() within - /// \return Status The status code returned - Status InitSampler(); - - /// Create new Generator object from the generator function - /// \return Status The status code returned - Status CreateGeneratorObject(); - - /// Initialize GeneratorOp - /// \return Status The status code returned - Status Init(); - - /// Check whether the target number of samples has been retrieved when eoe is hit. - /// \return Status The status code returned - Status CheckNumSamples() const; - - /// \brief Get the size for next batch. - /// \return Status The status code returned. - Status GetNextBatchSize(); - - /// \brief Get the sizes of each batch for next epoch. - /// \return Status The status code returned. - Status GetNextEpochBatchSizes(); - - /// \brief Check whether the number of samples collected and send EOB. - /// \return Status The status code returned. - Status CheckAndSendEOB(); -}; - -#ifndef _MSC_VER -#pragma GCC visibility pop -#endif -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GENERATOR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.cc deleted file mode 100644 index 4d107f98d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.cc +++ /dev/null @@ -1,335 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -const std::vector genres = { - "blues", "classical", "country", "disco", "hiphop", "jazz", "metal", "pop", "reggae", "rock", -}; - -const std::vector filtered_test = { - "blues.00012", "blues.00013", "blues.00014", "blues.00015", "blues.00016", "blues.00017", - "blues.00018", "blues.00019", "blues.00020", "blues.00021", "blues.00022", "blues.00023", - "blues.00024", "blues.00025", "blues.00026", "blues.00027", "blues.00028", "blues.00061", - "blues.00062", "blues.00063", "blues.00064", "blues.00065", "blues.00066", "blues.00067", - "blues.00068", "blues.00069", "blues.00070", "blues.00071", "blues.00072", "blues.00098", - "blues.00099", "classical.00011", "classical.00012", "classical.00013", "classical.00014", "classical.00015", - "classical.00016", "classical.00017", "classical.00018", "classical.00019", "classical.00020", "classical.00021", - "classical.00022", "classical.00023", "classical.00024", "classical.00025", "classical.00026", "classical.00027", - "classical.00028", "classical.00029", "classical.00034", "classical.00035", "classical.00036", "classical.00037", - "classical.00038", "classical.00039", "classical.00040", "classical.00041", "classical.00049", "classical.00077", - "classical.00078", "classical.00079", "country.00030", "country.00031", "country.00032", "country.00033", - "country.00034", "country.00035", "country.00036", "country.00037", "country.00038", "country.00039", - "country.00040", "country.00043", "country.00044", "country.00046", "country.00047", "country.00048", - "country.00050", "country.00051", "country.00053", "country.00054", "country.00055", "country.00056", - "country.00057", "country.00058", "country.00059", "country.00060", "country.00061", "country.00062", - "country.00063", "country.00064", "disco.00001", "disco.00021", "disco.00058", "disco.00062", - "disco.00063", "disco.00064", "disco.00065", "disco.00066", "disco.00069", "disco.00076", - "disco.00077", "disco.00078", "disco.00079", "disco.00080", "disco.00081", "disco.00082", - "disco.00083", "disco.00084", "disco.00085", "disco.00086", "disco.00087", "disco.00088", - "disco.00091", "disco.00092", "disco.00093", "disco.00094", "disco.00096", "disco.00097", - "disco.00099", "hiphop.00000", "hiphop.00026", "hiphop.00027", "hiphop.00030", "hiphop.00040", - "hiphop.00043", "hiphop.00044", "hiphop.00045", "hiphop.00051", "hiphop.00052", "hiphop.00053", - "hiphop.00054", "hiphop.00062", "hiphop.00063", "hiphop.00064", "hiphop.00065", "hiphop.00066", - "hiphop.00067", "hiphop.00068", "hiphop.00069", "hiphop.00070", "hiphop.00071", "hiphop.00072", - "hiphop.00073", "hiphop.00074", "hiphop.00075", "hiphop.00099", "jazz.00073", "jazz.00074", - "jazz.00075", "jazz.00076", "jazz.00077", "jazz.00078", "jazz.00079", "jazz.00080", - "jazz.00081", "jazz.00082", "jazz.00083", "jazz.00084", "jazz.00085", "jazz.00086", - "jazz.00087", "jazz.00088", "jazz.00089", "jazz.00090", "jazz.00091", "jazz.00092", - "jazz.00093", "jazz.00094", "jazz.00095", "jazz.00096", "jazz.00097", "jazz.00098", - "jazz.00099", "metal.00012", "metal.00013", "metal.00014", "metal.00015", "metal.00022", - "metal.00023", "metal.00025", "metal.00026", "metal.00027", "metal.00028", "metal.00029", - "metal.00030", "metal.00031", "metal.00032", "metal.00033", "metal.00038", "metal.00039", - "metal.00067", "metal.00070", "metal.00073", "metal.00074", "metal.00075", "metal.00078", - "metal.00083", "metal.00085", "metal.00087", "metal.00088", "pop.00000", "pop.00001", - "pop.00013", "pop.00014", "pop.00043", "pop.00063", "pop.00064", "pop.00065", - "pop.00066", "pop.00069", "pop.00070", "pop.00071", "pop.00072", "pop.00073", - "pop.00074", "pop.00075", "pop.00076", "pop.00077", "pop.00078", "pop.00079", - "pop.00082", "pop.00088", "pop.00089", "pop.00090", "pop.00091", "pop.00092", - "pop.00093", "pop.00094", "pop.00095", "pop.00096", "reggae.00034", "reggae.00035", - "reggae.00036", "reggae.00037", "reggae.00038", "reggae.00039", "reggae.00040", "reggae.00046", - "reggae.00047", "reggae.00048", "reggae.00052", "reggae.00053", "reggae.00064", "reggae.00065", - "reggae.00066", "reggae.00067", "reggae.00068", "reggae.00071", "reggae.00079", "reggae.00082", - "reggae.00083", "reggae.00084", "reggae.00087", "reggae.00088", "reggae.00089", "reggae.00090", - "rock.00010", "rock.00011", "rock.00012", "rock.00013", "rock.00014", "rock.00015", - "rock.00027", "rock.00028", "rock.00029", "rock.00030", "rock.00031", "rock.00032", - "rock.00033", "rock.00034", "rock.00035", "rock.00036", "rock.00037", "rock.00039", - "rock.00040", "rock.00041", "rock.00042", "rock.00043", "rock.00044", "rock.00045", - "rock.00046", "rock.00047", "rock.00048", "rock.00086", "rock.00087", "rock.00088", - "rock.00089", "rock.00090", -}; - -const std::vector filtered_train = { - "blues.00029", "blues.00030", "blues.00031", "blues.00032", "blues.00033", "blues.00034", - "blues.00035", "blues.00036", "blues.00037", "blues.00038", "blues.00039", "blues.00040", - "blues.00041", "blues.00042", "blues.00043", "blues.00044", "blues.00045", "blues.00046", - "blues.00047", "blues.00048", "blues.00049", "blues.00073", "blues.00074", "blues.00075", - "blues.00076", "blues.00077", "blues.00078", "blues.00079", "blues.00080", "blues.00081", - "blues.00082", "blues.00083", "blues.00084", "blues.00085", "blues.00086", "blues.00087", - "blues.00088", "blues.00089", "blues.00090", "blues.00091", "blues.00092", "blues.00093", - "blues.00094", "blues.00095", "blues.00096", "blues.00097", "classical.00030", "classical.00031", - "classical.00032", "classical.00033", "classical.00043", "classical.00044", "classical.00045", "classical.00046", - "classical.00047", "classical.00048", "classical.00050", "classical.00051", "classical.00052", "classical.00053", - "classical.00054", "classical.00055", "classical.00056", "classical.00057", "classical.00058", "classical.00059", - "classical.00060", "classical.00061", "classical.00062", "classical.00063", "classical.00064", "classical.00065", - "classical.00066", "classical.00067", "classical.00080", "classical.00081", "classical.00082", "classical.00083", - "classical.00084", "classical.00085", "classical.00086", "classical.00087", "classical.00088", "classical.00089", - "classical.00090", "classical.00091", "classical.00092", "classical.00093", "classical.00094", "classical.00095", - "classical.00096", "classical.00097", "classical.00098", "classical.00099", "country.00019", "country.00020", - "country.00021", "country.00022", "country.00023", "country.00024", "country.00025", "country.00026", - "country.00028", "country.00029", "country.00065", "country.00066", "country.00067", "country.00068", - "country.00069", "country.00070", "country.00071", "country.00072", "country.00073", "country.00074", - "country.00075", "country.00076", "country.00077", "country.00078", "country.00079", "country.00080", - "country.00081", "country.00082", "country.00083", "country.00084", "country.00085", "country.00086", - "country.00087", "country.00088", "country.00089", "country.00090", "country.00091", "country.00092", - "country.00093", "country.00094", "country.00095", "country.00096", "country.00097", "country.00098", - "country.00099", "disco.00005", "disco.00015", "disco.00016", "disco.00017", "disco.00018", - "disco.00019", "disco.00020", "disco.00022", "disco.00023", "disco.00024", "disco.00025", - "disco.00026", "disco.00027", "disco.00028", "disco.00029", "disco.00030", "disco.00031", - "disco.00032", "disco.00033", "disco.00034", "disco.00035", "disco.00036", "disco.00037", - "disco.00039", "disco.00040", "disco.00041", "disco.00042", "disco.00043", "disco.00044", - "disco.00045", "disco.00047", "disco.00049", "disco.00053", "disco.00054", "disco.00056", - "disco.00057", "disco.00059", "disco.00061", "disco.00070", "disco.00073", "disco.00074", - "disco.00089", "hiphop.00002", "hiphop.00003", "hiphop.00004", "hiphop.00005", "hiphop.00006", - "hiphop.00007", "hiphop.00008", "hiphop.00009", "hiphop.00010", "hiphop.00011", "hiphop.00012", - "hiphop.00013", "hiphop.00014", "hiphop.00015", "hiphop.00016", "hiphop.00017", "hiphop.00018", - "hiphop.00019", "hiphop.00020", "hiphop.00021", "hiphop.00022", "hiphop.00023", "hiphop.00024", - "hiphop.00025", "hiphop.00028", "hiphop.00029", "hiphop.00031", "hiphop.00032", "hiphop.00033", - "hiphop.00034", "hiphop.00035", "hiphop.00036", "hiphop.00037", "hiphop.00038", "hiphop.00041", - "hiphop.00042", "hiphop.00055", "hiphop.00056", "hiphop.00057", "hiphop.00058", "hiphop.00059", - "hiphop.00060", "hiphop.00061", "hiphop.00077", "hiphop.00078", "hiphop.00079", "hiphop.00080", - "jazz.00000", "jazz.00001", "jazz.00011", "jazz.00012", "jazz.00013", "jazz.00014", - "jazz.00015", "jazz.00016", "jazz.00017", "jazz.00018", "jazz.00019", "jazz.00020", - "jazz.00021", "jazz.00022", "jazz.00023", "jazz.00024", "jazz.00041", "jazz.00047", - "jazz.00048", "jazz.00049", "jazz.00050", "jazz.00051", "jazz.00052", "jazz.00053", - "jazz.00054", "jazz.00055", "jazz.00056", "jazz.00057", "jazz.00058", "jazz.00059", - "jazz.00060", "jazz.00061", "jazz.00062", "jazz.00063", "jazz.00064", "jazz.00065", - "jazz.00066", "jazz.00067", "jazz.00068", "jazz.00069", "jazz.00070", "jazz.00071", - "jazz.00072", "metal.00002", "metal.00003", "metal.00005", "metal.00021", "metal.00024", - "metal.00035", "metal.00046", "metal.00047", "metal.00048", "metal.00049", "metal.00050", - "metal.00051", "metal.00052", "metal.00053", "metal.00054", "metal.00055", "metal.00056", - "metal.00057", "metal.00059", "metal.00060", "metal.00061", "metal.00062", "metal.00063", - "metal.00064", "metal.00065", "metal.00066", "metal.00069", "metal.00071", "metal.00072", - "metal.00079", "metal.00080", "metal.00084", "metal.00086", "metal.00089", "metal.00090", - "metal.00091", "metal.00092", "metal.00093", "metal.00094", "metal.00095", "metal.00096", - "metal.00097", "metal.00098", "metal.00099", "pop.00002", "pop.00003", "pop.00004", - "pop.00005", "pop.00006", "pop.00007", "pop.00008", "pop.00009", "pop.00011", - "pop.00012", "pop.00016", "pop.00017", "pop.00018", "pop.00019", "pop.00020", - "pop.00023", "pop.00024", "pop.00025", "pop.00026", "pop.00027", "pop.00028", - "pop.00029", "pop.00031", "pop.00032", "pop.00033", "pop.00034", "pop.00035", - "pop.00036", "pop.00038", "pop.00039", "pop.00040", "pop.00041", "pop.00042", - "pop.00044", "pop.00046", "pop.00049", "pop.00050", "pop.00080", "pop.00097", - "pop.00098", "pop.00099", "reggae.00000", "reggae.00001", "reggae.00002", "reggae.00004", - "reggae.00006", "reggae.00009", "reggae.00011", "reggae.00012", "reggae.00014", "reggae.00015", - "reggae.00016", "reggae.00017", "reggae.00018", "reggae.00019", "reggae.00020", "reggae.00021", - "reggae.00022", "reggae.00023", "reggae.00024", "reggae.00025", "reggae.00026", "reggae.00027", - "reggae.00028", "reggae.00029", "reggae.00030", "reggae.00031", "reggae.00032", "reggae.00042", - "reggae.00043", "reggae.00044", "reggae.00045", "reggae.00049", "reggae.00050", "reggae.00051", - "reggae.00054", "reggae.00055", "reggae.00056", "reggae.00057", "reggae.00058", "reggae.00059", - "reggae.00060", "reggae.00063", "reggae.00069", "rock.00000", "rock.00001", "rock.00002", - "rock.00003", "rock.00004", "rock.00005", "rock.00006", "rock.00007", "rock.00008", - "rock.00009", "rock.00016", "rock.00017", "rock.00018", "rock.00019", "rock.00020", - "rock.00021", "rock.00022", "rock.00023", "rock.00024", "rock.00025", "rock.00026", - "rock.00057", "rock.00058", "rock.00059", "rock.00060", "rock.00061", "rock.00062", - "rock.00063", "rock.00064", "rock.00065", "rock.00066", "rock.00067", "rock.00068", - "rock.00069", "rock.00070", "rock.00091", "rock.00092", "rock.00093", "rock.00094", - "rock.00095", "rock.00096", "rock.00097", "rock.00098", "rock.00099", -}; - -const std::vector filtered_valid = { - "blues.00000", "blues.00001", "blues.00002", "blues.00003", "blues.00004", "blues.00005", - "blues.00006", "blues.00007", "blues.00008", "blues.00009", "blues.00010", "blues.00011", - "blues.00050", "blues.00051", "blues.00052", "blues.00053", "blues.00054", "blues.00055", - "blues.00056", "blues.00057", "blues.00058", "blues.00059", "blues.00060", "classical.00000", - "classical.00001", "classical.00002", "classical.00003", "classical.00004", "classical.00005", "classical.00006", - "classical.00007", "classical.00008", "classical.00009", "classical.00010", "classical.00068", "classical.00069", - "classical.00070", "classical.00071", "classical.00072", "classical.00073", "classical.00074", "classical.00075", - "classical.00076", "country.00000", "country.00001", "country.00002", "country.00003", "country.00004", - "country.00005", "country.00006", "country.00007", "country.00009", "country.00010", "country.00011", - "country.00012", "country.00013", "country.00014", "country.00015", "country.00016", "country.00017", - "country.00018", "country.00027", "country.00041", "country.00042", "country.00045", "country.00049", - "disco.00000", "disco.00002", "disco.00003", "disco.00004", "disco.00006", "disco.00007", - "disco.00008", "disco.00009", "disco.00010", "disco.00011", "disco.00012", "disco.00013", - "disco.00014", "disco.00046", "disco.00048", "disco.00052", "disco.00067", "disco.00068", - "disco.00072", "disco.00075", "disco.00090", "disco.00095", "hiphop.00081", "hiphop.00082", - "hiphop.00083", "hiphop.00084", "hiphop.00085", "hiphop.00086", "hiphop.00087", "hiphop.00088", - "hiphop.00089", "hiphop.00090", "hiphop.00091", "hiphop.00092", "hiphop.00093", "hiphop.00094", - "hiphop.00095", "hiphop.00096", "hiphop.00097", "hiphop.00098", "jazz.00002", "jazz.00003", - "jazz.00004", "jazz.00005", "jazz.00006", "jazz.00007", "jazz.00008", "jazz.00009", - "jazz.00010", "jazz.00025", "jazz.00026", "jazz.00027", "jazz.00028", "jazz.00029", - "jazz.00030", "jazz.00031", "jazz.00032", "metal.00000", "metal.00001", "metal.00006", - "metal.00007", "metal.00008", "metal.00009", "metal.00010", "metal.00011", "metal.00016", - "metal.00017", "metal.00018", "metal.00019", "metal.00020", "metal.00036", "metal.00037", - "metal.00068", "metal.00076", "metal.00077", "metal.00081", "metal.00082", "pop.00010", - "pop.00053", "pop.00055", "pop.00058", "pop.00059", "pop.00060", "pop.00061", - "pop.00062", "pop.00081", "pop.00083", "pop.00084", "pop.00085", "pop.00086", - "reggae.00061", "reggae.00062", "reggae.00070", "reggae.00072", "reggae.00074", "reggae.00076", - "reggae.00077", "reggae.00078", "reggae.00085", "reggae.00092", "reggae.00093", "reggae.00094", - "reggae.00095", "reggae.00096", "reggae.00097", "reggae.00098", "reggae.00099", "rock.00038", - "rock.00049", "rock.00050", "rock.00051", "rock.00052", "rock.00053", "rock.00054", - "rock.00055", "rock.00056", "rock.00071", "rock.00072", "rock.00073", "rock.00074", - "rock.00075", "rock.00076", "rock.00077", "rock.00078", "rock.00079", "rock.00080", - "rock.00081", "rock.00082", "rock.00083", "rock.00084", "rock.00085", -}; - -GTZANOp::GTZANOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - usage_(usage), - folder_path_(folder_path), - data_schema_(std::move(data_schema)) {} - -Status GTZANOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - const uint32_t sample_rate = 22050; - std::shared_ptr waveform, rate, label; - RETURN_IF_NOT_OK(ReadAudio(audio_names_[row_id].first, &waveform)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &rate)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_names_[row_id].second, &label)); - (*trow) = TensorRow(row_id, {std::move(waveform), std::move(rate), std::move(label)}); - trow->setPath({audio_names_[row_id].first, audio_names_[row_id].first, audio_names_[row_id].first}); - return Status::OK(); -} - -void GTZANOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - return; - } - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nGTZAN directory: " << folder_path_ << "\n\n"; -} - -Status GTZANOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_label = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_label))); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = op->audio_names_.size(); - return Status::OK(); -} - -Status GTZANOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status GTZANOp::ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform) { - RETURN_UNEXPECTED_IF_NULL(waveform); - const int32_t kWavFileSampleRate = 22050; - int32_t sample_rate = 0; - std::vector waveform_vec; - RETURN_IF_NOT_OK(ReadWaveFile(audio_dir, &waveform_vec, &sample_rate)); - CHECK_FAIL_RETURN_UNEXPECTED(sample_rate == kWavFileSampleRate, - "Invalid file, sampling rate of GTZAN wav file must be 22050, file path: " + audio_dir); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, waveform)); - RETURN_IF_NOT_OK((*waveform)->ExpandDim(0)); - return Status::OK(); -} - -Status GTZANOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, GTZAN Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, GTZAN Dataset dir: " + folder_path_ + " does not exist."); - } - Path dir(folder_path_); - - if (usage_ == "all") { - for (std::string sub_directory : genres) { - Path full_dir = dir / sub_directory; - if (!full_dir.Exists() || !full_dir.IsDirectory()) { - continue; - } - auto dir_it = Path::DirIterator::OpenDirectory(&full_dir); - if (dir_it != nullptr) { - while (dir_it->HasNext()) { - Path file = dir_it->Next(); - std::string file_name = file.ToString(); - auto pos = file_name.find_last_of('.'); - std::string name = file_name.substr(0, pos), temp_ext = file_name.substr(pos); - if (temp_ext == ".wav" && name.find('.') != std::string::npos) { - audio_names_.push_back({file.ToString(), sub_directory}); - } else { - MS_LOG(WARNING) << "Invalid file, invalid file name or file type: " << file.ToString() << "."; - } - } - } else { - MS_LOG(WARNING) << "Invalid file path, unable to open directory: " << full_dir.ToString() << "."; - } - } - } else { - const std::vector *files_point = nullptr; - if (usage_ == "test") { - files_point = &filtered_test; - } else if (usage_ == "train") { - files_point = &filtered_train; - } else { - files_point = &filtered_valid; - } - std::string ext = ".wav"; - for (auto sub_file_name : *files_point) { - auto pos = sub_file_name.find_first_of('.'); - std::string cls = sub_file_name.substr(0, pos); - Path full_dir = dir / cls / (sub_file_name + ext); - if (full_dir.Exists()) { - audio_names_.push_back({full_dir.ToString(), cls}); - } else { - MS_LOG(WARNING) << "The audio file is lost, file name= " << (sub_file_name + ext); - } - } - } - num_rows_ = audio_names_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0, "Invalid data, no valid data found in path:" + folder_path_); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h deleted file mode 100644 index 3aae85c44..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GTZAN_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GTZAN_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class GTZANOp : public MappableLeafOp { - public: - /// \brief Constructor - /// \param[in] usage Usage of this dataset, can be 'train', 'valid', 'test', or 'all'. - /// \param[in] num_workers Number of workers reading audios in parallel. - /// \param[in] folder_path Dir directory of GTZAN. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema The schema of the GTZAN dataset. - /// \param[in] sampler Sampler tells GTZANOp what to read. - GTZANOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \Destructor. - ~GTZANOp() = default; - - /// \A print method typically used for debugging. - /// \param[out] out Output stream. - /// \param[in] show_all Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// \Function to count the number of samples in the GTZAN dataset. - /// \param[in] dir Path to the GTZAN directory. - /// \param[in] usage Choose the subset of GTZAN dataset. - /// \param[out] count Output arg that will hold the actual dataset size. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - /// \Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "GTZANOp"; } - - private: - /// \Load a tensor row according to a pair. - /// \param[in] row_id Id for this tensor row. - /// \param[out] row Audio & label read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \Parse a audio file. - /// \param[in] audio_dir Audio file path. - /// \param[out] waveform The output waveform tensor. - /// \return Status The status code returned. - Status ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform); - - /// \Prepare data. - /// \return Status The status code returned. - Status PrepareData(); - - /// \Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - const std::string usage_; - std::string folder_path_; - std::unique_ptr data_schema_; - std::vector> audio_names_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_GTZAN_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.cc deleted file mode 100644 index 833a47b5d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.cc +++ /dev/null @@ -1,404 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h" - -#include - -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -#ifdef ENABLE_PYTHON -ImageFolderOp::ImageFolderOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool recursive, bool do_decode, - const std::set &exts, const std::map &map, - std::unique_ptr data_schema, std::shared_ptr sampler, - py::function decrypt) - : MappableLeafOp(num_wkrs, queue_size, std::move(sampler)), - folder_path_(std::move(file_dir)), - recursive_(recursive), - decode_(do_decode), - extensions_(exts), - class_index_(map), - data_schema_(std::move(data_schema)), - sampler_ind_(0), - dirname_offset_(0), - decrypt_(std::move(decrypt)) { - folder_name_queue_ = std::make_unique>(num_wkrs * queue_size); - image_name_queue_ = std::make_unique>(num_wkrs * queue_size); -} -#else -ImageFolderOp::ImageFolderOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool recursive, bool do_decode, - const std::set &exts, const std::map &map, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_wkrs, queue_size, std::move(sampler)), - folder_path_(std::move(file_dir)), - recursive_(recursive), - decode_(do_decode), - extensions_(exts), - class_index_(map), - data_schema_(std::move(data_schema)), - sampler_ind_(0), - dirname_offset_(0) { - folder_name_queue_ = std::make_unique>(num_wkrs * queue_size); - image_name_queue_ = std::make_unique>(num_wkrs * queue_size); -} -#endif - -// Master thread that pulls the prescan worker's results. -// Keep collecting results until all prescan workers quit -// Then consolidate 2 level shuffles together into 1 giant vector -// calculate numRows then return -Status ImageFolderOp::PrepareData() { - std::vector v; - int64_t cnt = 0; - while (cnt != num_workers_) { // count number of end signals - FolderImagesPair p; - RETURN_IF_NOT_OK(image_name_queue_->PopFront(&p)); - if (p == nullptr) { - cnt++; - } else { - v.push_back(p); - } - } - std::sort(v.begin(), v.end(), - [](const FolderImagesPair &lhs, const FolderImagesPair &rhs) { return lhs->first < rhs->first; }); - // following loop puts the 2 level of shuffles together into 1 vector - for (size_t ind = 0; ind < v.size(); ++ind) { - while (v[ind]->second.empty() == false) { - MS_ASSERT(!(v[ind]->first.empty())); // make sure that v[ind]->first.substr(1) is not out of bound - v[ind]->second.front()->second = class_index_.empty() ? ind : class_index_[v[ind]->first.substr(1)]; - image_label_pairs_.push_back(v[ind]->second.front()); - v[ind]->second.pop(); - } - } - image_label_pairs_.shrink_to_fit(); - num_rows_ = image_label_pairs_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } - // free memory of two queues used for pre-scan - folder_name_queue_->Reset(); - image_name_queue_->Reset(); - return Status::OK(); -} - -// Load 1 TensorRow (image,label) using 1 ImageLabelPair. 1 function call produces 1 TensorTow -Status ImageFolderOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - ImageLabelPair pair_ptr = image_label_pairs_[row_id]; - std::shared_ptr image, label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(pair_ptr->second, &label)); -#ifdef ENABLE_PYTHON - RETURN_IF_NOT_OK(MappableLeafOp::ImageDecrypt(folder_path_ + (pair_ptr->first), &image, decrypt_)); -#else - RETURN_IF_NOT_OK(Tensor::CreateFromFile(folder_path_ + (pair_ptr->first), &image)); -#endif - - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = "Invalid image, " + folder_path_ + (pair_ptr->first) + - " decode failed, the image is broken or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({folder_path_ + (pair_ptr->first), std::string("")}); - return Status::OK(); -} - -void ImageFolderOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\n" - << DatasetName(true) << " directory: " << folder_path_ << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// Derived from RandomAccessOp -Status ImageFolderOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_label_pairs_.empty()) { - if (image_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + DatasetName(true) + - "Dataset API can't read the data file(interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR], Map containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < image_label_pairs_.size(); ++i) { - (*cls_ids)[image_label_pairs_[i]->second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -// Worker Entry for pre-scanning all the folders and do the 1st level shuffle -// Worker pull a file name from folder_name_queue_ (which is a Queue), walks all the images under that foldername -// After walking is complete, sort all the file names (relative path to all jpeg files under the same directory ) -// (Sort is automatically conducted using a set which is implemented using a Red-Black Tree) -// Add the sorted filenames in to a queue. The make a pair (foldername, queue*), -// foldername is used for 2nd level sorting. -// FYI: 1st level sorting: sort all images under the same directory. -// FYI: 2nd level sorting: sort all folder names -// push this pair to image_name_queue (which is again a Queue) -Status ImageFolderOp::PrescanWorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - std::string folder_name; - RETURN_IF_NOT_OK(folder_name_queue_->PopFront(&folder_name)); - while (folder_name.empty() == false) { - Path folder(folder_path_ + folder_name); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (folder.Exists() == false || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + folder_name + " does not exist or permission denied."); - } - std::set imgs; // use this for ordering - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (extensions_.empty() || extensions_.find(file.Extension()) != extensions_.end()) { - (void)imgs.insert(file.ToString().substr(dirname_offset_)); - } else { - MS_LOG(WARNING) << DatasetName(true) << " operator unsupported file found: " << file.ToString() - << ", extension: " << file.Extension() << "."; - } - } - FolderImagesPair p = std::make_shared>>(); - p->first = folder_name; - for (const std::string &img : imgs) { - p->second.push(std::make_shared>(img, 0)); - } - RETURN_IF_NOT_OK(image_name_queue_->EmplaceBack(p)); - RETURN_IF_NOT_OK(folder_name_queue_->PopFront(&folder_name)); - } - RETURN_IF_NOT_OK(image_name_queue_->EmplaceBack(nullptr)); // end signal - return Status::OK(); -} - -// This helper function recursively walks all folder_paths, and send each foldername to folder_name_queue_ -// if mRecursive == false, don't go into folder of folders -Status ImageFolderOp::RecursiveWalkFolder(Path *dir) { - RETURN_UNEXPECTED_IF_NULL(dir); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - if (class_index_.empty() || - class_index_.find(subdir.ToString().substr(dirname_offset_ + 1)) != class_index_.end()) { - RETURN_IF_NOT_OK(folder_name_queue_->EmplaceBack(subdir.ToString().substr(dirname_offset_))); - } - if (recursive_ == true) { - MS_LOG(ERROR) << "[Internal ERROR] RecursiveWalkFolder(&subdir) functionality is disabled permanently. " - << "No recursive walk of directory will be performed."; - } - } - } - return Status::OK(); -} - -// A thread that calls RecursiveWalkFolder -Status ImageFolderOp::StartAsyncWalk() { - TaskManager::FindMe()->Post(); - Path dir(folder_path_); - if (dir.Exists() == false || dir.IsDirectory() == false) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + folder_path_ + " may not exist or the path is not a directory."); - } - dirname_offset_ = folder_path_.length(); - RETURN_IF_NOT_OK(RecursiveWalkFolder(&dir)); - // send out num_workers_ end signal to folder_name_queue_, 1 for each worker. - // Upon receiving end Signal, worker quits and set another end Signal to image_name_queue. - for (int32_t ind = 0; ind < num_workers_; ++ind) { - RETURN_IF_NOT_OK(folder_name_queue_->EmplaceBack("")); // end signal - } - return Status::OK(); -} - -Status ImageFolderOp::RegisterAndLaunchThreads() { - RETURN_IF_NOT_OK(ParallelOp::RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(folder_name_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(image_name_queue_->Register(tree_->AllTasks())); - - // The following code launch 3 threads group - // 1) A thread that walks all folders and push the folder names to a util:Queue folder_name_queue_. - // 2) Workers that pull foldername from folder_name_queue_, walk it and return the sorted images to image_name_queue - // 3) Launch main workers that load TensorRows by reading all images - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask(Name() + "::WalkDir", - std::bind(&ImageFolderOp::StartAsyncWalk, this), nullptr, id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&ImageFolderOp::PrescanWorkerEntry, this, std::placeholders::_1), - Name() + "::PrescanWorkerEntry", id())); - - return Status::OK(); -} - -Status ImageFolderOp::CountRowsAndClasses(const std::string &path, const std::set &exts, int64_t *num_rows, - int64_t *num_classes, std::map class_index) { - Path dir(path); - std::string err_msg = ""; - int64_t row_cnt = 0; - err_msg += (dir.Exists() == false || dir.IsDirectory() == false) - ? "Invalid dataset_dir, " + path + " does not exist or the path is not a directory. " - : ""; - err_msg += (num_classes == nullptr && num_rows == nullptr) ? "[Internal ERROR] num_class and num_rows are null." : ""; - if (err_msg.empty() == false) { - RETURN_STATUS_UNEXPECTED(err_msg); - } - std::queue folder_paths; - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - std::unordered_set folder_names; - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - folder_paths.push(subdir.ToString()); - if (!class_index.empty()) { - folder_names.insert(subdir.Basename()); - } - } - } - if (num_classes != nullptr) { - // if class index is empty, get everything on disk - if (class_index.empty()) { - *num_classes = folder_paths.size(); - } else { - for (const auto &p : class_index) { - CHECK_FAIL_RETURN_UNEXPECTED(folder_names.find(p.first) != folder_names.end(), - "Invalid subdirectory, class: " + p.first + " doesn't exist in " + path + " ."); - } - (*num_classes) = class_index.size(); - } - } - // return here if only num_class is needed - RETURN_OK_IF_TRUE(num_rows == nullptr); - while (folder_paths.empty() == false) { - Path subdir(folder_paths.front()); - dir_itr = Path::DirIterator::OpenDirectory(&subdir); - if (subdir.Exists() == false || dir_itr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid subdirectory, ImageFolder Dataset subdirectory: " + subdir.ToString() + - " does not exist or permission denied"); - } - while (dir_itr->HasNext()) { - if (exts.empty() || exts.find(dir_itr->Next().Extension()) != exts.end()) { - ++row_cnt; - } - } - folder_paths.pop(); - } - (*num_rows) = row_cnt; - return Status::OK(); -} - -Status ImageFolderOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Get number of classes -Status ImageFolderOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (num_classes_ > 0) { - *num_classes = num_classes_; - return Status::OK(); - } - RETURN_IF_NOT_OK(CountRowsAndClasses(folder_path_, extensions_, nullptr, num_classes, class_index_)); - num_classes_ = *num_classes; - return Status::OK(); -} - -Status ImageFolderOp::InitPullMode() { - // to avoid the concurrent and multi end signal in StartAsyncWalk, explicitly set num_workers_ to 1 - num_workers_ = 1; - RETURN_IF_NOT_OK(folder_name_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(image_name_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask(Name() + "::WalkDir", - std::bind(&ImageFolderOp::StartAsyncWalk, this), nullptr, id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&ImageFolderOp::PrescanWorkerEntry, this, std::placeholders::_1), - Name() + "::PrescanWorkerEntry", id())); - return PrepareData(); -} - -Status ImageFolderOp::GetClassIndexing( - std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - output_class_indexing->clear(); - - // if class_index_ exist, return directly - if (!class_index_.empty()) { - (void)std::transform( - class_index_.begin(), class_index_.end(), std::back_inserter(*output_class_indexing), - [](const auto &elem) { return std::pair>(elem.first, {elem.second}); }); - return Status::OK(); - } - - // Iter folder path - Path dir(folder_path_); - if (!dir.Exists() || !dir.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + folder_path_ + " does not exist or not a directory."); - } - - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - std::vector folder_names; - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - folder_names.push_back(subdir.Basename()); - } - } - if (folder_names.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } - - std::sort(folder_names.begin(), folder_names.end()); - int32_t label_count = 0; - (void)std::transform(folder_names.begin(), folder_names.end(), std::back_inserter(*output_class_indexing), - [&label_count](const auto &elem) { - auto p = std::pair>(elem, {label_count}); - label_count++; - return p; - }); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h deleted file mode 100644 index ac09d6edc..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMAGE_FOLDER_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMAGE_FOLDER_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// Forward declares -template -class Queue; - -using ImageLabelPair = std::shared_ptr>; -using FolderImagesPair = std::shared_ptr>>; - -class ImageFolderOp : public MappableLeafOp { - public: -#ifdef ENABLE_PYTHON - // Constructor - // @param int32_t num_wkrs - Num of workers reading images in parallel - // @param std::string - dir directory of ImageNetFolder - // @param int32_t queue_size - connector queue size - // @param bool recursive - read recursively - // @param bool do_decode - decode the images after reading - // @param std::set &exts - set of file extensions to read, if empty, read everything under the dir - // @param std::map &map- map of folder name and class id - // @param std::unique_ptr data_schema - schema of data - // @param py::function decrypt - Image decryption function, which accepts the path of the encrypted image file - // and returns the decrypted bytes data. Default: None, no decryption. - ImageFolderOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool recursive, bool do_decode, - const std::set &exts, const std::map &map, - std::unique_ptr data_schema, std::shared_ptr sampler, - py::function decrypt = py::none()); -#else - // Constructor - // @param int32_t num_wkrs - Num of workers reading images in parallel - // @param std::string - dir directory of ImageNetFolder - // @param int32_t queue_size - connector queue size - // @param bool recursive - read recursively - // @param bool do_decode - decode the images after reading - // @param std::set &exts - set of file extensions to read, if empty, read everything under the dir - // @param std::map &map- map of folder name and class id - // @param std::unique_ptr data_schema - schema of data - ImageFolderOp(int32_t num_wkrs, std::string file_dir, int32_t queue_size, bool recursive, bool do_decode, - const std::set &exts, const std::map &map, - std::unique_ptr data_schema, std::shared_ptr sampler); -#endif - - /// Destructor. - ~ImageFolderOp() = default; - - /// Initialize ImageFOlderOp related var, calls the function to walk all files - /// @param - std::string dir file directory to ImageNetFolder - /// @return Status The status code returned - Status PrepareData() override; - - // Worker thread pulls a number of IOBlock from IOBlock Queue, make a TensorRow and push it to Connector - // @param int32_t workerId - id of each worker - // @return Status The status code returned - Status PrescanWorkerEntry(int32_t worker_id); - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class - // @param (std::map> * map - key label, val all ids for this class - // @return Status The status code returned - Status GetClassIds(std::map> *cls_ids) const override; - - /// A print method typically used for debugging - /// @param out - /// @param show_all - void Print(std::ostream &out, bool show_all) const override; - - /// This function is a hack! It is to return the num_class and num_rows. The result - /// returned by this function may not be consistent with what image_folder_op is going to return - /// user this at your own risk! - static Status CountRowsAndClasses(const std::string &path, const std::set &exts, int64_t *num_rows, - int64_t *num_classes, std::map class_index); - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "ImageFolderOp"; } - - // DatasetName name getter - // \return DatasetName of the current Op - virtual std::string DatasetName(bool upper = false) const { return upper ? "ImageFolder" : "image folder"; } - - //// \brief Base-class override for GetNumClasses - //// \param[out] num_classes the number of classes - //// \return Status of the function - Status GetNumClasses(int64_t *num_classes) override; - - //// \brief Gets the class indexing - //// \param[out] output_class_indexing The index mapping of dataset - //// \return Status The status code return - Status GetClassIndexing(std::vector>> *output_class_indexing) override; - - protected: - // Load a tensor row according to a pair - // @param row_id_type row_id - id for this tensor row - // @param ImageLabelPair pair - - // @param TensorRow row - image & label read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// @param std::string & dir - dir to walk all images - /// @param int64_t * cnt - number of non folder files under the current dir - /// @return - virtual Status RecursiveWalkFolder(Path *dir); - - /// start walking of all dirs - /// @return - Status StartAsyncWalk(); - - // Called first when function is called - // @return - Status RegisterAndLaunchThreads() override; - - /// Private function for computing the assignment of the column name map. - /// @return - Status - Status ComputeColMap() override; - - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - Status InitPullMode() override; - - std::string folder_path_; // directory of image folder - bool recursive_; - bool decode_; - std::set extensions_; // extensions allowed - std::map class_index_; - std::unique_ptr data_schema_; - int64_t sampler_ind_; - uint64_t dirname_offset_; - std::vector image_label_pairs_; - std::unique_ptr> folder_name_queue_; - std::unique_ptr> image_name_queue_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMAGE_FOLDER_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.cc deleted file mode 100644 index a235886db..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.cc +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr int32_t kNumClasses = 2; - -IMDBOp::IMDBOp(int32_t num_workers, const std::string &file_dir, int32_t queue_size, const std::string &usage, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(std::move(file_dir)), - usage_(usage), - data_schema_(std::move(data_schema)), - sampler_ind_(0) {} - -Status IMDBOp::PrepareData() { - std::vector usage_list; - if (usage_ == "all") { - usage_list.push_back("train"); - usage_list.push_back("test"); - } else { - usage_list.push_back(usage_); - } - std::vector label_list = {"pos", "neg"}; - // get abs path for folder_path_ - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, imdb dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, imdb dataset dir: " + folder_path_ + " does not exist."); - } - Path base_dir(realpath.value()); - for (auto usage : usage_list) { - for (auto label : label_list) { - Path dir = base_dir / usage / label; - RETURN_IF_NOT_OK(GetDataByUsage(dir.ToString(), label)); - } - } - text_label_pairs_.shrink_to_fit(); - num_rows_ = text_label_pairs_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } - return Status::OK(); -} - -// Load 1 TensorRow (text, label) using 1 std::pair. 1 function call produces 1 TensorTow -Status IMDBOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair pair_ptr = text_label_pairs_[row_id]; - std::shared_ptr text, label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(pair_ptr.second, &label)); - RETURN_IF_NOT_OK(LoadFile(pair_ptr.first, &text)); - - (*trow) = TensorRow(row_id, {std::move(text), std::move(label)}); - trow->setPath({pair_ptr.first, std::string("")}); - return Status::OK(); -} - -void IMDBOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\n" - << DatasetName(true) << " directory: " << folder_path_ << "\nUsage: " << usage_ << "\n\n"; - } -} - -// Derived from RandomAccessOp -Status IMDBOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || text_label_pairs_.empty()) { - if (text_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid dataset dir, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR], Map containing text-index pair is nullptr or has been set in other place, " - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < text_label_pairs_.size(); ++i) { - (*cls_ids)[text_label_pairs_[i].second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status IMDBOp::GetDataByUsage(const std::string &folder, const std::string &label) { - Path dir_usage_label(folder); - if (!dir_usage_label.Exists() || !dir_usage_label.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid parameter, dataset dir may not exist or is not a directory: " + folder); - } - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir_usage_label); - CHECK_FAIL_RETURN_UNEXPECTED(dir_itr != nullptr, - "Invalid path, failed to open imdb dir: " + folder + ", permission denied."); - std::map text_label_map; - while (dir_itr->HasNext()) { - Path file = dir_itr->Next(); - text_label_map[file.ToString()] = (label == "pos") ? 1 : 0; - } - for (auto item : text_label_map) { - text_label_pairs_.emplace_back(std::make_pair(item.first, item.second)); - } - return Status::OK(); -} - -Status IMDBOp::CountRows(const std::string &path, const std::string &usage, int64_t *num_rows) { - RETURN_UNEXPECTED_IF_NULL(num_rows); - // get abs path for folder_path_ - auto abs_path = FileUtils::GetRealPath(path.c_str()); - if (!abs_path.has_value()) { - MS_LOG(ERROR) << "Invalid file path, imdb dataset dir: " << path << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, imdb dataset dir: " + path + " does not exist."); - } - Path data_dir(abs_path.value()); - std::vector all_dirs_list = {"pos", "neg"}; - std::vector usage_list; - if (usage == "all") { - usage_list.push_back("train"); - usage_list.push_back("test"); - } else { - usage_list.push_back(usage); - } - int64_t row_cnt = 0; - for (int32_t ind = 0; ind < usage_list.size(); ++ind) { - Path texts_dir_usage_path = data_dir / usage_list[ind]; - CHECK_FAIL_RETURN_UNEXPECTED( - texts_dir_usage_path.Exists() && texts_dir_usage_path.IsDirectory(), - "Invalid path, dataset path may not exist or is not a directory: " + texts_dir_usage_path.ToString()); - - for (auto dir : all_dirs_list) { - Path texts_dir_usage_dir_path((texts_dir_usage_path / dir).ToString()); - std::shared_ptr dir_iter = Path::DirIterator::OpenDirectory(&texts_dir_usage_dir_path); - CHECK_FAIL_RETURN_UNEXPECTED(dir_iter != nullptr, - "Invalid path, failed to open imdb dir: " + path + ", permission denied."); - RETURN_UNEXPECTED_IF_NULL(dir_iter); - while (dir_iter->HasNext()) { - row_cnt++; - } - } - } - (*num_rows) = row_cnt; - return Status::OK(); -} - -Status IMDBOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Get number of classes -Status IMDBOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - *num_classes = kNumClasses; - return Status::OK(); -} - -Status IMDBOp::LoadFile(const std::string &file, std::shared_ptr *out_row) { - RETURN_UNEXPECTED_IF_NULL(out_row); - - std::ifstream handle(file, std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file); - } - - std::string line; - // IMDB just have a line for every txt. - while (getline(handle, line)) { - if (line.empty()) { - continue; - } - auto rc = LoadTensor(line, out_row); - if (rc.IsError()) { - handle.close(); - return rc; - } - } - handle.close(); - return Status::OK(); -} - -Status IMDBOp::LoadTensor(const std::string &line, std::shared_ptr *out_row) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(line, &tensor)); - *out_row = std::move(tensor); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h deleted file mode 100644 index 919615eb6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMDB_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMDB_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// Forward declares -template -class Queue; - -class IMDBOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] int32_t num_workers - num of workers reading texts in parallel. - /// \param[in] std::string dataset_dir - dir directory of IMDB dataset. - /// \param[in] int32_t queue_size - connector queue size. - /// \param[in] std::string usage - the type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] DataSchema data_schema - the schema of each column in output data. - /// \param[in] std::unique_ptr sampler - sampler tells Folder what to read. - IMDBOp(int32_t num_workers, const std::string &dataset_dir, int32_t queue_size, const std::string &usage, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~IMDBOp() = default; - - /// \brief Parse IMDB data. - /// \return Status - The status code returned. - Status PrepareData() override; - - /// \brief Method derived from RandomAccess Op, enable Sampler to get all ids for each class - /// \param[in] map cls_ids - key label, val all ids for this class - /// \return Status - The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief This function return the num_rows. - /// \param[in] std::string path - dir directory of IMDB dataset. - /// \param[in] std::string usage - the type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[out] int64_t *num_rows - output arg that will hold the actual dataset size. - /// \return Status - The status code returned. - static Status CountRows(const std::string &path, const std::string &usage, int64_t *num_rows); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "IMDBOp"; } - - /// \brief Dataset name getter. - /// \param[in] upper Whether to get upper name. - /// \return Dataset name of the current Op. - virtual std::string DatasetName(bool upper = false) const { return upper ? "IMDB" : "imdb"; } - - /// \brief Base-class override for GetNumClasses - /// \param[out] int64_t *num_classes - the number of classes - /// \return Status - The status code returned. - Status GetNumClasses(int64_t *num_classes) override; - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] uint64_t row_id - row_id need to load. - /// \param[out] TensorRow *row - text & task read into this tensor row. - /// \return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Parses a single row and puts the data into a tensor table. - /// \param[in] string line - the content of the row. - /// \param[out] Tensor *out_row - the id of the row filled in the tensor table. - /// \return Status - The status code returned. - Status LoadTensor(const std::string &line, std::shared_ptr *out_row); - - /// \brief Reads a text file and loads the data into Tensor. - /// \param[in] string file - the file to read. - /// \param[out] Tensor *out_row - the id of the row filled in the tensor table. - /// \return Status - The status code returned. - Status LoadFile(const std::string &file, std::shared_ptr *out_row); - - /// \brief Called first when function is called - /// \param[in] string folder - the folder include files. - /// \param[in] string label - the name of label. - /// \return Status - The status code returned. - Status GetDataByUsage(const std::string &folder, const std::string &label); - - /// \brief function for computing the assignment of the column name map. - /// \return Status - The status code returned. - Status ComputeColMap() override; - - std::string folder_path_; // directory of text folder - std::string usage_; - int64_t sampler_ind_; - std::unique_ptr data_schema_; - std::vector> text_label_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IMDB_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.cc deleted file mode 100644 index 08c819f38..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.cc +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" - -#include -#include - -namespace mindspore { -namespace dataset { -// IOBlock Class // - -// Constructor of the IOBlock (1). A simpler one for the case when the block only has 1 key. -IOBlock::IOBlock(int64_t inKey, IOBlockFlags io_block_flags) : index_keys_(1, inKey), io_block_flags_(io_block_flags) {} - -// Constructor of the IOBlock (2) -IOBlock::IOBlock(const std::vector &in_keys, IOBlockFlags io_block_flags) : io_block_flags_(io_block_flags) { - index_keys_.insert(index_keys_.end(), in_keys.begin(), in_keys.end()); -} - -// Constructor of the IOBlock (3). A special IOBlock that is used for control messaging. -IOBlock::IOBlock(IOBlockFlags io_block_flags) : io_block_flags_(io_block_flags) {} - -// Fetches the first key from this block -Status IOBlock::GetKey(int64_t *out_key) const { - if (out_key == nullptr || index_keys_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Failed to get the key from IOBlock."); - } - *out_key = index_keys_[0]; - return Status::OK(); -} - -// Fetches the list of keys from this block. -Status IOBlock::GetKeys(std::vector *out_keys) const { - if (out_keys == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Output arg for GetKeys is null."); - } - *out_keys = index_keys_; // vector copy assign - return Status::OK(); -} - -// FilenameBlock derived class // - -// Constructor of the FilenameBlock (1) -FilenameBlock::FilenameBlock(int64_t key, int64_t start_offset, int64_t end_offset, IOBlockFlags io_block_flags) - : IOBlock(key, io_block_flags), start_offset_(start_offset), end_offset_(end_offset) {} - -// Constructor of the FilenameBlock (2). A special IOBlock that is used for control messaging. -FilenameBlock::FilenameBlock(IOBlockFlags io_block_flags) - : IOBlock(io_block_flags), start_offset_(kInvalidOffset), end_offset_(kInvalidOffset) {} - -// Gets the filename from the block using the provided index container -Status FilenameBlock::GetFilename(std::string *out_filename, const AutoIndexObj &index) const { - if (out_filename == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Failed to get filename from FilenameBlock."); - } - - // a FilenameBlock only has one key. Call base class method to fetch that key - int64_t fetched_key; - RETURN_IF_NOT_OK(IOBlock::GetKey(&fetched_key)); - - // Do an index lookup using that key to get the filename. - auto r = index.Search(fetched_key); - if (r.second) { - auto &it = r.first; - *out_filename = it.value(); - } else { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Could not find filename from index."); - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h deleted file mode 100644 index 290b481f1..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IO_BLOCK_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IO_BLOCK_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/auto_index.h" - -namespace mindspore { -namespace dataset { -// The IOBlock class is used to describe a "unit of work" that a storage leaf operator worker thread -// is responsible for acting on. -// The IOBlocks and it's derived classes abstracts a key-store and key-lookup interface where each -// block contains 1 to n keys, and the keys are used in conjunction with an index to provide the meta -// information for satisfying an IO request. -class IOBlock { - public: - enum IOBlockFlags : uint32_t { - kFlagNone = 0, - kFlagEOE = 1U, // end of IOBlocks for one epoch - kFlagEOF = 1U << 1, // end of IOBlocks for entire program - kFlagWait = 1U << 2, // control signal for workers to suspend operations - kFlagQuit = 1U << 3 // control signal for workers to quit - }; - - // Constructor of the IOBlock (1). A simpler one for the case when the block only has 1 key. - // @param inKey - A single key to add into the block - // @param io_block_flags - The flag setting for the block - IOBlock(int64_t inKey, IOBlockFlags io_block_flags); - - // Constructor of the IOBlock (2). - // @param in_keys - A vector of keys to add into the block - // @param io_block_flags - The flag setting for the block - IOBlock(const std::vector &in_keys, IOBlockFlags io_block_flags); - - // Constructor of the IOBlock (3). A special IOBlock that is used for control messaging. - // @param io_block_flags - The flag setting for the block - explicit IOBlock(IOBlockFlags io_block_flags); - - // Destructor - virtual ~IOBlock() = default; - - // Fetches the first key from the block. - // @note Only useful if you know the block only has 1 key. - // @return A copy of the first key from the block - // @return Status The status code returned - Status GetKey(int64_t *out_key) const; - - // Fetches the list of keys from this block. - // @param out_keys - A copy of the vector of keys from the block. - // @return Status The status code returned - Status GetKeys(std::vector *out_keys) const; - - // Does this block have the eoe flag turned on? - // @return T/F if the IOBlock is eoe - bool eoe() const { return static_cast(io_block_flags_) & static_cast(kFlagEOE); } - - // Does this block have the eof flag turned on? - // @return T/F if the IOBlock is eof - bool eof() const { return static_cast(io_block_flags_) & static_cast(kFlagEOF); } - - // Does this block have the wait flag turned on? - // @return T/F is the IOBlock is wait - bool wait() const { return static_cast(io_block_flags_) & static_cast(kFlagWait); } - - // Adds a key to this block - // @param key - The key to add to this block - void AddKey(int64_t key) { index_keys_.push_back(key); } - - std::string FlagName() const { - switch (io_block_flags_) { - case IOBlockFlags::kFlagNone: - return "Data"; - case IOBlockFlags::kFlagEOE: - return "EOE"; - case IOBlockFlags::kFlagEOF: - return "EOF"; - case IOBlockFlags::kFlagWait: - return "Wait"; - case IOBlockFlags::kFlagQuit: - return "Quit"; - default: - return "Unknown"; - } - } - - protected: - std::vector index_keys_; // keys used for lookups to the meta info for the data - IOBlockFlags io_block_flags_; -}; // class IOBlock - -const int64_t kInvalidOffset = -1; - -// The Filename block derived class implements a style of IO block where each block contains only a -// single key that maps to a filename. -class FilenameBlock : public IOBlock { - public: - // Constructor of the FilenameBlock (1) - // @param key - The key identifier that can be used to find the data for this block - // @param start_offset - Start offset - // @param end_offset - End offset - // @param io_block_flags - The flag setting for the block - FilenameBlock(int64_t key, int64_t start_offset, int64_t end_offset, IOBlockFlags io_block_flags); - - // Constructor of the FilenameBlock (2). A special IOBlock that is used for control messaging. - // @param io_block_flags - The flag setting for the block - explicit FilenameBlock(IOBlockFlags io_block_flags); - - // Destructor - ~FilenameBlock() = default; - - // Gets the filename from the block using the provided index container - // @param out_filename - The filename to add to the block - // @param index - The index to perform lookup against - // @return Status The status code returned - Status GetFilename(std::string *out_filename, const AutoIndexObj &index) const; - - // Get the start offset of file - // @return int64_t - Start offset - int64_t GetStartOffset() const { return start_offset_; } - - // Get the end offset of the file - // @return int64_t - Start offset - int64_t GetEndOffset() const { return end_offset_; } - - private: - int64_t start_offset_; - int64_t end_offset_; -}; // class TFBlock -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IO_BLOCK_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.cc deleted file mode 100644 index e545b0c79..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.cc +++ /dev/null @@ -1,543 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -IWSLTOp::IWSLTOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, std::unique_ptr data_schema, - IWSLTType type, const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, const std::string &valid_set, - const std::string &test_set) - : NonMappableLeafOp(num_workers, worker_connector_size, num_samples, op_connector_size, shuffle_files, num_devices, - device_id), - iwslt_type_(type), - data_schema_(std::move(data_schema)), - dataset_dir_(dataset_dir), - usage_(usage), - language_pair_(language_pair), - valid_set_(valid_set), - test_set_(test_set) {} - -Status IWSLTOp::Init() { - RETURN_IF_NOT_OK(this->GetFiles()); - RETURN_IF_NOT_OK(filename_index_->insert(src_target_file_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(src_target_file_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - return Status::OK(); -} - -std::vector IWSLTOp::Split(const std::string &s, const std::string &delim) { - std::vector res; - std::string::size_type pos1 = 0; - std::string::size_type pos2 = s.find(delim); - while (std::string::npos != pos2) { - res.push_back(s.substr(pos1, pos2 - pos1)); - - pos1 = pos2 + delim.size(); - pos2 = s.find(delim, pos1); - } - if (pos1 != s.length()) { - res.push_back(s.substr(pos1)); - } - return res; -} - -Status IWSLTOp::Trim(std::string *text, const std::string &character) { - RETURN_UNEXPECTED_IF_NULL(text); - CHECK_FAIL_RETURN_UNEXPECTED(!text->empty(), "Invalid file, read an empty line."); - (void)text->erase(0, text->find_first_not_of(character)); - (void)text->erase(text->find_last_not_of(character) + 1); - return Status::OK(); -} - -Status IWSLTOp::LoadTensor(const std::string &line, TensorRow *out_row, size_t index) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(line, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -Status IWSLTOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - std::ifstream handle(file, std::ifstream::in); - std::string line; - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + DatasetName() + " file: " + file); - } - - int64_t rows_total = 0; - while (getline(handle, line)) { - if (line.empty()) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - const int kColumnSize = 2; - TensorRow tRow(kColumnSize, nullptr); - tRow.setPath({file, file}); - - // Remove the newline character. - auto s = Trim(&line, "\n"); - if (s != Status::OK()) { - handle.close(); - return s; - } - s = Trim(&line, "\r"); - if (s != Status::OK()) { - handle.close(); - return s; - } - std::vector sentence_list = Split(line, "#*$"); - if (!sentence_list.empty() && sentence_list.size() == kColumnSize) { - s = LoadTensor(sentence_list[0], &tRow, 0); - if (s != Status::OK()) { - handle.close(); - return s; - } - s = LoadTensor(sentence_list[1], &tRow, 1); - if (s != Status::OK()) { - handle.close(); - return s; - } - s = jagged_rows_connector_->Add(worker_id, std::move(tRow)); - if (s != Status::OK()) { - handle.close(); - return s; - } - rows_total++; - } - } - handle.close(); - return Status::OK(); -} - -Status IWSLTOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -void IWSLTOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nIWSLT files list:\n"; - for (int i = 0; i < src_target_file_list_.size(); ++i) { - out << " " << src_target_file_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -int64_t IWSLTOp::CountFileRows(const std::string &file) { - std::ifstream handle(file, std::ifstream::in); - if (!handle.is_open()) { - MS_LOG(ERROR) << "Invalid file, failed to open file: " << file; - return 0; - } - - std::string line; - int64_t count = 0; - while (getline(handle, line)) { - if (!line.empty()) { - count++; - } - } - handle.close(); - return count; -} - -Status IWSLTOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count = CountFileRows(it.value()); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (int i = 0; i < src_target_file_list_.size(); ++i) { - ss << " " << src_target_file_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + ": " + file_list); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -Status IWSLTOp::ComputeColMap() { - // Set the column name mapping (base class field). - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status IWSLTOp::CountTotalRows(IWSLTType type, const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, const std::string &valid_set, - const std::string &test_set, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - int32_t num_workers = GlobalContext::config_manager()->num_parallel_workers(); - int32_t connector_que_size = GlobalContext::config_manager()->op_connector_size(); - int32_t worker_connector_size = GlobalContext::config_manager()->worker_connector_size(); - const int32_t shard_id = 0; - const int32_t num_shards = 1; - const int64_t num_samples = 0; - bool shuffle_files = false; - // Do internal Schema generation. - auto schema = std::make_unique(); - - // Create and initialize. - std::shared_ptr op = std::make_shared( - num_workers, num_samples, worker_connector_size, connector_que_size, shuffle_files, num_shards, shard_id, - std::move(schema), type, dataset_dir, usage, language_pair, valid_set, test_set); - RETURN_IF_NOT_OK(op->Init()); - - *count = 0; - std::vector file_list = op->FileNames(); - for (auto file : file_list) { - *count += op->CountFileRows(file); - } - return Status::OK(); -} - -Status LoadXmlDocument(XMLDocument *xml_document, const std::string &file_path, XMLElement **doc) { - RETURN_UNEXPECTED_IF_NULL(xml_document); - XMLError e = xml_document->LoadFile(common::SafeCStr(file_path)); - if (e != XMLError::XML_SUCCESS) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to load xml file: " + file_path); - } - XMLElement *root = xml_document->RootElement(); - if (root == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid data, failed to load root element for xml file."); - } - XMLElement *firstChild = root->FirstChildElement(); - if (firstChild == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid data, no first child found in " + file_path); - } - *doc = firstChild->FirstChildElement("doc"); - if (*doc == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid data, no doc found in " + file_path); - } - return Status::OK(); -} - -Status IWSLTOp::CleanXmlFile(const std::string &src_file_path, const std::string &target_file_path, - const std::string &new_file_path) { - XMLDocument xml_document1, xml_document2; - XMLElement *src_doc = nullptr; - XMLElement *target_doc = nullptr; - - RETURN_IF_NOT_OK(LoadXmlDocument(&xml_document1, src_file_path, &src_doc)); - RETURN_IF_NOT_OK(LoadXmlDocument(&xml_document2, target_file_path, &target_doc)); - std::string src_content, target_content; - std::ofstream new_file(new_file_path, std::ofstream::out); - CHECK_FAIL_RETURN_UNEXPECTED(new_file.is_open(), "Invalid file, failed to open file: " + new_file_path); - - while (src_doc != nullptr && target_doc != nullptr) { - XMLElement *src_seg = src_doc->FirstChildElement("seg"); - XMLElement *target_seg = target_doc->FirstChildElement("seg"); - while (src_seg != nullptr && target_seg != nullptr) { - src_content = src_seg->GetText(); - target_content = target_seg->GetText(); - auto s = Trim(&src_content, " "); - if (s != Status::OK()) { - new_file.close(); - return s; - } - s = Trim(&target_content, " "); - if (s != Status::OK()) { - new_file.close(); - return s; - } - src_seg = src_seg->NextSiblingElement(); - target_seg = target_seg->NextSiblingElement(); - new_file << (src_content + "#*$" + target_content + "\n"); - } - src_doc = src_doc->NextSiblingElement(); - target_doc = target_doc->NextSiblingElement(); - } - - new_file.close(); - - ChangeFileMode(new_file_path, S_IRUSR | S_IWUSR); - - return Status::OK(); -} - -bool IWSLTOp::IsContainTags(const std::string &content) { - std::vector xml_tags = {" &src_file_list, - const std::vector &target_file_list, - std::vector *src_target_file_list) { - RETURN_UNEXPECTED_IF_NULL(src_target_file_list); - std::string::size_type position; - std::string new_path; - std::string src_path, target_path; - for (int i = 0; i < src_file_list.size(); i++) { - src_path = src_file_list[i]; - target_path = target_file_list[i]; - - // Add new train file name. - position = src_path.find(".tags"); - if (position != std::string::npos) { - new_path = src_path; - const int kTagSize = 5; - const int kSuffixSize = 3; - new_path = new_path.replace(new_path.find(".tags"), kTagSize, ""); - new_path = new_path.substr(0, new_path.length() - kSuffixSize); - - // Write data to the new file path. - RETURN_IF_NOT_OK(CleanTagFile(src_path, target_path, new_path)); - src_target_file_list->push_back(new_path); - } else { - // Add new valid or test file name. - // Delete suffix. - const int kSuffixXMLSize = 7; - new_path = src_path; - new_path = new_path.substr(0, new_path.length() - kSuffixXMLSize); - // Write data to the new file path. - RETURN_IF_NOT_OK(CleanXmlFile(src_path, target_path, new_path)); - src_target_file_list->push_back(new_path); - } - } - return Status::OK(); -} - -std::string IWSLTOp::GenerateIWSLT2016TagsFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &suffix) { - Path src_language_path(src_language); - Path target_language_path(target_language); - Path sub_dir(src_language + "-" + target_language); - Path file_name("train.tags." + src_language + "-" + target_language + "." + suffix); - Path file_path = dir / "texts" / src_language_path / target_language_path / sub_dir / file_name; - return file_path.ToString(); -} - -std::string IWSLTOp::GenerateIWSLT2016XMLFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &set_type, - const std::string &suffix) { - Path src_language_path(src_language); - Path target_language_path(target_language); - Path sub_dir(src_language + "-" + target_language); - Path file_name("IWSLT16.TED." + set_type + "." + src_language + "-" + target_language + "." + suffix + ".xml"); - Path file_path = dir / "texts" / src_language_path / target_language_path / sub_dir / file_name; - return file_path.ToString(); -} - -std::string IWSLTOp::GenerateIWSLT2017TagsFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &suffix) { - Path sub_const_dir("texts"); - Path sub_src_language_dir("DeEnItNlRo"); - Path sub_tgt_language_dir("DeEnItNlRo"); - Path sub_src_tgt_dir("DeEnItNlRo-DeEnItNlRo"); - Path file_name("train.tags." + src_language + "-" + target_language + "." + suffix); - Path file_path = dir / sub_const_dir / sub_src_language_dir / sub_tgt_language_dir / sub_src_tgt_dir / file_name; - return file_path.ToString(); -} - -std::string IWSLTOp::GenerateIWSLT2017XMLFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &set_type, - const std::string &suffix) { - Path sub_const_dir("texts"); - Path sub_src_language_dir("DeEnItNlRo"); - Path sub_tgt_language_dir("DeEnItNlRo"); - Path sub_src_tgt_dir("DeEnItNlRo-DeEnItNlRo"); - Path file_name("IWSLT17.TED." + set_type + "." + src_language + "-" + target_language + "." + suffix + ".xml"); - Path file_path = dir / sub_const_dir / sub_src_language_dir / sub_tgt_language_dir / sub_src_tgt_dir / file_name; - return file_path.ToString(); -} - -Status IWSLTOp::GetFiles() { - std::vector src_path_list; - std::vector target_path_list; - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_dataset_dir.has_value(), "Get real path failed: " + dataset_dir_); - Path root_dir(real_dataset_dir.value()); - - if (iwslt_type_ == kIWSLT2016) { - if (usage_ == "train" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2016TagsFileName(root_dir, language_pair_[0], language_pair_[1], language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2016TagsFileName(root_dir, language_pair_[0], language_pair_[1], language_pair_[1])); - } - if (usage_ == "valid" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2016XMLFileName(root_dir, language_pair_[0], language_pair_[1], valid_set_, language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2016XMLFileName(root_dir, language_pair_[0], language_pair_[1], valid_set_, language_pair_[1])); - } - if (usage_ == "test" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2016XMLFileName(root_dir, language_pair_[0], language_pair_[1], test_set_, language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2016XMLFileName(root_dir, language_pair_[0], language_pair_[1], test_set_, language_pair_[1])); - } - } else { - if (usage_ == "train" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2017TagsFileName(root_dir, language_pair_[0], language_pair_[1], language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2017TagsFileName(root_dir, language_pair_[0], language_pair_[1], language_pair_[1])); - } - if (usage_ == "valid" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2017XMLFileName(root_dir, language_pair_[0], language_pair_[1], valid_set_, language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2017XMLFileName(root_dir, language_pair_[0], language_pair_[1], valid_set_, language_pair_[1])); - } - if (usage_ == "test" || usage_ == "all") { - src_path_list.push_back( - GenerateIWSLT2017XMLFileName(root_dir, language_pair_[0], language_pair_[1], test_set_, language_pair_[0])); - target_path_list.push_back( - GenerateIWSLT2017XMLFileName(root_dir, language_pair_[0], language_pair_[1], test_set_, language_pair_[1])); - } - } - RETURN_IF_NOT_OK(GenerateNewFile(src_path_list, target_path_list, &src_target_file_list_)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h deleted file mode 100644 index a366ff5e6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IWSLT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IWSLT_OP_H_ - -#include -#include -#include - -#include "./tinyxml2.h" - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -using tinyxml2::XMLDocument; -using tinyxml2::XMLElement; -using tinyxml2::XMLError; - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -/// \class IWSLTOp. -/// \brief A Op derived class to represent IWSLT Op. -class IWSLTOp : public NonMappableLeafOp { - public: - enum IWSLTType { kIWSLT2016, kIWSLT2017 }; - - /// \brief Constructor of IWSLTOp. - /// \param[in] num_workers Number of worker threads reading data from yelp_review files. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. - /// \param[in] data_schema Schema of dataset. - /// \param[in] type Type of data set read. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of this dataset, can be "train", "test", "valid" or "all" data. - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] valid_set A string to identify validation set. - /// \param[in] test_set A string to identify test set. - IWSLTOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, std::unique_ptr, IWSLTType type, - const std::string &dataset_dir, const std::string &usage, const std::vector &language_pair, - const std::string &valid_set, const std::string &test_set); - - /// \brief Destructor. - ~IWSLTOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Instantiates the internal queues and connectors. - /// \return Status The error code returned. - Status Init() override; - - /// \brief Function to count the number of samples in the IWSLT dataset. - /// \param[in] type IWSLT data set version, which can be kIWSLT2016 and kIWSLT2017. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of IWSLT2017, can be "train", "valid", "test" or "all". - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] valid_set A string to identify validation set. - /// \param[in] test_set A string to identify test set. - /// \param[out] count The total number of rows in file. - /// \return Status The status code returned. - static Status CountTotalRows(IWSLTType type, const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, const std::string &valid_set, - const std::string &test_set, int64_t *count); - - /// \brief Op name getter. - /// \return std::string Name of the current Op. - std::string Name() const override { return "IWSLTOp"; } - - /// \brief DatasetName name getter. - /// \param[in] upper If true, the return value is uppercase, otherwise, it is lowercase. - /// \return std::string DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "IWSLT" : "iwslt"; } - - // \brief File names getter. - // \return std::vector Vector of the input file names. - std::vector FileNames() { return src_target_file_list_; } - - private: - /// \brief Split string based on a character delimiter. - /// \param[in] s The input string. - /// \param[in] delim The delimiter. - /// \return std::vector The result after segmentation. - std::vector Split(const std::string &s, const std::string &delim); - - /// \brief Remove the characters specified at the beginning and end. - /// \param[in] text The input string. - /// \param[in] character The removed character. - /// \return Status The status code returned. - Status Trim(std::string *text, const std::string &character); - - /// \brief Function to count the number of samples in one data file. - /// \param[in] file Path to the data file. - /// \return int64_t The total number of rows in file. - int64_t CountFileRows(const std::string &file); - - /// \brief Parses a single row and puts the data into a tensor table. - /// \param[in] line The content of the row. - /// \param[out] out_row Output tensor. - /// \param[in] index The id of the row filled in the tensor table. - /// \return Status The status code returned. - Status LoadTensor(const std::string &line, TensorRow *out_row, size_t index); - - /// \brief Reads a IWSLT file and loads the data into multiple TensorRows. - /// \param[in] file The file to read. - /// \param[in] start_offset The start offset of file. - /// \param[in] end_offset The end offset of file. - /// \param[in] worker_id The id of the worker that is executing this function. - /// \return Status The status code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - /// \brief Fill the IOBlockQueue. - /// \param[in] i_keys Keys of file to fill to the IOBlockQueue. - /// \return Status The status code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - /// \brief Calculate number of rows in each shard. - /// \return Status The status code returned. - Status CalculateNumRowsPerShard() override; - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - /// \brief Write the data of the source file and the target file to a new file after cleaning. - /// \param[in] src_file_path Source file path. - /// \param[in] target_file_path Target file path. - /// \param[in] new_file_path Write data to new file path. - /// \return Status The status code returned. - Status CleanXmlFile(const std::string &src_file_path, const std::string &target_file_path, - const std::string &new_file_path); - - /// \brief Determine whether the centent contains the specified label. - /// \param[in] content This content to be determined. - /// \return bool If it contains, return true, otherwise, return false. - bool IsContainTags(const std::string &content); - - /// \brief Write the data of the source file and the target file to a new file after cleaning. - /// \param[in] src_file_path Source file path. - /// \param[in] target_file_path Target file path. - /// \param[in] new_file_path Write data to new file path. - /// \return Status The status code returned. - Status CleanTagFile(const std::string &file_path, const std::string &target_file_path, - const std::string &new_file_path); - - // \brief Get all files in the dataset_dir_. - // \return Status The status code returned. - Status GetFiles(); - - /// \brief Generate IWSLT2016 training data set file list. - /// \param[in] dir The directory where the files are stored. - /// \param[in] src_language The source language type. - /// \param[in] target_language The target language type. - /// \param[in] suffix The file suffix. - /// \return std::string The file path. - std::string GenerateIWSLT2016TagsFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &suffix); - - /// \brief Generate IWSLT2016 valid data set or test data set file list. - /// \param[in] dir The directory where the files are stored. - /// \param[in] src_language The source language type. - /// \param[in] target_language The target language type. - /// \param[in] set_type The type of data set read. - /// \param[in] suffix The file suffix. - /// \return std::string The file path. - std::string GenerateIWSLT2016XMLFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &set_type, - const std::string &suffix); - - /// \brief Generate IWSLT2017 training data set file list. - /// \param[in] dir The directory where the files are stored. - /// \param[in] src_language The source language type. - /// \param[in] target_language The target language type. - /// \param[in] suffix The file suffix. - /// \return std::string The file path. - std::string GenerateIWSLT2017TagsFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &suffix); - - /// \brief Generate IWSLT2016 valid data set or test data set file list. - /// \param[in] dir The directory where the files are stored. - /// \param[in] src_language The source language type. - /// \param[in] target_language The target language type. - /// \param[in] set_type The type of data set read. - /// \param[in] suffix The file suffix. - /// \return std::string The file path. - std::string GenerateIWSLT2017XMLFileName(Path dir, const std::string &src_language, - const std::string &target_language, const std::string &set_type, - const std::string &suffix); - - /// \brief Generate new file path and write data. - /// \param[in] src_path_list The source file path. - /// \param[in] target_path_list The target file path. - /// \param[out] src_target_file_list The newly generated file path list. - /// \return Status The status code returned. - Status GenerateNewFile(const std::vector &src_file_list, - const std::vector &target_file_list, - std::vector *src_target_file_list); - - IWSLTType iwslt_type_; - std::unique_ptr data_schema_; - std::vector src_target_file_list_; - std::string dataset_dir_; - std::string usage_; - std::vector language_pair_; - std::string valid_set_; - std::string test_set_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_IWSLT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.cc deleted file mode 100644 index 892b1c29b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.cc +++ /dev/null @@ -1,323 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h" - -#include -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr int kLabelNameIndex = 0; -constexpr int kTruncatedIndex = 1; -constexpr int kOccludedIndex = 2; -constexpr int kAlphaIndex = 3; -constexpr int kXMinIndex = 4; -constexpr int kYMinIndex = 5; -constexpr int kXMaxIndex = 6; -constexpr int kYMaxIndex = 7; -constexpr int kFirstDimensionIndex = 8; -constexpr int kSecondDimensionIndex = 9; -constexpr int kThirdDimensionIndex = 10; -constexpr int kFirstLocationIndex = 11; -constexpr int kSecondLocationIndex = 12; -constexpr int kThirdLocationIndex = 13; -constexpr int kRotationYIndex = 14; -constexpr int kTotalParamNums = 14; -const char kImagesFolder[] = "data_object_image_2"; -const char kAnnotationsFolder[] = "data_object_label_2"; -const char kImageExtension[] = ".png"; -const char kAnnotationExtension[] = ".txt"; -const int32_t kKittiFileNameLength = 6; - -KITTIOp::KITTIOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int32_t queue_size, - bool decode, std::unique_ptr data_schema, const std::shared_ptr &sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - decode_(decode), - usage_(usage), - folder_path_(dataset_dir), - data_schema_(std::move(data_schema)) {} - -void KITTIOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nKITTI directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status KITTIOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string image_id = image_ids_[row_id]; - std::shared_ptr image; - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, KITTI Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, KITTI Dataset dir: " + folder_path_ + " does not exist."); - } - Path path(realpath.value()); - if (usage_ == "train") { - TensorRow annotation; - Path kImageFile = path / kImagesFolder / "training" / "image_2" / (image_id + kImageExtension); - Path kAnnotationFile = path / kAnnotationsFolder / "training" / "label_2" / (image_id + kAnnotationExtension); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile.ToString(), data_schema_->Column(0), &image)); - RETURN_IF_NOT_OK(ReadAnnotationToTensor(kAnnotationFile.ToString(), &annotation)); - trow->setId(row_id); - trow->setPath({kImageFile.ToString(), kAnnotationFile.ToString(), kAnnotationFile.ToString(), - kAnnotationFile.ToString(), kAnnotationFile.ToString(), kAnnotationFile.ToString(), - kAnnotationFile.ToString(), kAnnotationFile.ToString(), kAnnotationFile.ToString()}); - trow->push_back(std::move(image)); - trow->insert(trow->end(), annotation.begin(), annotation.end()); - } else if (usage_ == "test") { - Path kImageFile = path / kImagesFolder / "testing" / "image_2" / (image_id + kImageExtension); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile.ToString(), data_schema_->Column(0), &image)); - trow->setId(row_id); - trow->setPath({kImageFile.ToString()}); - trow->push_back(std::move(image)); - } - return Status::OK(); -} - -Status KITTIOp::ParseImageIds() { - if (!image_ids_.empty()) { - return Status::OK(); - } - auto folder_realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!folder_realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, KITTI Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, KITTI Dataset dir: " + folder_path_ + " does not exist."); - } - Path path(folder_realpath.value()); - Path image_sets_file(""); - if (usage_ == "train") { - image_sets_file = path / kImagesFolder / "training" / "image_2"; - } else if (usage_ == "test") { - image_sets_file = path / kImagesFolder / "testing" / "image_2"; - } - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&image_sets_file); - CHECK_FAIL_RETURN_UNEXPECTED(dirItr != nullptr, "Invalid path, failed to open KITTI image dir: " + - image_sets_file.ToString() + ", permission denied."); - int32_t total_image_size = 0; - while (dirItr->HasNext()) { - total_image_size++; - } - std::string format_id; - for (int32_t i = 0; i < total_image_size; ++i) { - format_id = ""; - std::string id = std::to_string(i); - for (int32_t j = 0; j < kKittiFileNameLength - id.size(); ++j) { - format_id = format_id + std::string("0"); - } - image_ids_.push_back(format_id + id); - } - image_ids_.shrink_to_fit(); - num_rows_ = image_ids_.size(); - return Status::OK(); -} - -Status KITTIOp::ParseAnnotationIds() { - std::vector new_image_ids; - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, KITTI Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, KITTI Dataset dir: " + folder_path_ + " does not exist."); - } - Path path(realpath.value()); - for (auto id : image_ids_) { - Path kAnnotationName = path / kAnnotationsFolder / "training" / "label_2" / (id + kAnnotationExtension); - RETURN_IF_NOT_OK(ParseAnnotationBbox(kAnnotationName.ToString())); - if (annotation_map_.find(kAnnotationName.ToString()) != annotation_map_.end()) { - new_image_ids.push_back(id); - } - } - if (image_ids_.size() != new_image_ids.size()) { - image_ids_.clear(); - image_ids_.insert(image_ids_.end(), new_image_ids.begin(), new_image_ids.end()); - } - uint32_t count = 0; - for (auto &label : label_index_) { - label.second = count++; - } - num_rows_ = image_ids_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API KITTIDataset. Please check file path or dataset API."); - } - return Status::OK(); -} - -Status KITTIOp::ParseAnnotationBbox(const std::string &path) { - CHECK_FAIL_RETURN_UNEXPECTED(Path(path).Exists(), "Invalid path, " + path + " does not exist."); - Annotation annotation; - auto file_realpath = FileUtils::GetRealPath(path.c_str()); - if (!file_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + path + " does not exist."); - } - std::ifstream in_file; - in_file.open(file_realpath.value(), std::ifstream::in); - if (in_file.fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + path); - } - std::string anno; - while (getline(in_file, anno)) { - std::string label_name; - std::string line_result; - std::vector vector_string; - float truncated = 0.0, occluded = 0.0, alpha = 0.0, xmin = 0.0, ymin = 0.0, xmax = 0.0, ymax = 0.0, - first_dimension = 0.0, second_dimension = 0.0, third_dimension = 0.0, first_location = 0.0, - second_location = 0.0, third_location = 0.0, rotation_y = 0.0; - std::stringstream line(anno); - while (line >> line_result) { - vector_string.push_back(line_result); - } - label_name = vector_string[kLabelNameIndex]; - truncated = std::atof(vector_string[kTruncatedIndex].c_str()); - occluded = std::atof(vector_string[kOccludedIndex].c_str()); - alpha = std::atof(vector_string[kAlphaIndex].c_str()); - xmin = std::atof(vector_string[kXMinIndex].c_str()); - ymin = std::atof(vector_string[kYMinIndex].c_str()); - xmax = std::atof(vector_string[kXMaxIndex].c_str()); - ymax = std::atof(vector_string[kYMaxIndex].c_str()); - first_dimension = std::atof(vector_string[kFirstDimensionIndex].c_str()); - second_dimension = std::atof(vector_string[kSecondDimensionIndex].c_str()); - third_dimension = std::atof(vector_string[kThirdDimensionIndex].c_str()); - first_location = std::atof(vector_string[kFirstLocationIndex].c_str()); - second_location = std::atof(vector_string[kSecondLocationIndex].c_str()); - third_location = std::atof(vector_string[kThirdLocationIndex].c_str()); - rotation_y = std::atof(vector_string[kRotationYIndex].c_str()); - if (label_name != "" || (xmin > 0 && ymin > 0 && xmax > xmin && ymax > ymin)) { - std::vector bbox_list = {truncated, - occluded, - alpha, - xmin, - ymin, - xmax, - ymax, - first_dimension, - second_dimension, - third_dimension, - first_location, - second_location, - third_location, - rotation_y}; - annotation.emplace_back(std::make_pair(label_name, bbox_list)); - label_index_[label_name] = 0; - } - } - in_file.close(); - if (annotation.size() > 0) { - annotation_map_[path] = annotation; - } - return Status::OK(); -} - -Status KITTIOp::PrepareData() { - RETURN_IF_NOT_OK(this->ParseImageIds()); - if (usage_ == "train") { - RETURN_IF_NOT_OK(this->ParseAnnotationIds()); - } - return Status::OK(); -} - -Status KITTIOp::ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor) { - RETURN_UNEXPECTED_IF_NULL(tensor); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); - if (decode_) { - Status rc = Decode(*tensor, tensor); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("Invalid data, failed to decode image: " + path); - } - } - return Status::OK(); -} - -Status KITTIOp::ReadAnnotationToTensor(const std::string &path, TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - Annotation annotation = annotation_map_[path]; - std::shared_ptr bbox, alpha, dimensions, location, occuluded, rotation_y, truncated, label; - std::vector bbox_data, alpha_data, dimensions_data, location_data, rotation_y_data, truncated_data; - std::vector occuluded_data; - std::vector label_data; - dsize_t bbox_num = 0; - for (auto item : annotation) { - if (label_index_.find(item.first) != label_index_.end()) { - label_data.push_back(static_cast(label_index_[item.first])); - CHECK_FAIL_RETURN_UNEXPECTED(item.second.size() == kTotalParamNums, - "Invalid file, the format of the annotation file is not as expected, got " + - std::to_string(item.second.size()) + " parameters."); - - std::vector tmp_bbox = {(item.second)[3], (item.second)[4], (item.second)[5], (item.second)[6]}; - std::vector tmp_dimensions = {(item.second)[7], (item.second)[8], (item.second)[9]}; - std::vector tmp_location = {(item.second)[10], (item.second)[11], (item.second)[12]}; - bbox_data.insert(bbox_data.end(), tmp_bbox.begin(), tmp_bbox.end()); - dimensions_data.insert(dimensions_data.end(), tmp_dimensions.begin(), tmp_dimensions.end()); - location_data.insert(location_data.end(), tmp_location.begin(), tmp_location.end()); - truncated_data.push_back(static_cast((item.second)[0])); - occuluded_data.push_back(static_cast(int64_t((item.second)[1]))); - alpha_data.push_back(static_cast((item.second)[2])); - rotation_y_data.push_back(static_cast((item.second)[13])); - bbox_num++; - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(label_data, TensorShape({bbox_num, 1}), &label)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(truncated_data, TensorShape({bbox_num, 1}), &truncated)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(occuluded_data, TensorShape({bbox_num, 1}), &occuluded)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(alpha_data, TensorShape({bbox_num, 1}), &alpha)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(bbox_data, TensorShape({bbox_num, 4}), &bbox)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(dimensions_data, TensorShape({bbox_num, 3}), &dimensions)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(location_data, TensorShape({bbox_num, 3}), &location)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(rotation_y_data, TensorShape({bbox_num, 1}), &rotation_y)); - (*row) = TensorRow({std::move(label), std::move(truncated), std::move(occuluded), std::move(alpha), std::move(bbox), - std::move(dimensions), std::move(location), std::move(rotation_y)}); - return Status::OK(); -} - -Status KITTIOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - RETURN_IF_NOT_OK(PrepareData()); - *count = static_cast(image_ids_.size()); - return Status::OK(); -} - -Status KITTIOp::ComputeColMap() { - // Set the column name map (base class field). - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h deleted file mode 100644 index 65d98a61d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KITTI_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KITTI_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using Annotation = std::vector>>; - -/// \class KITTIOp -/// \brief A source dataset for reading and parsing KITTI dataset. -class KITTIOp : public MappableLeafOp { - public: - // Constructor - // @param std::string dataset_dir - dir directory of KITTI. - // @param std::string usage - split of KITTI. - // @param int32_t num_workers - number of workers reading images in parallel. - // @param int32_t queue_size - connector queue size. - // @param bool decode - whether to decode images. - // @param std::unique_ptr data_schema - the schema of the KITTI dataset. - // @param std::shared_ptr sampler - sampler tells KITTIOp what to read. - KITTIOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int32_t queue_size, - bool decode, std::unique_ptr data_schema, const std::shared_ptr &sampler); - - // Destructor. - ~KITTIOp() = default; - - // A print method typically used for debugging. - // @param out - The output stream to write output to. - // @param show_all - A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the KITTIDataset. - // @param int64_t *count - output rows number of KITTIDataset. - Status CountTotalRows(int64_t *count); - - // Op name getter. - // @return Name of the current Op. - std::string Name() const override { return "KITTIOp"; } - - private: - // Load a tensor row according to image id. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow *row - image & target read into this tensor row. - // @return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - // Load an image to Tensor. - // @param const std::string &path - path to the image file. - // @param const ColDescriptor &col - contains tensor implementation and datatype. - // @param std::shared_ptr *tensor - return image tensor. - // @return Status The status code returned. - Status ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor); - - // Load an annotation to Tensor. - // @param const std::string &path - path to the image file. - // @param TensorRow *row - return annotation tensor. - // @return Status The status code returned. - Status ReadAnnotationToTensor(const std::string &path, TensorRow *row); - - // Read image list from ImageSets. - // @return Status The status code returned. - Status ParseImageIds(); - - // Read annotation from Annotation folder. - // @return Status The status code returned. - Status ParseAnnotationIds(); - - // Function to parse annotation bbox. - // @param const std::string &path - path to annotation xml. - // @return Status The status code returned. - Status ParseAnnotationBbox(const std::string &path); - - // Private function for computing the assignment of the column name map. - // @return Status The status code returned. - Status ComputeColMap() override; - - protected: - Status PrepareData() override; - - private: - bool decode_; - std::string folder_path_; - std::string usage_; - std::unique_ptr data_schema_; - std::vector image_ids_; - std::map label_index_; - std::map annotation_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KITTI_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.cc deleted file mode 100644 index 2c63cd8f6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.cc +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -KMnistOp::KMnistOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MnistOp(usage, num_workers, folder_path, queue_size, std::move(data_schema), std::move(sampler)) {} - -Status KMnistOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - // the logic of counting the number of samples is copied from ParseKMnistData() and uses CheckReader(). - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - - RETURN_IF_NOT_OK(op->WalkAllFiles()); - - for (size_t i = 0; i < op->image_names_.size(); ++i) { - auto image_realpath = FileUtils::GetRealPath(op->image_names_[i].c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + op->image_names_[i] + " does not exist."); - } - auto label_realpath = FileUtils::GetRealPath(op->label_names_[i].c_str()); - if (!label_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + op->label_names_[i] + " does not exist."); - } - std::ifstream image_reader; - image_reader.open(image_realpath.value(), std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(image_reader.is_open(), - "Invalid file, failed to open image file: " + op->image_names_[i]); - std::ifstream label_reader; - label_reader.open(label_realpath.value(), std::ios::in | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(label_reader.is_open(), - "Invalid file, failed to open label file: " + op->label_names_[i]); - uint32_t num_images; - Status s = op->CheckImage(op->image_names_[i], &image_reader, &num_images); - image_reader.close(); - RETURN_IF_NOT_OK(s); - - uint32_t num_labels; - s = op->CheckLabel(op->label_names_[i], &label_reader, &num_labels); - label_reader.close(); - RETURN_IF_NOT_OK(s); - - CHECK_FAIL_RETURN_UNEXPECTED((num_images == num_labels), - "Invalid data, num of images is not equal to num of labels."); - *count = *count + num_images; - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h deleted file mode 100644 index 14ab6cc96..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KMNIST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KMNIST_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" - -namespace mindspore { -namespace dataset { -/// \brief Forward declares. -template -class Queue; - -class KMnistOp : public MnistOp { - public: - /// \brief Constructor. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] num_workers Number of workers reading images in parallel. - /// \param[in] folder_path Dir directory of kmnist. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema The schema of the kmnist dataset. - /// \param[in] Sampler Tells KMnistOp what to read. - KMnistOp(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~KMnistOp() = default; - - /// \brief Function to count the number of samples in the KMNIST dataset. - /// \param[in] dir Path to the KMNIST directory. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] count Output arg that will hold the minimum of the actual dataset size and numSamples. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "KMnistOp"; } - - /// \brief Dataset name getter. - /// \param[in] upper Whether to get upper name. - /// \return Dataset name of the current Op. - std::string DatasetName(bool upper = false) const override { return upper ? "KMnist" : "kmnist"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_KMNIST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.cc deleted file mode 100644 index 09a6b0e01..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.cc +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const char kImageFolder[] = "lfw"; -const char kImageFolderFunneled[] = "lfw_funneled"; -const char kImageFolderDeepFunneled[] = "lfw-deepfunneled"; -const char kImageExtension[] = ".jpg"; -const char kAnnotationDevTest[] = "DevTest"; -const char kAnnotationDevTrain[] = "DevTrain"; -const char kAnnotationNames[] = "lfw-names"; -const char kAnnotationExtension[] = ".txt"; -const int32_t kLFWFileNameLength = 4; - -LFWOp::LFWOp(int32_t num_workers, const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::string &image_set, int32_t queue_size, bool decode, std::unique_ptr data_schema, - const std::shared_ptr &sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(dataset_dir), - task_(task), - usage_(usage), - image_set_(image_set), - decode_(decode), - data_schema_(std::move(data_schema)) {} - -void LFWOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nLFW directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status LFWOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - Path path(real_folder_path_); - std::string image_type_path = ""; - if (image_set_ == "original") { - image_type_path = kImageFolder; - } else if (image_set_ == "funneled") { - image_type_path = kImageFolderFunneled; - } else if (image_set_ == "deepfunneled") { - image_type_path = kImageFolderDeepFunneled; - } - if (task_ == "people") { - std::vector image_id = image_label_people_[row_id]; - std::shared_ptr image, label; - Path kImageFile = path / image_type_path / image_id[1] / (image_id[0] + kImageExtension); - std::string name = image_id[1]; - RETURN_IF_NOT_OK(Tensor::CreateScalar(class_index_[name], &label)); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile.ToString(), data_schema_->Column(0), &image)); - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({kImageFile.ToString(), std::string("")}); - } else if (task_ == "pairs") { - std::vector image_id = image_label_pair_[row_id]; - std::shared_ptr image1, image2, label; - Path kImageFile1 = path / image_type_path / image_id[1] / (image_id[0] + kImageExtension); - Path kImageFile2 = path / image_type_path / image_id[3] / (image_id[2] + kImageExtension); - uint32_t match = std::atoi(image_id[4].c_str()); - RETURN_IF_NOT_OK(Tensor::CreateScalar(match, &label)); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile1.ToString(), data_schema_->Column(0), &image1)); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile2.ToString(), data_schema_->Column(1), &image2)); - (*trow) = TensorRow(row_id, {std::move(image1), std::move(image2), std::move(label)}); - trow->setPath({kImageFile1.ToString(), kImageFile2.ToString(), std::string("")}); - } - return Status::OK(); -} - -Status LFWOp::GetClassIndexing() { - Path path(real_folder_path_); - Path lfw_names_file = path / (std::string(kAnnotationNames) + std::string(kAnnotationExtension)); - std::ifstream in_file; - in_file.open(lfw_names_file.ToString(), std::ios::in); - if (in_file.fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + path.ToString()); - } - std::string lfw_names; - uint32_t line = 0; - while (getline(in_file, lfw_names)) { - std::stringstream line_string(lfw_names); - std::string line_result; - std::vector vector_string; - while (line_string >> line_result) { - vector_string.push_back(line_result); - } - class_index_[vector_string[0]] = line; - line += 1; - } - in_file.close(); - return Status::OK(); -} - -Status LFWOp::ParsePeopleImageIds(const std::vector> &annotation_vector_string) { - if (annotation_vector_string.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid file, LFW annotation file is empty. Check file path: " + folder_path_); - } - uint32_t fold_nums, begin; - if (usage_ == "10fold") { - fold_nums = std::atoi(annotation_vector_string[0][0].c_str()); - begin = 1; - } else { - fold_nums = 1; - begin = 0; - } - for (uint32_t i = 0; i < fold_nums; i++) { - uint32_t n_lines = std::atoi(annotation_vector_string[begin][0].c_str()); - for (uint32_t j = begin + 1; j < begin + n_lines + 1; j++) { - std::vector vector_string = annotation_vector_string[j]; - std::string name = vector_string[0]; - uint32_t num = std::atoi(vector_string[1].c_str()); - std::string format_id; - for (uint32_t k = 1; k < num + 1; k++) { - format_id = ""; - std::string id = std::to_string(k); - for (int32_t l = 0; l < kLFWFileNameLength - id.size(); ++l) { - format_id = format_id + std::string("0"); - } - std::string file_name = name + "_" + (format_id + id); - std::vector image_name; - (void)image_name.emplace_back(file_name); - (void)image_name.emplace_back(name); - (void)image_label_people_.emplace_back(image_name); - } - } - begin += n_lines + 1; - } - num_rows_ = image_label_people_.size(); - return Status::OK(); -} - -Status LFWOp::ParsePairsImageIds(const std::vector> &annotation_vector_string) { - if (annotation_vector_string.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid file, LFW annotation file is empty. Check file path: " + folder_path_); - } - uint32_t fold_nums, pairs_nums, begin; - if (usage_ == "10fold") { - fold_nums = std::atoi(annotation_vector_string[0][0].c_str()); - pairs_nums = std::atoi(annotation_vector_string[0][1].c_str()); - } else { - fold_nums = 1; - pairs_nums = std::atoi(annotation_vector_string[0][0].c_str()); - } - begin = 1; - for (uint32_t i = 0; i < fold_nums; i++) { - std::string first_format_id; - std::string second_format_id; - for (uint32_t j = begin; j < begin + pairs_nums; j++) { - std::vector vector_string = annotation_vector_string[j]; - std::string name = vector_string[0]; - std::string first_extension = vector_string[1]; - std::string second_extension = vector_string[2]; - first_format_id = ""; - second_format_id = ""; - for (int32_t l = 0; l < kLFWFileNameLength - first_extension.size(); ++l) { - first_format_id = first_format_id + std::string("0"); - } - for (int32_t l = 0; l < kLFWFileNameLength - second_extension.size(); ++l) { - second_format_id = second_format_id + std::string("0"); - } - std::string first_name = name + "_" + (first_format_id + first_extension); - std::string second_name = name + "_" + (second_format_id + second_extension); - std::vector image_pair; - (void)image_pair.emplace_back(first_name); - (void)image_pair.emplace_back(name); - (void)image_pair.emplace_back(second_name); - (void)image_pair.emplace_back(name); - (void)image_pair.emplace_back("1"); - (void)image_label_pair_.push_back(image_pair); - } - int multiple = 2; - for (uint32_t k = begin + pairs_nums; k < begin + (multiple * pairs_nums); k++) { - std::vector vector_string = annotation_vector_string[k]; - std::string first = vector_string[0]; - std::string first_extension = vector_string[1]; - std::string second = vector_string[2]; - std::string second_extension = vector_string[3]; - first_format_id = ""; - second_format_id = ""; - for (int32_t l = 0; l < kLFWFileNameLength - first_extension.size(); ++l) { - first_format_id = first_format_id + std::string("0"); - } - for (int32_t l = 0; l < kLFWFileNameLength - second_extension.size(); ++l) { - second_format_id = second_format_id + std::string("0"); - } - std::string first_name = first + "_" + (first_format_id + first_extension); - std::string second_name = second + "_" + (second_format_id + second_extension); - std::vector image_pair; - (void)image_pair.emplace_back(first_name); - (void)image_pair.emplace_back(first); - (void)image_pair.emplace_back(second_name); - (void)image_pair.emplace_back(second); - (void)image_pair.emplace_back("0"); - (void)image_label_pair_.push_back(image_pair); - } - begin += (multiple * pairs_nums); - } - num_rows_ = image_label_pair_.size(); - return Status::OK(); -} - -std::vector> LFWOp::ReadFile(const std::string &annotation_file_path) const { - std::ifstream in_file; - in_file.open(annotation_file_path, std::ios::in); - if (in_file.fail()) { - MS_LOG(ERROR) << "Invalid file, failed to open file: " + annotation_file_path; - } - std::string line; - std::vector> annotation_vector_string; - while (getline(in_file, line)) { - std::stringstream line_string(line); - std::string line_result; - std::vector vector_string; - while (line_string >> line_result) { - vector_string.push_back(line_result); - } - (void)annotation_vector_string.emplace_back(vector_string); - } - in_file.close(); - return annotation_vector_string; -} - -Status LFWOp::ParseImageIds() { - Path path(real_folder_path_); - std::vector file_list; - if (usage_ == "10fold") { - file_list.push_back((path / (task_ + kAnnotationExtension)).ToString()); - } - if (usage_ == "train" || usage_ == "all") { - file_list.push_back( - (path / (task_ + std::string(kAnnotationDevTrain) + std::string(kAnnotationExtension))).ToString()); - } - if (usage_ == "test" || usage_ == "all") { - file_list.push_back( - (path / (task_ + std::string(kAnnotationDevTest) + std::string(kAnnotationExtension))).ToString()); - } - for (auto file : file_list) { - std::vector> annotation_vector_string = ReadFile(file); - if (task_ == "people") { - RETURN_IF_NOT_OK(ParsePeopleImageIds(annotation_vector_string)); - } else if (task_ == "pairs") { - RETURN_IF_NOT_OK(ParsePairsImageIds(annotation_vector_string)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid parameter, task should be \"people\" or \"pairs\", got " + task_); - } - } - return Status::OK(); -} - -Status LFWOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, LFW Dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, LFW Dataset dir: " + folder_path_ + " does not exist."); - } - real_folder_path_ = realpath.value(); - RETURN_IF_NOT_OK(this->ParseImageIds()); - RETURN_IF_NOT_OK(this->GetClassIndexing()); - return Status::OK(); -} - -Status LFWOp::ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor) { - RETURN_UNEXPECTED_IF_NULL(tensor); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); - if (decode_) { - Status rc = Decode(*tensor, tensor); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("Invalid image, failed to decode " + path + - ": the image is damaged or permission denied."); - } - } - return Status::OK(); -} - -Status LFWOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - RETURN_IF_NOT_OK(PrepareData()); - if (task_ == "people") { - *count = static_cast(image_label_people_.size()); - } else if (task_ == "pairs") { - *count = static_cast(image_label_pair_.size()); - } else { - RETURN_STATUS_UNEXPECTED("Invalid parameter, task should be \"people\" or \"pairs\", got " + task_); - } - return Status::OK(); -} - -Status LFWOp::ComputeColMap() { - // Set the column name map (base class field). - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h deleted file mode 100644 index 7e1d10a59..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LFW_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LFW_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// \class LFWOp -/// \brief A source dataset for reading and parsing LFW dataset. -class LFWOp : public MappableLeafOp { - public: - // Constructor - // @param int32_t num_workers - num of workers reading images in parallel. - // @param std::string dataset_dir - dir directory of LFW. - // @param std::string task - set the task type of reading LFW. - // @param std::string usage - split of LFW. - // @param std::string image_set - image set of image funneling to use. - // @param int32_t queue_size - connector queue size. - // @param bool decode - decode the images after reading. - // @param std::unique_ptr data_schema - schema of data. - // @param td::unique_ptr sampler - sampler tells LFWOp what to read. - LFWOp(int32_t num_workers, const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::string &image_set, int32_t queue_size, bool decode, std::unique_ptr, - const std::shared_ptr &sampler); - - // Destructor. - ~LFWOp() = default; - - // A print method typically used for debugging. - // @param out The output stream to write output to. - // @param show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the LFWDataset. - // @param int64_t *count - output rows number of LFWDataset. - // @return Status The status code returned. - Status CountTotalRows(int64_t *count); - - // Op name getter. - // @return Name of the current Op. - std::string Name() const override { return "LFWOp"; } - - private: - // Load a tensor row according to image id. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow *row - image & target read into this tensor row. - // @return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - // Load an image to Tensor. - // @param const std::string &path - path to the image file. - // @param const ColDescriptor &col - contains tensor implementation and datatype. - // @param std::shared_ptr *tensor - return image tensor. - // @return Status The status code returned. - Status ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor); - - // Read txt file. - // @param const std::string &annotation_file_path - path to the txt file. - // @return std::vector> The txt line context. - std::vector> ReadFile(const std::string &annotation_file_path) const; - - // Read image list from ImageSets. - // @return Status The status code returned. - Status ParseImageIds(); - - // Read image list from ImageSets when task is people. - // @param const std::vector> &annotation_vector_string - contains the information - // of each line read from the txt file. - // @return Status The status code returned. - Status ParsePeopleImageIds(const std::vector> &annotation_vector_string); - - // Read image list from ImageSets when task is pairs. - // @param const std::vector> &annotation_vector_string - contains the information - // of each line read from the txt file. - // @return Status The status code returned. - Status ParsePairsImageIds(const std::vector> &annotation_vector_string); - - // Gets the class indexing. - // @return Status The status code returned. - Status GetClassIndexing(); - - // Private function for computing the assignment of the column name map. - // @return Status The status code returned. - Status ComputeColMap() override; - - // Parse LFW dataset files. - // @return Status The status code returned. - Status PrepareData() override; - - private: - bool decode_; - std::string folder_path_; - std::string task_; - std::string usage_; - std::string image_set_; - std::unique_ptr data_schema_; - std::vector> image_label_people_; - std::vector> image_label_pair_; - std::map class_index_; - std::string real_folder_path_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LFW_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.cc deleted file mode 100644 index b2a6f54e2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.cc +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -const int32_t label_file_suffix_len = 10; -const char label_file_suffix[] = ".trans.tsv"; -const char audio_file_suffix[] = ".wav"; -const std::vector usage_list = {"dev-clean", "dev-other", "test-clean", "test-other", - "train-clean-100", "train-clean-360", "train-other-500"}; - -LibriTTSOp::LibriTTSOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - usage_(usage), - data_schema_(std::move(data_schema)) {} - -Status LibriTTSOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - LibriTTSLabelTuple audio_tuple = audio_label_tuples_[row_id]; - const uint32_t rate = 24000; - std::shared_ptr waveform, sample_rate, original_text, normalized_text, speaker_id, chapter_id, utterance_id; - Path dir(real_path_); - std::string file_name = audio_tuple.utterance_id + audio_file_suffix; - Path full_dir = dir / audio_tuple.usage / std::to_string(audio_tuple.speaker_id) / - std::to_string(audio_tuple.chapter_id) / file_name; - RETURN_IF_NOT_OK(ReadAudio(full_dir.ToString(), &waveform)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(rate, &sample_rate)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_tuple.original_text, &original_text)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_tuple.normalized_text, &normalized_text)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_tuple.speaker_id, &speaker_id)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_tuple.chapter_id, &chapter_id)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(audio_tuple.utterance_id, &utterance_id)); - (*trow) = TensorRow( - row_id, {std::move(waveform), std::move(sample_rate), std::move(original_text), std::move(normalized_text), - std::move(speaker_id), std::move(chapter_id), std::move(utterance_id)}); - std::string label_path = audio_tuple.label_path; - trow->setPath({full_dir.ToString(), full_dir.ToString(), label_path, label_path, label_path, label_path, label_path}); - return Status::OK(); -} - -void LibriTTSOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nLibriTTS directory: " << dataset_dir_ << "\n\n"; - } -} - -Status LibriTTSOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_original_text = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("original_text", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_original_text))); - TensorShape scalar_normalized_text = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("normalized_text", DataType(DataType::DE_STRING), - TensorImpl::kFlexible, 0, &scalar_normalized_text))); - TensorShape scalar_speaker_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("speaker_id", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_speaker_id))); - TensorShape scalar_chapter_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("chapter_id", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_chapter_id))); - TensorShape scalar_utterance_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("utterance_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance_id))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = - std::make_shared(dir, usage, num_workers, op_connect_size, std::move(schema), std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = op->audio_label_tuples_.size(); - return Status::OK(); -} - -Status LibriTTSOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status LibriTTSOp::ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform) { - RETURN_UNEXPECTED_IF_NULL(waveform); - const int32_t kWavFileSampleRate = 24000; - int32_t sample_rate = 0; - std::vector waveform_vec; - RETURN_IF_NOT_OK(ReadWaveFile(audio_dir, &waveform_vec, &sample_rate)); - CHECK_FAIL_RETURN_UNEXPECTED( - sample_rate == kWavFileSampleRate, - "Invalid file, sampling rate of LibriTTS wav file must be 24000, file path: " + audio_dir); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, waveform)); - RETURN_IF_NOT_OK((*waveform)->ExpandDim(0)); - return Status::OK(); -} - -Status LibriTTSOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, LibriTTS dataset dir: " << dataset_dir_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, LibriTTS dataset dir: " + dataset_dir_ + " does not exist."); - } - real_path_ = realpath.value(); - Path dir(real_path_); - if (usage_ != "all") { - Path full_dir = dir / usage_; - cur_usage_ = usage_; - RETURN_IF_NOT_OK(GetPaths(&full_dir)); - RETURN_IF_NOT_OK(GetLabels()); - } else { - for (std::string usage_iter : usage_list) { - cur_usage_ = usage_iter; - Path full_dir = dir / cur_usage_; - RETURN_IF_NOT_OK(GetPaths(&full_dir)); - RETURN_IF_NOT_OK(GetLabels()); - } - } - num_rows_ = audio_label_tuples_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0, - "Invalid data, no valid data matching the dataset API LibriTTSDataset. " - "Please check dataset API or file path: " + - dataset_dir_ + "."); - return Status::OK(); -} - -Status LibriTTSOp::GetPaths(Path *dir) { - RETURN_UNEXPECTED_IF_NULL(dir); - auto iter = Path::DirIterator::OpenDirectory(dir); - if (iter == nullptr) { - MS_LOG(WARNING) << "Invalid file path, unable to open directory: " << dir->ToString() << "."; - } else { - while (iter->HasNext()) { - Path sub_dir = iter->Next(); - if (sub_dir.IsDirectory()) { - RETURN_IF_NOT_OK(GetPaths(&sub_dir)); - } else { - Path file_path = sub_dir; - std::string file_name = file_path.Basename(); - int32_t length = file_name.size(); - if (length > label_file_suffix_len && file_name.substr(length - label_file_suffix_len) == label_file_suffix) { - label_files_.push_back(sub_dir.ToString()); - return Status::OK(); - } - } - } - } - return Status::OK(); -} - -Status LibriTTSOp::GetLabels() { - std::string utterance_id_body = ""; - std::string original_text_body = ""; - std::string normalized_text_body = ""; - const uint32_t base = 10; - const uint32_t ascii_zero = 48; - const size_t underline_exact = 3; - for (std::string label_file : label_files_) { - std::ifstream label_reader(label_file, std::ios::in); - while (getline(label_reader, utterance_id_body, '\t')) { - getline(label_reader, original_text_body, '\t'); - getline(label_reader, normalized_text_body, '\n'); - uint32_t speaker_id = 0; - uint32_t chapter_id = 0; - size_t underline_num = 0; - size_t underline_inx[4] = {0}; - for (size_t i = 0; i < utterance_id_body.size() && underline_num <= underline_exact; i++) { - if (utterance_id_body[i] == '_') { - underline_inx[underline_num++] = i; - } - } - if (underline_num != underline_exact) { - label_reader.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, the file may not be a LibriTTS dataset file: " + label_file); - } - for (size_t i = 0; i < underline_inx[0]; i++) { - speaker_id = speaker_id * base + utterance_id_body[i] - ascii_zero; - } - for (size_t i = underline_inx[0] + 1; i < underline_inx[1]; i++) { - chapter_id = chapter_id * base + utterance_id_body[i] - ascii_zero; - } - audio_label_tuples_.push_back( - {cur_usage_, utterance_id_body, original_text_body, normalized_text_body, speaker_id, chapter_id, label_file}); - } - label_reader.close(); - } - label_files_.clear(); - return Status::OK(); -} -} // namespace dataset. -} // namespace mindspore. diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h deleted file mode 100644 index 5fb7d7204..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LIBRI_TTS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LIBRI_TTS_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -struct LibriTTSLabelTuple { - std::string usage; - std::string utterance_id; - std::string original_text; - std::string normalized_text; - uint32_t speaker_id; - uint32_t chapter_id; - std::string label_path; -}; - -class LibriTTSOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Dir directory of LibriTTS. - /// \param[in] usage usage of this dataset, can be "dev-clean", "dev-other", "test-clean", "test-other", - /// "train-clean-100", "train-clean-360", "train-other-500", or "all". - /// \param[in] num_workers Number of workers reading audios in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema The schema of the LibriTTS dataset. - /// \param[in] sampler Sampler tells LibriSpeechOp what to read. - LibriTTSOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~LibriTTSOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out Output stream. - /// \param[in] show_all Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the LibriTTS dataset. - /// \param[in] dir Path to the LibriTTS directory. - /// \param[in] usage Select the data set section. - /// \param[out] count Output arg that will hold the minimum of the actual dataset size and numSamples. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "LibriTTSOp"; } - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] row_id Id for this tensor row. - /// \param[out] row Audio & label read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Read all paths in the directory. - /// \param[in] dir File path to be traversed. - /// \return Status The status code returned. - Status GetPaths(Path *dir); - - /// \brief Read all label files. - /// \return Status The status code returned. - Status GetLabels(); - - /// \brief Parse a single wav file. - /// \param[in] audio_dir Audio file path. - /// \param[out] waveform The output waveform tensor. - /// \return Status The status code returned. - Status ReadAudio(const std::string &audio_dir, std::shared_ptr *waveform); - - /// \brief Prepare all data in the directory. - /// \return Status The status code returned. - Status PrepareData(); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - const std::string usage_; - std::string cur_usage_; - std::string real_path_; - std::string dataset_dir_; - std::unique_ptr data_schema_; - std::vector audio_label_tuples_; - std::vector label_files_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LIBRI_TTS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.cc deleted file mode 100644 index a6457fb4c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.cc +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -LJSpeechOp::LJSpeechOp(const std::string &file_dir, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(file_dir), - data_schema_(std::move(data_schema)) {} - -Status LJSpeechOp::PrepareData() { - auto real_path = FileUtils::GetRealPath(folder_path_.c_str()); - if (!real_path.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, LJSpeech Dataset folder: " + folder_path_ + " does not exist."); - } - Path root_folder(real_path.value()); - Path metadata_file_path = root_folder / "metadata.csv"; - CHECK_FAIL_RETURN_UNEXPECTED(metadata_file_path.Exists() && !metadata_file_path.IsDirectory(), - "Invalid file, failed to find LJSpeech metadata file: " + metadata_file_path.ToString()); - std::ifstream csv_reader(metadata_file_path.ToString(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(csv_reader.is_open(), - "Invalid file, failed to open LJSpeech metadata file: " + metadata_file_path.ToString() + - ", make sure file not damaged or permission denied."); - std::string line = ""; - while (getline(csv_reader, line)) { - int32_t last_pos = 0, curr_pos = 0; - std::vector row; - while (curr_pos < line.size()) { - if (line[curr_pos] == '|') { - row.emplace_back(line.substr(last_pos, curr_pos - last_pos)); - last_pos = curr_pos + 1; - } - ++curr_pos; - } - row.emplace_back(line.substr(last_pos, curr_pos - last_pos)); - meta_info_list_.emplace_back(row); - } - if (meta_info_list_.empty()) { - csv_reader.close(); - RETURN_STATUS_UNEXPECTED("Reading failed, unable to read valid data from the LJSpeech metadata file: " + - metadata_file_path.ToString() + "."); - } - num_rows_ = meta_info_list_.size(); - csv_reader.close(); - return Status::OK(); -} - -// Load 1 TensorRow (waveform, sample_rate, transcription, normalized_transcription). -// 1 function call produces 1 TensorTow -Status LJSpeechOp::LoadTensorRow(row_id_type index, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - int32_t num_items = meta_info_list_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(index >= 0 && index < num_items, "[Internal ERROR] The input index is out of range."); - std::shared_ptr waveform; - std::shared_ptr sample_rate_scalar; - std::shared_ptr transcription, normalized_transcription; - std::string file_name_pref = meta_info_list_[index][0], transcription_str = meta_info_list_[index][1], - normalized_transcription_str = meta_info_list_[index][2]; - int32_t sample_rate; - std::string file_name = file_name_pref + ".wav"; - Path root_folder(folder_path_); - Path wav_file_path = root_folder / "wavs" / file_name; - Path metadata_file_path = root_folder / "metadata.csv"; - std::vector waveform_vec; - RETURN_IF_NOT_OK(ReadWaveFile(wav_file_path.ToString(), &waveform_vec, &sample_rate)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, &waveform)); - RETURN_IF_NOT_OK(waveform->ExpandDim(0)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &sample_rate_scalar)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(transcription_str, &transcription)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(normalized_transcription_str, &normalized_transcription)); - (*trow) = TensorRow(index, {waveform, sample_rate_scalar, transcription, normalized_transcription}); - // Add file path info - trow->setPath({wav_file_path.ToString(), metadata_file_path.ToString(), metadata_file_path.ToString(), - metadata_file_path.ToString()}); - return Status::OK(); -} - -void LJSpeechOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nLJSpeech directory: " << folder_path_ << "\n\n"; - } -} - -Status LJSpeechOp::CountTotalRows(const std::string &dir, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - auto real_path = FileUtils::GetRealPath(dir.c_str()); - if (!real_path.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file, " + dir + " does not exist."); - } - Path root_folder(real_path.value()); - Path metadata_file_path = root_folder / "metadata.csv"; - CHECK_FAIL_RETURN_UNEXPECTED(metadata_file_path.Exists() && !metadata_file_path.IsDirectory(), - "Invalid file, failed to find metadata file: " + metadata_file_path.ToString()); - std::ifstream csv_reader(metadata_file_path.ToString(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(csv_reader.is_open(), - "Invalid file, failed to open metadata file: " + metadata_file_path.ToString() + - ", make sure file not damaged or permission denied."); - std::string line = ""; - int64_t cnt = 0; - while (getline(csv_reader, line)) { - ++cnt; - } - *count = cnt; - csv_reader.close(); - return Status::OK(); -} - -Status LJSpeechOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h deleted file mode 100644 index 292e545f3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LJ_SPEECH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LJ_SPEECH_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// \brief Read LJSpeech dataset. -class LJSpeechOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] file_dir Directory of lj_speech dataset. - /// \param[in] num_workers Number of workers reading audios in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema Data schema of lj_speech dataset. - /// \param[in] sampler Sampler tells LJSpeechOp what to read. - LJSpeechOp(const std::string &file_dir, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~LJSpeechOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number of samples in the LJSpeech dataset. - /// \param[in] dir Path to the directory of LJSpeech dataset. - /// \param[out] count Output arg that will hold the actual dataset size. - /// \return Status - static Status CountTotalRows(const std::string &dir, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "LJSpeechOp"; } - - protected: - /// \brief Called first when function is called. - /// \return Status - Status PrepareData() override; - - private: - /// \brief Load a tensor row. - /// \param[in] index Index need to load. - /// \param[out] trow Waveform & sample_rate & transcription & normalized_transcription read into this tensor row. - /// \return Status the status code returned. - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status - Status ComputeColMap() override; - - std::string folder_path_; - std::unique_ptr data_schema_; - std::vector> meta_info_list_; // the shape is (N, 3) -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LJ_SPEECH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.cc deleted file mode 100644 index 00c6e7746..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.cc +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -LSUNOp::LSUNOp(int32_t num_wkrs, const std::string &file_dir, int32_t queue_size, const std::string &usage, - const std::vector &classes, bool do_decode, std::unique_ptr data_schema, - std::shared_ptr sampler) - : ImageFolderOp(num_wkrs, file_dir, queue_size, false, do_decode, {}, {}, std::move(data_schema), - std::move(sampler)), - usage_(std::move(usage)), - classes_(std::move(classes)) {} - -void LSUNOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nLSUN directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status LSUNOp::WalkDir(Path *dir, const std::string &usage, const std::vector &classes, - const std::unique_ptr> &folder_name_queue, int64_t *num_class) { - RETURN_UNEXPECTED_IF_NULL(dir); - std::vector split; - if (usage == "train" || usage == "all") { - split.push_back("_train"); - } - if (usage == "valid" || usage == "all") { - split.push_back("_val"); - } - uint64_t dirname_offset = dir->ToString().length(); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(dir); - CHECK_FAIL_RETURN_UNEXPECTED(dir_itr != nullptr, - "Invalid path, failed to open image dir: " + dir->ToString() + ", permission denied."); - std::set classes_set; - std::vector valid_classes = classes; - if (classes.empty()) { - valid_classes = {"bedroom", "bridge", "church_outdoor", "classroom", "conference_room", - "dining_room", "kitchen", "living_room", "restaurant", "tower"}; - } - while (dir_itr->HasNext()) { - std::string subdir = dir_itr->Next().ToString(); - for (auto str : split) { - std::string name = subdir.substr(dirname_offset); - for (auto class_name : valid_classes) { - if (name.find(class_name + str) != std::string::npos) { - RETURN_IF_NOT_OK(folder_name_queue->EmplaceBack(name)); - classes_set.insert(class_name); - } - } - } - } - if (num_class != nullptr) { - *num_class = classes_set.size(); - } - return Status::OK(); -} - -// A thread that calls WalkFolder -Status LSUNOp::RecursiveWalkFolder(Path *dir) { - RETURN_UNEXPECTED_IF_NULL(dir); - if (usage_ == "test") { - Path folder(folder_path_); - folder = folder / "test"; - - RETURN_IF_NOT_OK(folder_name_queue_->EmplaceBack(folder.ToString().substr(dirname_offset_))); - return Status::OK(); - } - - RETURN_IF_NOT_OK(WalkDir(dir, usage_, classes_, folder_name_queue_, nullptr)); - return Status::OK(); -} - -Status LSUNOp::CountRowsAndClasses(const std::string &path, const std::string &usage, - const std::vector &classes, int64_t *num_rows, int64_t *num_classes) { - Path dir(path); - int64_t row_cnt = 0; - CHECK_FAIL_RETURN_UNEXPECTED(dir.Exists() && dir.IsDirectory(), "Invalid parameter, input dataset path " + path + - " does not exist or is not a directory."); - CHECK_FAIL_RETURN_UNEXPECTED(num_classes != nullptr || num_rows != nullptr, - "[Internal ERROR] num_class and num_rows are null."); - - int32_t queue_size = 1024; - auto folder_name_queue = std::make_unique>(queue_size); - RETURN_IF_NOT_OK(WalkDir(&dir, usage, classes, folder_name_queue, num_classes)); - - // return here if only num_class is needed - RETURN_OK_IF_TRUE(num_rows == nullptr); - - while (!folder_name_queue->empty()) { - std::string name; - RETURN_IF_NOT_OK(folder_name_queue->PopFront(&name)); - Path subdir(path + name); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&subdir); - CHECK_FAIL_RETURN_UNEXPECTED(dir_itr != nullptr, "Invalid path, failed to open dir: " + subdir.ToString() + - ", not exists or permission denied."); - while (dir_itr->HasNext()) { - ++row_cnt; - Path subdir_pic = dir_itr->Next(); - } - } - *num_rows = row_cnt; - return Status::OK(); -} - -Status LSUNOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - ImageLabelPair pair_ptr = image_label_pairs_[row_id]; - std::shared_ptr image, label; - uint32_t label_num = static_cast(pair_ptr->second); - RETURN_IF_NOT_OK(Tensor::CreateScalar(label_num, &label)); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(folder_path_ + (pair_ptr->first), &image)); - - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = "Invalid image, " + folder_path_ + (pair_ptr->first) + - " decode failed, the image is broken or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({folder_path_ + (pair_ptr->first), std::string("")}); - return Status::OK(); -} - -// Get number of classes -Status LSUNOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (num_classes_ > 0) { - *num_classes = num_classes_; - return Status::OK(); - } - - RETURN_IF_NOT_OK(CountRowsAndClasses(folder_path_, usage_, classes_, nullptr, num_classes)); - num_classes_ = *num_classes; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h deleted file mode 100644 index 7f9cbac0a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LSUN_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LSUN_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h" - -namespace mindspore { -namespace dataset { -/// \brief Forward declares. -template -class Queue; - -using ImageLabelPair = std::shared_ptr>; -using FolderImagesPair = std::shared_ptr>>; - -class LSUNOp : public ImageFolderOp { - public: - /// \brief Constructor. - /// \param[in] int32_t num_wkrs num of workers reading images in parallel. - /// \param[in] std::string file_dir dir directory of LSUNDataset. - /// \param[in] int32_t queue_size connector queue size. - /// \param[in] std::string usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] std::vector classes Classes list to load. - /// \param[in] bool do_decode decode the images after reading. - /// \param[in] std::unique_ptr data_schema schema of data. - /// \param[in] unique_ptr sampler sampler tells LSUNOp what to read. - LSUNOp(int32_t num_wkrs, const std::string &file_dir, int32_t queue_size, const std::string &usage, - const std::vector &classes, bool do_decode, std::unique_ptr data_schema, - std::shared_ptr sampler); - - /// \brief Destructor. - ~LSUNOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Function to count the number and classes of samples in the LSUN dataset. - /// \param[in] const std::string &path path to the LSUN directory. - /// \param[in] std::string usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] const std::vector &classes Classes list to load. - /// \param[out] int64_t *num_rows output arg that will hold the minimum of the actual dataset - /// size and numSamples. - /// \param[out] int64_t *num_classes output arg that will hold the classes num of the actual dataset - /// size and numSamples. - /// \return Status The status code returned. - static Status CountRowsAndClasses(const std::string &path, const std::string &usage, - const std::vector &classes, int64_t *num_rows, int64_t *num_classes); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "LSUNOp"; } - - /// \brief Dataset name getter. - /// \param[in] upper Whether to get upper name. - /// \return Dataset name of the current Op. - std::string DatasetName(bool upper = false) const override { return upper ? "LSUN" : "lsun"; } - - /// \brief Load a tensor row according to a pair - /// \param[in] row_id id for this tensor row - /// \param[out] trow image & label read into this tensor row - /// \return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - /// \brief Base-class override for GetNumClasses - /// \param[out] num_classes the number of classes - /// \return Status of the function - Status GetNumClasses(int64_t *num_classes) override; - - private: - /// \brief Base-class override for RecursiveWalkFolder - /// \param[in] std::string & dir dir to lsun dataset. - /// \return Status of the function - Status RecursiveWalkFolder(Path *dir) override; - - /// \brief Function to save the path list to folder_paths - /// \param[in] std::string & dir dir to lsun dataset. - /// \param[in] std::string usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] const std::vector &classes Classes list to load. - /// \param[out] std::unique_ptr> &folder_name_queue output arg that will hold the path list. - /// \param[out] int64_t *num_class the number of classes - /// \return Status of the function - static Status WalkDir(Path *dir, const std::string &usage, const std::vector &classes, - const std::unique_ptr> &folder_name_queue, int64_t *num_class); - - std::string usage_; - std::vector classes_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_LSUN_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.cc deleted file mode 100644 index 704dfecf1..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.cc +++ /dev/null @@ -1,315 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h" - -#include -#include -#include -#include - -#include "utils/file_utils.h" -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -ManifestOp::ManifestOp(int32_t num_works, std::string file, int32_t queue_size, bool decode, - const std::map &class_index, std::unique_ptr data_schema, - std::shared_ptr sampler, std::string usage) - : MappableLeafOp(num_works, queue_size, std::move(sampler)), - io_block_pushed_(0), - sampler_ind_(0), - data_schema_(std::move(data_schema)), - file_(std::move(file)), - class_index_(class_index), - decode_(decode), - usage_(usage) { - (void)std::transform(usage_.begin(), usage_.end(), usage_.begin(), ::tolower); -} - -// Load 1 TensorRow (image,label) using 1 ImageLabelPair. 1 function call produces 1 TensorTow -Status ManifestOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair> data = image_labelname_[static_cast(row_id)]; - std::shared_ptr image; - std::shared_ptr label; - std::vector label_index(data.second.size()); - (void)std::transform(data.second.begin(), data.second.end(), label_index.begin(), - [this](const std::string &label_name) { return label_index_[label_name]; }); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(label_index, &label)); - if (label_index.size() == 1) { - RETURN_IF_NOT_OK(label->Reshape(TensorShape({}))); - } else { - RETURN_IF_NOT_OK(label->Reshape(TensorShape(std::vector(1, label_index.size())))); - } - - RETURN_IF_NOT_OK(Tensor::CreateFromFile(data.first, &image)); - if (decode_ == true) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = - "Invalid image, failed to decode: " + data.first + ", the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({data.first, file_}); - return Status::OK(); -} - -void ManifestOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\nManifest file: " << file_ << "\nDecode: " << (decode_ ? "yes" : "no") - << "\n\n"; - } -} - -// Derived from RandomAccessOp -Status ManifestOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_labelname_.empty()) { - if (image_labelname_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid manifest file, image data is missing in " + file_); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - - for (size_t i = 0; i < image_labelname_.size(); i++) { - size_t image_index = i; - for (size_t j = 0; j < image_labelname_[image_index].second.size(); j++) { - std::string label_name = (image_labelname_[image_index].second)[j]; - int32_t label_index = label_index_.at(label_name); - (*cls_ids)[label_index].emplace_back(image_index); - } - } - - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -// Manifest file content -// {"source": "/path/to/image1.jpg", "usage":"train", annotation": ...} -// {"source": "/path/to/image2.jpg", "usage":"eval", "annotation": ...} -Status ManifestOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(file_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file_ + " does not exist."); - } - - std::ifstream file_handle(realpath.value(), std::ios::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + file_ + - ": manifest file is damaged or permission denied!"); - } - std::string line; - std::set classes; - uint64_t line_count = 1; - while (getline(file_handle, line)) { - try { - nlohmann::json js = nlohmann::json::parse(line); - std::string image_file_path = js.value("source", ""); - if (image_file_path == "") { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid manifest file, 'source' is missing in file: " + file_ + " at line " + - std::to_string(line_count)); - } - // If image is not JPEG/PNG/GIF/BMP, drop it - bool valid = false; - auto s = CheckImageType(image_file_path, &valid); - if (s != Status::OK()) { - file_handle.close(); - return s; - } - if (!valid) { - continue; - } - std::string usage = js.value("usage", ""); - if (usage == "") { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid manifest file, 'usage' is missing in file: " + file_ + " at line " + - std::to_string(line_count)); - } - (void)std::transform(usage.begin(), usage.end(), usage.begin(), ::tolower); - if (usage != usage_) { - continue; - } - std::vector labels; - nlohmann::json annotations = js.at("annotation"); - for (nlohmann::json::iterator it = annotations.begin(); it != annotations.end(); ++it) { - nlohmann::json annotation = it.value(); - std::string label_name = annotation.value("name", ""); - classes.insert(label_name); - if (label_name == "") { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid manifest file, 'name' attribute of label is missing in file: " + file_ + - " at line " + std::to_string(line_count)); - } - if (class_index_.empty() || class_index_.find(label_name) != class_index_.end()) { - if (label_index_.find(label_name) == label_index_.end()) { - label_index_[label_name] = 0; - } - labels.emplace_back(label_name); - } - } - if (!labels.empty()) { - image_labelname_.emplace_back(std::make_pair(image_file_path, labels)); - } - line_count++; - } catch (const std::exception &err) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid manifest file, parse ManiFest file: " + file_ + " failed, " + - std::string(err.what())); - } - } - num_classes_ = classes.size(); - file_handle.close(); - RETURN_IF_NOT_OK(CountDatasetInfo()); - return Status::OK(); -} - -// Only support JPEG/PNG/GIF/BMP -Status ManifestOp::CheckImageType(const std::string &file_name, bool *valid) { - auto realpath = FileUtils::GetRealPath(file_name.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file_name << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file_name + " does not exist."); - } - - std::ifstream file_handle; - constexpr int read_num = 3; - *valid = false; - file_handle.open(realpath.value(), std::ios::binary | std::ios::in); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid manifest file, failed to open " + file_name + - " : the manifest file is damaged or permission denied."); - } - unsigned char file_type[read_num]; - (void)file_handle.read(reinterpret_cast(file_type), read_num); - - if (file_handle.fail()) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid manifest file, failed to read " + file_name + - " : the manifest file is damaged or permission denied."); - } - file_handle.close(); - if (file_type[0] == 0xff && file_type[1] == 0xd8 && file_type[2] == 0xff) { - // Normal JPEGs start with \xff\xd8\xff\xe0 - // JPEG with EXIF stats with \xff\xd8\xff\xe1 - // Use \xff\xd8\xff to cover both. - *valid = true; - } else if (file_type[0] == 0x89 && file_type[1] == 0x50 && file_type[2] == 0x4e) { - // It's a PNG - *valid = true; - } else if (file_type[0] == 0x47 && file_type[1] == 0x49 && file_type[2] == 0x46) { - // It's a GIF - *valid = true; - } else if (file_type[0] == 0x42 && file_type[1] == 0x4d) { - // It's a BMP - *valid = true; - } - return Status::OK(); -} - -Status ManifestOp::CountDatasetInfo() { - int32_t index = 0; - for (auto &label : label_index_) { - label.second = class_index_.empty() ? index : class_index_[label.first]; - index++; - } - - num_rows_ = static_cast(image_labelname_.size()); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, ManifestDataset API can't read the data file (interface mismatch or no data found). " - "Check file path: " + - file_); - } - return Status::OK(); -} - -Status ManifestOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - RETURN_IF_NOT_OK(PrepareData()); - *count = static_cast(image_labelname_.size()); - return Status::OK(); -} - -Status ManifestOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Get number of classes -Status ManifestOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (num_classes_ > 0) { - *num_classes = num_classes_; - return Status::OK(); - } - int64_t classes_count; - RETURN_IF_NOT_OK(PrepareData()); - classes_count = static_cast(label_index_.size()); - *num_classes = classes_count; - num_classes_ = classes_count; - return Status::OK(); -} - -Status ManifestOp::GetClassIndexing(std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - if ((*output_class_indexing).empty()) { - RETURN_IF_NOT_OK(PrepareData()); - RETURN_IF_NOT_OK(CountDatasetInfo()); - int32_t count = 0; - for (const auto &label : label_index_) { - if (!class_index_.empty()) { - (*output_class_indexing) - .emplace_back(std::make_pair(label.first, std::vector(1, class_index_[label.first]))); - } else { - (*output_class_indexing).emplace_back(std::make_pair(label.first, std::vector(1, count))); - } - count++; - } - } - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h deleted file mode 100644 index 6bbf6afde..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MANIFEST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MANIFEST_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class ManifestOp : public MappableLeafOp { - public: - // Constructor - // @param int32_t num_works - Num of workers reading images in parallel - // @param std::string - file list of Manifest - // @param int32_t queue_size - connector queue size - // @param td::unique_ptr sampler - sampler tells ImageFolderOp what to read - ManifestOp(int32_t num_works, std::string file, int32_t queue_size, bool decode, - const std::map &class_index, std::unique_ptr data_schema, - std::shared_ptr sampler, std::string usage); - // Destructor. - ~ManifestOp() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class - // @param (std::map> * map - key label, val all ids for this class - // @return Status The status code returned - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging - // @param out - // @param show_all - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Counts the total number of rows in Manifest - /// \param[out] count Number of rows counted - /// \return Status of the function - Status CountTotalRows(int64_t *count); - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "ManifestOp"; } - - /// \brief Base-class override for GetNumClasses - /// \param[out] num_classes the number of classes - /// \return Status of the function - Status GetNumClasses(int64_t *num_classes) override; - - /// \brief Gets the class indexing - /// \return Status - The status code return - Status GetClassIndexing(std::vector>> *output_class_indexing) override; - - protected: - // Parse manifest file to get image path and label and so on. - // @return Status The status code returned - Status PrepareData() override; - - private: - // Load a tensor row according to a pair - // @param row_id_type row_id - id for this tensor row - // @param std::pair> - > - // @param TensorRow row - image & label read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - // Check if image ia valid.Only support JPEG/PNG/GIF/BMP - // @return - Status CheckImageType(const std::string &file_name, bool *valid); - - // Count label index,num rows and num samples - // @return Status The status code returned - Status CountDatasetInfo(); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - int64_t io_block_pushed_; - int64_t sampler_ind_; - std::unique_ptr data_schema_; - std::string file_; // file that store the information of images - std::map class_index_; - bool decode_; - std::string usage_; - - std::map label_index_; - std::vector>> image_labelname_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MANIFEST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.cc deleted file mode 100644 index 5865fce76..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.cc +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -MappableLeafOp::MappableLeafOp(int32_t num_wkrs, int32_t queue_size, std::shared_ptr sampler) - : ParallelOp(num_wkrs, queue_size, std::move(sampler)), - sample_ids_(nullptr), - curr_row_(0), - prepared_data_{false}, - eof_handled_{false} {} - -#ifdef ENABLE_PYTHON -Status MappableLeafOp::ImageDecrypt(const std::string &path, std::shared_ptr *tensor, - const py::function &decrypt) { - RETURN_UNEXPECTED_IF_NULL(tensor); - if (decrypt == nullptr || py::isinstance(decrypt)) { - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); - } else { - // Acquire Python GIL - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized."); - } - try { - py::object ret_py_obj = decrypt(path); - if (!py::isinstance(ret_py_obj)) { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, - "The return value of the decrypt function is not of type bytes."); - } - int64_t num_bytes = static_cast(len(ret_py_obj)); - CHECK_FAIL_RETURN_UNEXPECTED(num_bytes < kDeMaxDim, - "The length of decrypted bytes returned by the decryption function exceeds the " - "maximum value of int64, check path: " + - path); - std::string ret_str = py::cast(ret_py_obj); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(TensorShape{num_bytes}, DataType(DataType::DE_UINT8), - reinterpret_cast(ret_str.c_str()), num_bytes, tensor)); - } catch (const py::error_already_set &e) { - RETURN_STATUS_ERROR(StatusCode::kMDPyFuncException, e.what()); - } - } - return Status::OK(); -} -#endif - -// Main logic, Register Queue with TaskGroup, launch all threads and do the functor's work -Status MappableLeafOp::operator()() { - // Registering and launching worker threads have to be before in sync with caller (i.e., before FindMe()::Post()) - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - // Initialize callback - RETURN_IF_NOT_OK(callback_manager_.Init(this)); - // Synchronize with TaskManager - TaskManager::FindMe()->Post(); - RETURN_IF_NOT_OK(InitOp()); - - int64_t ep_step = 0, total_step = 0; - RETURN_IF_NOT_OK(callback_manager_.Begin(CallbackParam(0, ep_step, total_step))); - TensorRow sample_row; - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - for (;;) { // each iteration is 1 repeat (usually =1 epoch, unless we have a repeat node above us), breaks when - // IsLastIteration() is true - if (op_current_repeats_ % GetOpNumRepeatsPerEpoch() == 0) { - ep_step = 0; - RETURN_IF_NOT_OK(callback_manager_.EpochBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - } - while (sample_row.eoe() == false) { - std::shared_ptr sample_ids = sample_row[0]; - for (auto itr = sample_ids->begin(); itr != sample_ids->end(); ++itr) { - if ((*itr) >= num_rows_) { - MS_LOG(WARNING) << "Skipping sample with ID: " << *itr << " since it is out of bound: " << num_rows_; - continue; // index out of bound, skipping - } - ep_step++; - total_step++; - RETURN_IF_NOT_OK(callback_manager_.StepBegin(CallbackParam(op_current_epochs_ + 1, ep_step, total_step))); - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::make_unique(*itr, IOBlock::kFlagNone))); - } - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - } - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::make_unique(IOBlock::kFlagEOE))); - if (!IsLastIteration()) { - // If not the last repeat, self-reset and go to loop again. - RETURN_IF_NOT_OK(Reset()); - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - } else { - break; - } - UpdateRepeatAndEpochCounter(); - } - RETURN_IF_NOT_OK(worker_in_queues_[NextWorkerID()]->Add(std::make_unique(IOBlock::kFlagEOF))); - for (int32_t i = 0; i < num_workers_; ++i) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(NextWorkerID())); - } - return Status::OK(); -} - -// Reset Sampler and wakeup Master thread (functor) -Status MappableLeafOp::Reset() { - MS_LOG(DEBUG) << Name() << " performing a self-reset."; - RETURN_IF_NOT_OK(sampler_->ResetSampler()); - curr_row_ = 0; - return Status::OK(); -} - -// hand shake with Sampler, allow Sampler to call RandomAccessOp's functions to get NumRows -Status MappableLeafOp::InitSampler() { - // Let the sampler know if we are resetting the pipeline to a specific epoch (op_current_repeats_ > 0) - // to mimic the behaviour in that state and have repeatability. - // Note that number of repeats is used since in each epoch we may reset sampler multiple times. - return sampler_->HandshakeRandomAccessOp(this, op_current_repeats_); -} - -// contains the main logic of pulling a IOBlock from IOBlockQueue, load a row and push the row to out_connector_ -// IMPORTANT: 1 IOBlock produces 1 row -Status MappableLeafOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - std::unique_ptr io_block; - - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&io_block)); - RETURN_UNEXPECTED_IF_NULL(io_block); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - start_time = GetSyscnt(); - - while (io_block != nullptr) { - if (io_block->wait()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagWait))); - RETURN_IF_NOT_OK(TaskManager::FindMe()->Wait()); // wait for auto tune update workers successful - TaskManager::FindMe()->Clear(); - } else if (io_block->eoe()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOE))); - } else if (io_block->eof()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOF))); - } else { - std::vector keys; - RETURN_IF_NOT_OK(io_block->GetKeys(&keys)); - if (keys.empty()) { - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, - {{"TensorRowFlags", IOBlock(IOBlock::kFlagQuit).FlagName()}})); - return Status::OK(); // empty key is a quit signal for workers - } - TensorRow trow; - RETURN_IF_NOT_OK(this->LoadTensorRow(keys[0], &trow)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(trow))); - } - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&io_block)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - start_time = GetSyscnt(); - } - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Unexpected nullptr received in worker."); -} - -Status MappableLeafOp::SendWaitFlagToWorker(int32_t worker_id) { - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::make_unique(IOBlock::kFlagWait))); - return Status::OK(); -} - -Status MappableLeafOp::SendQuitFlagToWorker(int32_t worker_id) { - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->Add(std::make_unique(IOBlock::kFlagQuit))); - return Status::OK(); -} - -Status MappableLeafOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - row->clear(); - if (!prepared_data_) { - RETURN_IF_NOT_OK(InitPullMode()); - prepared_data_ = true; - } - if (eof_handled_) { - *row = TensorRow(TensorRow::kFlagEOF); - return Status::OK(); - } - TensorRow sample_row; - if (sample_ids_ == nullptr) { - RETURN_IF_NOT_OK(this->InitSampler()); - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - CHECK_FAIL_RETURN_UNEXPECTED(sample_row.size() > 0, "GetNextRowPullMode: Expect at least one sample in sampler."); - sample_ids_ = sample_row[0]; - MS_LOG(DEBUG) << "Set sample_ids_=" << (*sample_ids_); - } - if (curr_row_ + 1 > sample_ids_->Size()) { - // For 1 billion samples mindrecord, sampler returns 1 million per time, thus it should be called multiple times. - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - if (!sample_row.eoe()) { - curr_row_ = 0; - sample_ids_ = sample_row[0]; - MS_LOG(DEBUG) << "Set sample_ids_=" << (*sample_ids_); - } else { - *row = TensorRow(TensorRow::kFlagEOE); - RETURN_IF_NOT_OK(ResetAndUpdateRepeat()); - return Status::OK(); - } - } - int64_t key; - RETURN_IF_NOT_OK(sample_ids_->GetItemAt(&key, {curr_row_})); - MS_LOG(DEBUG) << "Got key=" << key << " with curr_row_=" << curr_row_; - RETURN_IF_NOT_OK(LoadTensorRowPullMode(key, row)); - curr_row_++; - return Status::OK(); -} - -Status MappableLeafOp::ResetAndUpdateRepeat() { - if (!IsLastIteration()) { - RETURN_IF_NOT_OK(MappableLeafOp::Reset()); - TensorRow sample_row; - RETURN_IF_NOT_OK(sampler_->GetNextSample(&sample_row)); - if (sample_row.eoe()) { - return Status::OK(); - } - CHECK_FAIL_RETURN_UNEXPECTED(sample_row.size() > 0, "GetNextRowPullMode: Expect at least one sample in sampler."); - // Get sample_ids - sample_ids_ = sample_row[0]; - MS_LOG(DEBUG) << "Set sample_ids_=" << (*sample_ids_); - UpdateRepeatAndEpochCounter(); - } else { - eof_handled_ = true; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h deleted file mode 100644 index 85dfd11e0..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MAPPABLE_LEAF_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MAPPABLE_LEAF_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -class MappableLeafOp : public ParallelOp, TensorRow>, public RandomAccessOp { - public: - /// Constructor - /// \param int32_t num_wkrs - Num of workers reading images in parallel - /// \param int32_t queue_size - connector queue size - /// \param td::unique_ptr sampler - sampler tells the source what to read - MappableLeafOp(int32_t num_wkrs, int32_t queue_size, std::shared_ptr sampler); - - /// Destructor. - ~MappableLeafOp() = default; - - /// Main Loop of MappableLeaf - /// Master thread: Fill IOBlockQueue, then goes to sleep - /// Worker thread: pulls IOBlock from IOBlockQueue, work on it then put row to out_connector_ - /// \return Status The status code returned - Status operator()() override; - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "MappableLeafPp"; } - -#ifdef ENABLE_PYTHON - /// \brief Decrypt the encrypted image data as a public function. - /// \param[in] path - The path of the image that needs to be decrypted. - /// \param[in] decrypt - Image decryption function. Default: None, no decryption. - /// \param[out] tensor - Returned tensor. - /// \return Status code. - static Status ImageDecrypt(const std::string &path, std::shared_ptr *tensor, - const py::function &decrypt = py::none()); -#endif - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - TensorPtr sample_ids_; // sample id pointer for pull mode - uint32_t curr_row_; // current row number count for pull mode - bool prepared_data_; // flag to indicate whether the data is prepared before LoadTensorRow for pull mode - bool eof_handled_; // T/F if this op got an eof - - /// Initialize Sampler, calls sampler->Init() within - /// @return Status The status code returned - Status InitSampler(); - - virtual Status InitOp() { - // The order of the following 2 functions must not be changed! - RETURN_IF_NOT_OK(this->PrepareData()); // Prepare data - RETURN_IF_NOT_OK(this->InitSampler()); // pass numRows to Sampler - return Status::OK(); - } - - virtual Status PrepareData() = 0; - - /// Worker thread pulls a number of IOBlock from IOBlock Queue, make a row and push it to Connector - /// \param int32_t workerId - id of each worker - /// \return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; - - /// Virtual function to Load a tensor row at location row_id - /// \param row_id_type row_id - id for this tensor row - /// \param TensorRow row - loaded row - /// \return Status The status code returned - virtual Status LoadTensorRow(row_id_type row_id, TensorRow *row) = 0; - - /// Reset function to be called after every epoch to reset the source op after - /// \return Status The status code returned - Status Reset() override; - Status SendWaitFlagToWorker(int32_t worker_id) override; - Status SendQuitFlagToWorker(int32_t worker_id) override; - - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - virtual Status InitPullMode() { return PrepareData(); } - - /// Virtual function to load a tensor row at location row_id for pull mode - /// \param row_id_type row_id - id for this tensor row - /// \param TensorRow row - loaded row - /// \return Status The status code returned - virtual Status LoadTensorRowPullMode(row_id_type row_id, TensorRow *row) { return LoadTensorRow(row_id, row); } - - /// reset the op and update repeat and epoch number if the condition is met. - /// \return Status The status code returned - Status ResetAndUpdateRepeat(); - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MAPPABLE_LEAF_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.cc deleted file mode 100644 index 78070df70..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.cc +++ /dev/null @@ -1,473 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h" - -#include -#include -#include - -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/mind_record_sampler.h" -#include "minddata/mindrecord/include/shard_column.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { - -using mindrecord::kInt64Len; -using mindrecord::MSRStatus; -using mindrecord::Schema; -using mindrecord::ShardOperator; -using mindrecord::ShardReader; - -// Constructor of the MindRecordOp. -MindRecordOp::MindRecordOp(int32_t num_mind_record_workers, std::vector dataset_file, bool load_dataset, - int32_t op_connector_queue_size, const std::vector &columns_to_load, - const std::vector> &operators, int64_t num_padded, - const mindrecord::json &sample_json, const std::map &sample_bytes, - const ShuffleMode shuffle_mode, std::unique_ptr shard_reader, - std::shared_ptr sampler) - : MappableLeafOp(num_mind_record_workers, op_connector_queue_size, std::move(sampler)), - dataset_file_(std::move(dataset_file)), - load_dataset_(load_dataset), - columns_to_load_(columns_to_load), - operators_(operators), - num_mind_record_workers_(num_mind_record_workers), - ended_worker_(0), - num_padded_(num_padded), - sample_json_(sample_json), - sample_bytes_(sample_bytes), - shuffle_mode_(shuffle_mode), - shard_reader_(std::move(shard_reader)) { - epoch_sync_flag_ = true; // MindRecordOp needs to turn this flag on, otherwise, calling ShuffleTask() before all - // tasks are consumed by the worker threads would cause problem. -} - -// Private helper method to encapsulate some common construction/reset tasks -Status MindRecordOp::Init() { - RETURN_IF_NOT_OK(shard_reader_->Open(dataset_file_, load_dataset_, num_mind_record_workers_, columns_to_load_, - operators_, num_padded_)); - - data_schema_ = std::make_unique(); - - std::vector col_names = shard_reader_->GetShardColumn()->GetColumnName(); - CHECK_FAIL_RETURN_UNEXPECTED(!col_names.empty(), - "Invalid column, no column names are specified, check mindrecord file."); - std::vector col_data_types = shard_reader_->GetShardColumn()->GeColumnDataType(); - std::vector> col_shapes = shard_reader_->GetShardColumn()->GetColumnShape(); - - bool load_all_cols = columns_to_load_.empty(); // if columns_to_load_ is empty it means load everything - std::map colname_to_ind; - for (uint32_t i = 0; i < col_names.size(); i++) { - std::string colname = col_names[i]; - ColDescriptor col_desc; - - TensorShape t_shape = TensorShape::CreateUnknownRankShape(); // shape of tensor, default unknown - std::string type_str = mindrecord::ColumnDataTypeNameNormalized[col_data_types[i]]; - DataType t_dtype = DataType(type_str); // valid types: {"bytes", "string", "int32", "int64", "float32", "float64"} - - if (col_data_types[i] == mindrecord::ColumnBytes) { // rank = 1 - col_desc = ColDescriptor(colname, t_dtype, TensorImpl::kFlexible, 1); - } else if (col_data_types[i] == mindrecord::ColumnString) { // rank = 0 - col_desc = ColDescriptor(colname, t_dtype, TensorImpl::kFlexible, 0); - } else if (col_shapes[i].size() > 0) { - std::vector vec(col_shapes[i].size()); // temporary vector to hold shape - (void)std::copy(col_shapes[i].begin(), col_shapes[i].end(), vec.begin()); - t_shape = TensorShape(vec); - col_desc = ColDescriptor(colname, t_dtype, TensorImpl::kFlexible, t_shape.Rank(), &t_shape); - } else { // unknown shape - // create colDesc and add it to schema - col_desc = ColDescriptor(colname, t_dtype, TensorImpl::kFlexible, t_shape.Rank(), &t_shape); - } - - colname_to_ind[colname] = data_schema_->NumColumns(); - RETURN_IF_NOT_OK(data_schema_->AddColumn(col_desc)); - - if (load_all_cols) { - columns_to_load_.emplace_back(colname); - } - } - - if (!load_all_cols) { - std::unique_ptr tmp_schema = std::make_unique(); - for (std::string colname : columns_to_load_) { - CHECK_FAIL_RETURN_UNEXPECTED(colname_to_ind.find(colname) != colname_to_ind.end(), - "Invalid column, " + colname + " does not exist in data file."); - RETURN_IF_NOT_OK(tmp_schema->AddColumn(data_schema_->Column(colname_to_ind[colname]))); - } - data_schema_ = std::move(tmp_schema); - } - - return Status::OK(); -} - -// Destructor -MindRecordOp::~MindRecordOp() {} - -// A print method typically used for debugging -void MindRecordOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nDataset file : "; - for (auto &file : dataset_file_) { - out << file << " "; - } - out << "\nNumber of rows : " << num_rows_ << "\nNumber of ShardReader workers : " << num_mind_record_workers_ - << "\n\n"; - } -} - -Status MindRecordOp::WorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - std::unique_ptr io_block; - - uint64_t start_time = GetSyscnt(); - double row_timer_start = 0; - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&io_block)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - - while (io_block != nullptr) { - if (io_block->wait()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagWait))); - RETURN_IF_NOT_OK(TaskManager::FindMe()->Wait()); // wait for auto tune update workers successful - TaskManager::FindMe()->Clear(); - } else if (io_block->eoe()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOE))); - } else if (io_block->eof()) { - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(TensorRow(TensorRow::TensorRowFlags::kFlagEOF))); - } else { - // load TensorRow - std::vector keys; - RETURN_IF_NOT_OK(io_block->GetKeys(&keys)); - if (keys.empty() == true) { - { - std::unique_lock lock(ended_worker_mutex_); - ended_worker_++; - if (ended_worker_ == num_workers_) { - shard_reader_->Close(); - } - } - RETURN_IF_NOT_OK(CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, - {{"TensorRowFlags", IOBlock(IOBlock::kFlagQuit).FlagName()}})); - return Status::OK(); // empty key is a quit signal for workers - } - - const uint64_t row_id = keys[0]; - TensorRow fetched_row; - - // Get the next row. Push it up to the output connector. - if (row_id % LOG_INTERVAL == 0) { - MS_LOG(DEBUG) << "MindRecord operator consumed row " << row_id << " by worker " << worker_id << "."; - } - RETURN_IF_NOT_OK(GetRowFromReader(&fetched_row, row_id, worker_id)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - fetched_row.TimerRecord(NameWithID(), RowTimer::kWorkerTime, {GetMilliTimeStamp() - row_timer_start}); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(std::move(fetched_row))); - } - row_timer_start = GetMilliTimeStamp(); - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&io_block)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - start_time = GetSyscnt(); - } - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Unexpected nullptr received in worker."); -} - -Status MindRecordOp::GetRowFromReader(TensorRow *fetched_row, uint64_t row_id, int32_t worker_id) { - RETURN_UNEXPECTED_IF_NULL(fetched_row); - *fetched_row = {}; - auto task_content_ptr = std::make_shared( - mindrecord::TaskType::kCommonTask, std::vector, mindrecord::json>>()); - double row_timer_start = GetMilliTimeStamp(); - RETURN_IF_NOT_OK(shard_reader_->GetNextById(row_id, worker_id, &task_content_ptr)); - fetched_row->TimerRecord(NameWithID(), RowTimer::kIOTime, {GetMilliTimeStamp() - row_timer_start}); - auto task_type = task_content_ptr->first; - auto tupled_buffer = task_content_ptr->second; - if (task_type == mindrecord::TaskType::kPaddedTask) { - RETURN_IF_NOT_OK(LoadTensorRow(fetched_row, {}, mindrecord::json(), task_type)); - std::vector file_path(fetched_row->size(), dataset_file_[0]); - fetched_row->setPath(file_path); - fetched_row->setId(row_id); - } - if (tupled_buffer.empty()) { - return Status::OK(); - } - if (task_type == mindrecord::TaskType::kCommonTask) { - for (const auto &tupled_row : tupled_buffer) { - std::vector columns_blob = std::get<0>(tupled_row); - mindrecord::json columns_json = std::get<1>(tupled_row); - RETURN_IF_NOT_OK(LoadTensorRow(fetched_row, columns_blob, columns_json, task_type)); - std::vector file_path(fetched_row->size(), dataset_file_[0]); - fetched_row->setPath(file_path); - fetched_row->setId(row_id); - } - } - - return Status::OK(); -} - -Status MindRecordOp::LoadTensorRow(TensorRow *tensor_row, const std::vector &columns_blob, - const mindrecord::json &columns_json, const mindrecord::TaskType task_type) { - RETURN_UNEXPECTED_IF_NULL(tensor_row); - for (int32_t i_col = 0; i_col < columns_to_load_.size(); i_col++) { - auto column_name = columns_to_load_[i_col]; - - // Initialize column parameters - const unsigned char *data = nullptr; - std::unique_ptr data_ptr; - uint64_t n_bytes = 0; - mindrecord::ColumnDataType column_data_type = mindrecord::ColumnNoDataType; - uint64_t column_data_type_size = 1; - std::vector column_shape; - - // Get column data - auto shard_column = shard_reader_->GetShardColumn(); - if (num_padded_ > 0 && task_type == mindrecord::TaskType::kPaddedTask) { - mindrecord::ColumnCategory category; - RETURN_IF_NOT_OK(shard_column->GetColumnTypeByName(column_name, &column_data_type, &column_data_type_size, - &column_shape, &category)); - if (category == mindrecord::ColumnInRaw) { - CHECK_FAIL_RETURN_UNEXPECTED_MR(sample_json_[column_name].is_string() || sample_json_[column_name].is_number(), - "Invalid padded_sample, the value of column: " + column_name + - " should be string or number but got: " + sample_json_[column_name].dump() + - ", check 'padded_sample'."); - RETURN_IF_NOT_OK(shard_column->GetColumnFromJson(column_name, sample_json_, &data_ptr, &n_bytes)); - } else if (category == mindrecord::ColumnInBlob) { - CHECK_FAIL_RETURN_UNEXPECTED(sample_bytes_.find(column_name) != sample_bytes_.end(), - "Invalid padded_sample, failed to retrieve blob data from padding sample, " - "check 'padded_sample'."); - - std::string ss(sample_bytes_[column_name]); - n_bytes = ss.size(); - data_ptr = std::make_unique(n_bytes); - (void)std::copy(ss.begin(), ss.end(), data_ptr.get()); - } else { - RETURN_STATUS_UNEXPECTED("Invalid datatype, retrieved data type is unknown."); - } - if (data == nullptr) { - data = reinterpret_cast(data_ptr.get()); - } - } else { - RETURN_IF_NOT_OK(shard_column->GetColumnValueByName(column_name, columns_blob, columns_json, &data, &data_ptr, - &n_bytes, &column_data_type, &column_data_type_size, - &column_shape)); - } - - std::shared_ptr tensor; - const ColDescriptor &column = data_schema_->Column(i_col); - DataType type = column.Type(); - - // Set shape - CHECK_FAIL_RETURN_UNEXPECTED(column_data_type_size != 0, - "[Internal ERROR] Found memory size of column data type is 0."); - auto num_elements = n_bytes / column_data_type_size; - if (type == DataType::DE_STRING) { - std::string s{data, data + n_bytes}; - RETURN_IF_NOT_OK(Tensor::CreateScalar(s, &tensor)); - } else if (type == DataType::DE_BYTES) { - std::vector strings; - strings.push_back(std::string(reinterpret_cast(data), num_elements)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(strings, TensorShape({1}), DataType(DataType::DE_BYTES), &tensor)); - } else if (column.HasShape()) { - auto new_shape = TensorShape(column.Shape()); - // if the numpy is null, create empty tensor shape - if (num_elements == 0) { - new_shape = TensorShape({}); - } else { - RETURN_IF_NOT_OK(column.MaterializeTensorShape(static_cast(num_elements), &new_shape)); - } - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(new_shape, type, data, &tensor)); - } else { - std::vector shapeDetails = {static_cast(num_elements)}; - auto new_shape = TensorShape(shapeDetails); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(new_shape, type, data, &tensor)); - } - tensor_row->push_back(std::move(tensor)); - } - return Status::OK(); -} - -// Overrides base class reset method. When an operator does a reset, it cleans up any state -// info from it's previous execution and then initializes itself so that it can be executed -// again. -Status MindRecordOp::Reset() { - MS_LOG(DEBUG) << Name() << " performing a self-reset."; - RETURN_IF_NOT_OK(WaitForWorkers()); - RETURN_IF_NOT_OK(MappableLeafOp::Reset()); // Call our super class reset first. - - // wakeup workers - for (auto &item : worker_tasks_) { - item->Post(); - } - - // wakeup the collector thread - wait_for_collector_.Set(); - - return Status::OK(); -} - -Status MindRecordOp::PrepareData() { - num_rows_ = shard_reader_->GetNumRows(); - return Status::OK(); -} - -Status MindRecordOp::RegisterAndLaunchThreads() { - RETURN_IF_NOT_OK(ParallelOp::RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(shard_reader_->Launch(true)); - return Status::OK(); -} - -Status MindRecordOp::ShardReaderLaunch() { - RETURN_IF_NOT_OK(shard_reader_->Launch(true)); - return Status::OK(); -} - -Status MindRecordOp::CountTotalRows(const std::vector dataset_path, bool load_dataset, - const std::shared_ptr &op, int64_t *count, int64_t num_padded) { - RETURN_UNEXPECTED_IF_NULL(op); - RETURN_UNEXPECTED_IF_NULL(count); - std::unique_ptr shard_reader = std::make_unique(); - RETURN_IF_NOT_OK(shard_reader->CountTotalRows(dataset_path, load_dataset, op, count, num_padded)); - return Status::OK(); -} - -Status MindRecordOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int i = 0; i < static_cast(columns_to_load_.size()); i++) { - column_name_id_map_[columns_to_load_[i]] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status MindRecordOp::AddNewWorkers(int32_t num_new_workers) { - // wait for workers to process the current rows - RETURN_IF_NOT_OK(WaitForWorkers()); - - RETURN_IF_NOT_OK(shard_reader_->ExtendRandomFileStreams(num_new_workers)); - num_mind_record_workers_ += num_new_workers; - - // create queue first - for (int32_t i = 0; i < num_new_workers; i++) { - RETURN_IF_NOT_OK(worker_in_queues_.AddQueue(tree_->AllTasks())); - RETURN_IF_NOT_OK(worker_out_queues_.AddQueue(tree_->AllTasks())); - } - - // launch new workers - for (int32_t i = 0; i < num_new_workers; i++) { - Task *new_task; - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask( - Name() + "::WorkerEntry", std::bind(&MindRecordOp::WorkerEntry, this, num_workers_), &new_task, id())); - CHECK_FAIL_RETURN_UNEXPECTED(new_task != nullptr, "Cannot create a new worker."); - worker_tasks_.push_back(new_task); - num_workers_++; - MS_LOG(INFO) << "A new worker has been added to op: " << Name() << "::" << id() << " num_workers=" << num_workers_; - } - - // wakeup old workers - for (auto &item : worker_tasks_) { - item->Post(); - } - - // wakeup the collector thread - wait_for_collector_.Set(); - - return Status::OK(); -} - -Status MindRecordOp::RemoveWorkers(int32_t num_workers) { - // wait for workers to process the current rows - RETURN_IF_NOT_OK(WaitForWorkers()); - - num_mind_record_workers_ -= num_workers; - RETURN_IF_NOT_OK(shard_reader_->ShrinkRandomFileStreams(num_workers)); - - for (int32_t i = 0; i < num_workers; i++) { - RETURN_IF_NOT_OK(SendQuitFlagToWorker(num_workers_ - 1)); - worker_tasks_[num_workers_ - 1]->Post(); - RETURN_IF_NOT_OK(worker_tasks_[num_workers_ - 1]->Join()); - RETURN_IF_NOT_OK(worker_in_queues_.RemoveLastQueue()); - worker_tasks_.pop_back(); - num_workers_--; - MS_LOG(INFO) << "Worker ID " << num_workers_ << " is requested to be removed in operator: " << NameWithID() - << " num_workers=" << num_workers_; - } - - // wakeup left workers - for (auto &item : worker_tasks_) { - item->Post(); - } - - // wakeup the collector thread - wait_for_collector_.Set(); - - return Status::OK(); -} - -Status MindRecordOp::InitPullMode() { - num_workers_ = 1; - RETURN_IF_NOT_OK(Init()); - RETURN_IF_NOT_OK(shard_reader_->Launch(true)); - return this->PrepareData(); -} - -Status MindRecordOp::LoadTensorRowPullMode(row_id_type row_id, TensorRow *row) { - return GetRowFromReader(row, row_id, 0); -} - -Status MindRecordOp::GetRowByIndex(size_t index, TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - size_t real_index = 0; - RETURN_IF_NOT_OK(shard_reader_->GetMappedIndex(index, &real_index)); - RETURN_IF_NOT_OK(GetRowFromReader(row, real_index, 0)); - return Status::OK(); -} - -TensorRow MindRecordOp::operator[](size_t index) { - TensorRow row; - auto status = GetRowByIndex(index, &row); - if (status.IsError()) { - MS_LOG(EXCEPTION) << status.ToString(); - } - return row; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h deleted file mode 100644 index 498e4b76f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MINDRECORD_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MINDRECORD_OP_H_ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "minddata/mindrecord/include/shard_column.h" -#include "minddata/mindrecord/include/shard_reader.h" -#include "minddata/mindrecord/include/common/shard_utils.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using mindrecord::ShardOperator; -using mindrecord::ShardReader; -using ShardTuple = std::vector, mindrecord::json>>; /// Row of data from ShardReader - -const int32_t LOG_INTERVAL = 19; - -class MindRecordOp : public MappableLeafOp { - public: - // Constructor of the MindRecordOp. - // @note The builder class should be used to call it - // @param num_mind_record_workers - The number of workers for the op (run by ShardReader) - // @param dataset_file - dataset files - // @param op_connector_queue_size - The output connector queue size - // @param columns_to_load - The list of columns to use (column name) - // @param operators - ShardOperators for Shuffle, Category, Sample - // @param sampler - sampler tells MindRecordOp what to read - MindRecordOp(int32_t num_mind_record_workers, std::vector dataset_file, bool load_dataset, - int32_t op_connector_queue_size, const std::vector &columns_to_load, - const std::vector> &operators, int64_t num_padded_, - const mindrecord::json &sample_json, const std::map &sample_bytes_, - const ShuffleMode shuffle_mode_, std::unique_ptr shard_reader, - std::shared_ptr sampler); - - /// Destructor - ~MindRecordOp() override; - - /// A print method typically used for debugging - /// @param out - The output stream to write output to - /// @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// << Stream output operator overload - /// @notes This allows you to write the debug print info using stream operators - /// @param out - reference to the output stream being overloaded - /// @param op - reference to the MindRecordOp to display - /// @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const MindRecordOp &op) { - op.Print(out, false); - return out; - } - - // Worker thread pulls a number of IOBlock from IOBlock Queue, make a TensorRow and push it to Connector - // @param int32_t workerId - id of each worker - // @return Status The status code returned - Status WorkerEntry(int32_t worker_id) override; - - // Called first when function is called - // @return - Status RegisterAndLaunchThreads() override; - - /// Overrides base class reset method. When an operator does a reset, it cleans up any state - /// info from it's previous execution and then initializes itself so that it can be executed - /// again. - /// @return Status The status code returned - Status Reset() override; - - static Status CountTotalRows(const std::vector dataset_path, bool load_dataset, - const std::shared_ptr &op, int64_t *count, int64_t num_padded); - - // Getter method - std::vector dataset_file() const { return dataset_file_; } - - /// Getter method - std::vector columns_to_load() const { return columns_to_load_; } - - bool load_dataset() const { return load_dataset_; } - - Status Init(); - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "MindRecordOp"; } - - // The Launch method of shard_reader_ needs to be called when using the dataset[index] behavior - // @return - Status ShardReaderLaunch(); - - TensorRow operator[](size_t index); - - private: - Status GetRowFromReader(TensorRow *fetched_row, uint64_t row_id, int32_t worker_id); - - /// Parses a single cell and puts the data into a tensor - /// @param tensor_row - the tensor row to put the parsed data in - /// @param columns_blob - the blob data received from the reader - /// @param columns_json - the data for fields received from the reader - Status LoadTensorRow(TensorRow *tensor_row, const std::vector &columns_blob, - const mindrecord::json &columns_json, const mindrecord::TaskType task_type); - - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override { - return Status(StatusCode::kMDSyntaxError, "[Internal ERROR] Cannot call this method."); - } - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - protected: - Status PrepareData() override; - - /// Add a new worker to the MindRecordOp. The function will have to wait for all workers to process current rows. - /// It will then update the shard reader. Finally, it adds a new thread to the list. - /// \note The caller of this function has to be the main thread of the Op, since it's the only entity responsible to - /// push rows to workers_in_queue - /// \return Status The status code returned - Status AddNewWorkers(int32_t num_new_workers = 1) override; - - /// Remove a worker from MindRecordOp. The function will have to wait for all workers to process current rows. - /// It will then update the shard reader. Finally, it removes a thread from the list. - /// \note The caller of this function has to be the main thread of the Op, since it's the only entity responsible to - /// push rows to workers_in_queue - /// \return Status The status code returned - Status RemoveWorkers(int32_t num_workers = 1) override; - - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - Status InitPullMode() override; - - /// Load a tensor row at location row_id for pull mode - /// \param row_id_type row_id - id for this tensor row - /// \param TensorRow row - loaded row - /// \return Status The status code returned - Status LoadTensorRowPullMode(row_id_type row_id, TensorRow *row) override; - - /// Calculate sampler id based on index to return corresponding data - /// \param size_t index - indexing of dataset - /// \param TensorRow row - Get data after sampler based on index - /// @return Status The status code returned - Status GetRowByIndex(size_t index, TensorRow *row); - - private: - std::vector dataset_file_; // dataset files - bool load_dataset_; // load dataset from single file or not - std::vector columns_to_load_; // Columns to load from dataset - std::vector> operators_; // ShardOperators to use - int32_t num_mind_record_workers_; // number of workers to be spawned by ShardReader - std::atomic ended_worker_; - - int64_t num_padded_; - mindrecord::json sample_json_; - std::map sample_bytes_; - - std::unique_ptr data_schema_; // Data schema for column typing - - std::unique_ptr shard_reader_; - - std::mutex ended_worker_mutex_; - - ShuffleMode shuffle_mode_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MINDRECORD_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.cc deleted file mode 100644 index 8a1c11651..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.cc +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" - -#include -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -const int32_t kMnistImageFileMagicNumber = 2051; -const int32_t kMnistLabelFileMagicNumber = 2049; -const int32_t kMnistImageRows = 28; -const int32_t kMnistImageCols = 28; - -MnistOp::MnistOp(std::string usage, int32_t num_workers, std::string folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(std::move(folder_path)), - usage_(std::move(usage)), - data_schema_(std::move(data_schema)), - image_path_({}), - label_path_({}) {} - -// Load 1 TensorRow (image,label) using 1 MnistLabelPair. -Status MnistOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - MnistLabelPair mnist_pair = image_label_pairs_[row_id]; - std::shared_ptr image, label; - // make a copy of cached tensor - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(mnist_pair.first, &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(mnist_pair.second, &label)); - - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({image_path_[row_id], label_path_[row_id]}); - return Status::OK(); -} - -void MnistOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows:" << num_rows_ << "\n" << DatasetName(true) << " Directory: " << folder_path_ << "\n\n"; - } -} - -// Derived from RandomAccessOp -Status MnistOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_label_pairs_.empty()) { - if (image_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid " + DatasetName() + " file, image data is missing."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < image_label_pairs_.size(); ++i) { - (*cls_ids)[image_label_pairs_[i].second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status MnistOp::ReadFromReader(std::ifstream *reader, uint32_t *result) { - uint32_t res = 0; - reader->read(reinterpret_cast(&res), 4); - CHECK_FAIL_RETURN_UNEXPECTED(!reader->fail(), - "Invalid file, failed to read 4 bytes from " + DatasetName() + " file."); - *result = SwapEndian(res); - return Status::OK(); -} - -uint32_t MnistOp::SwapEndian(uint32_t val) const { - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); - return (val << 16) | (val >> 16); -} - -Status MnistOp::CheckImage(const std::string &file_name, std::ifstream *image_reader, uint32_t *num_images) { - CHECK_FAIL_RETURN_UNEXPECTED(image_reader->is_open(), "Invalid " + DatasetName() + " file, failed to open " + - file_name + " : the file is damaged or permission denied."); - int64_t image_len = image_reader->seekg(0, std::ios::end).tellg(); - (void)image_reader->seekg(0, std::ios::beg); - // The first 16 bytes of the image file are type, number, row and column - CHECK_FAIL_RETURN_UNEXPECTED(image_len >= 16, - "Invalid " + DatasetName() + " file, the first data length of " + file_name + - " should be 16 bytes(contains type, number, row and column), but got " + - std::to_string(image_len) + "."); - - uint32_t magic_number; - RETURN_IF_NOT_OK(ReadFromReader(image_reader, &magic_number)); - CHECK_FAIL_RETURN_UNEXPECTED(magic_number == kMnistImageFileMagicNumber, - "Invalid " + DatasetName() + " file, the image number of " + file_name + " should be " + - std::to_string(kMnistImageFileMagicNumber) + ", but got " + - std::to_string(magic_number)); - - uint32_t num_items; - RETURN_IF_NOT_OK(ReadFromReader(image_reader, &num_items)); - uint32_t rows; - RETURN_IF_NOT_OK(ReadFromReader(image_reader, &rows)); - uint32_t cols; - RETURN_IF_NOT_OK(ReadFromReader(image_reader, &cols)); - // The image size of the Mnist dataset is fixed at [28,28] - CHECK_FAIL_RETURN_UNEXPECTED((rows == kMnistImageRows) && (cols == kMnistImageCols), - "Invalid " + DatasetName() + " file, shape of image in " + file_name + - " should be (28, 28), but got (" + std::to_string(rows) + ", " + std::to_string(cols) + - ")."); - CHECK_FAIL_RETURN_UNEXPECTED((image_len - 16) == num_items * rows * cols, - "Invalid " + DatasetName() + " file, truncated data length of " + file_name + - " should be " + std::to_string(image_len - 16) + ", but got " + - std::to_string(num_items * rows * cols)); - *num_images = num_items; - return Status::OK(); -} - -Status MnistOp::CheckLabel(const std::string &file_name, std::ifstream *label_reader, uint32_t *num_labels) { - CHECK_FAIL_RETURN_UNEXPECTED(label_reader->is_open(), "Invalid " + DatasetName() + " file, failed to open " + - file_name + " : the file is damaged or permission denied!"); - int64_t label_len = label_reader->seekg(0, std::ios::end).tellg(); - (void)label_reader->seekg(0, std::ios::beg); - // The first 8 bytes of the image file are type and number - CHECK_FAIL_RETURN_UNEXPECTED(label_len >= 8, "Invalid " + DatasetName() + " file, the first data length of " + - file_name + " should be 8 bytes(contains type and number), but got " + - std::to_string(label_len) + "."); - uint32_t magic_number; - RETURN_IF_NOT_OK(ReadFromReader(label_reader, &magic_number)); - CHECK_FAIL_RETURN_UNEXPECTED(magic_number == kMnistLabelFileMagicNumber, - "Invalid " + DatasetName() + " file, the number of labels in " + file_name + - " should be " + std::to_string(kMnistLabelFileMagicNumber) + ", but got " + - std::to_string(magic_number) + "."); - uint32_t num_items; - RETURN_IF_NOT_OK(ReadFromReader(label_reader, &num_items)); - CHECK_FAIL_RETURN_UNEXPECTED((label_len - 8) == num_items, "Invalid " + DatasetName() + - " file, the data length of labels in " + file_name + - " should be " + std::to_string(label_len - 8) + - ", but got " + std::to_string(num_items) + "."); - *num_labels = num_items; - return Status::OK(); -} - -Status MnistOp::ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index) { - RETURN_UNEXPECTED_IF_NULL(image_reader); - RETURN_UNEXPECTED_IF_NULL(label_reader); - uint32_t num_images, num_labels; - RETURN_IF_NOT_OK(CheckImage(image_names_[index], image_reader, &num_images)); - RETURN_IF_NOT_OK(CheckLabel(label_names_[index], label_reader, &num_labels)); - CHECK_FAIL_RETURN_UNEXPECTED((num_images == num_labels), - "Invalid " + DatasetName() + " file, the images number of " + image_names_[index] + - " should be equal to the labels number of " + label_names_[index] + - ", but got images number: " + std::to_string(num_images) + - ", labels number: " + std::to_string(num_labels) + "."); - // The image size of the Mnist dataset is fixed at [28,28] - int64_t size = kMnistImageRows * kMnistImageCols; - auto images_buf = std::make_unique(size * num_images); - auto labels_buf = std::make_unique(num_images); - if (images_buf == nullptr || labels_buf == nullptr) { - std::string err_msg = "[Internal ERROR] Failed to allocate memory for " + DatasetName() + " buffer."; - MS_LOG(ERROR) << err_msg.c_str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - (void)image_reader->read(images_buf.get(), size * num_images); - if (image_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid " + DatasetName() + " file, failed to read " + image_names_[index] + - " : the file is damaged or permission denied!"); - } - (void)label_reader->read(labels_buf.get(), num_images); - if (label_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid " + DatasetName() + " file, failed to read " + label_names_[index] + - " : the file is damaged or the file content is incomplete."); - } - TensorShape img_tensor_shape = TensorShape({kMnistImageRows, kMnistImageCols, 1}); - for (int64_t j = 0; j != num_images; ++j) { - auto pixels = &images_buf[j * size]; - std::shared_ptr image; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(img_tensor_shape, data_schema_->Column(0).Type(), - reinterpret_cast(pixels), &image)); - image_label_pairs_.emplace_back(std::make_pair(image, labels_buf[j])); - image_path_.push_back(image_names_[index]); - label_path_.push_back(label_names_[index]); - } - return Status::OK(); -} - -Status MnistOp::PrepareData() { - RETURN_IF_NOT_OK(this->WalkAllFiles()); - // MNIST contains 4 files, idx3 are image files, idx 1 are labels - // training files contain 60K examples and testing files contain 10K examples - // t10k-images-idx3-ubyte t10k-labels-idx1-ubyte train-images-idx3-ubyte train-labels-idx1-ubyte - for (size_t i = 0; i < image_names_.size(); ++i) { - std::ifstream image_reader, label_reader; - image_reader.open(image_names_[i], std::ios::in | std::ios::binary); - label_reader.open(label_names_[i], std::ios::in | std::ios::binary); - - Status s = ReadImageAndLabel(&image_reader, &label_reader, i); - // Close the readers - image_reader.close(); - label_reader.close(); - RETURN_IF_NOT_OK(s); - } - image_label_pairs_.shrink_to_fit(); - num_rows_ = image_label_pairs_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file in directory: " + folder_path_); - } - return Status::OK(); -} - -Status MnistOp::WalkAllFiles() { - const std::string img_ext = "idx3-ubyte"; - const std::string lbl_ext = "idx1-ubyte"; - const std::string train_prefix = "train"; - const std::string test_prefix = "t10k"; - - std::string real_path{""}; - RETURN_IF_NOT_OK(Path::RealPath(folder_path_, real_path)); - Path dir(real_path); - auto dir_it = Path::DirIterator::OpenDirectory(&dir); - std::string prefix; // empty string, used to match usage = "" (default) or usage == "all" - if (usage_ == "train" || usage_ == "test") { - prefix = (usage_ == "test" ? test_prefix : train_prefix); - } - if (dir_it != nullptr) { - while (dir_it->HasNext()) { - Path file = dir_it->Next(); - std::string fname = file.Basename(); // name of the mnist file - if ((fname.find(prefix + "-images") != std::string::npos) && (fname.find(img_ext) != std::string::npos)) { - image_names_.push_back(file.ToString()); - MS_LOG(INFO) << DatasetName(true) << " operator found image file at " << fname << "."; - } else if ((fname.find(prefix + "-labels") != std::string::npos) && (fname.find(lbl_ext) != std::string::npos)) { - label_names_.push_back(file.ToString()); - MS_LOG(INFO) << DatasetName(true) << " Operator found label file at " << fname << "."; - } - } - } else { - MS_LOG(WARNING) << DatasetName(true) << " operator unable to open directory " << dir.ToString() << "."; - } - - std::sort(image_names_.begin(), image_names_.end()); - std::sort(label_names_.begin(), label_names_.end()); - - CHECK_FAIL_RETURN_UNEXPECTED( - image_names_.size() == label_names_.size(), - "Invalid " + DatasetName() + " file, num of images should be equal to num of labels, but got num of images: " + - std::to_string(image_names_.size()) + ", num of labels: " + std::to_string(label_names_.size()) + "."); - - return Status::OK(); -} - -Status MnistOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - // the logic of counting the number of samples is copied from ParseMnistData() and uses CheckReader() - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - RETURN_IF_NOT_OK(op->WalkAllFiles()); - - for (size_t i = 0; i < op->image_names_.size(); ++i) { - std::ifstream image_reader; - image_reader.open(op->image_names_[i], std::ios::in | std::ios::binary); - std::ifstream label_reader; - label_reader.open(op->label_names_[i], std::ios::in | std::ios::binary); - - uint32_t num_images; - auto s = op->CheckImage(op->image_names_[i], &image_reader, &num_images); - if (s != Status::OK()) { - image_reader.close(); - label_reader.close(); - return s; - } - uint32_t num_labels; - s = op->CheckLabel(op->label_names_[i], &label_reader, &num_labels); - if (s != Status::OK()) { - image_reader.close(); - label_reader.close(); - return s; - } - if (num_images != num_labels) { - image_reader.close(); - label_reader.close(); - RETURN_STATUS_UNEXPECTED("Invalid " + op->DatasetName() + - " file, num of images should be equal to num of labels, but got num of images: " + - std::to_string(num_images) + ", num of labels: " + std::to_string(num_labels) + "."); - } - *count = *count + num_images; - - // Close the readers - image_reader.close(); - label_reader.close(); - } - - return Status::OK(); -} - -Status MnistOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h deleted file mode 100644 index 25c77e26a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MNIST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MNIST_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using MnistLabelPair = std::pair, uint32_t>; - -class MnistOp : public MappableLeafOp { - public: - // Constructor - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all' - // @param int32_t num_workers - number of workers reading images in parallel - // @param std::string folder_path - dir directory of mnist - // @param int32_t queue_size - connector queue size - // @param std::unique_ptr data_schema - the schema of the mnist dataset - // @param td::unique_ptr sampler - sampler tells MnistOp what to read - MnistOp(std::string usage, int32_t num_workers, std::string folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - // Destructor. - ~MnistOp() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class - // @param (std::map> * map - key label, val all ids for this class - // @return Status The status code returned - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging - // @param out - // @param show_all - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the MNIST dataset - // @param dir path to the MNIST directory - // @param count output arg that will hold the minimum of the actual dataset size and numSamples - // @return - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "MnistOp"; } - - // DatasetName name getter - // \return DatasetName of the current Op - virtual std::string DatasetName(bool upper = false) const { return upper ? "Mnist" : "mnist"; } - - protected: - // Load a tensor row according to a pair - // @param row_id_type row_id - id for this tensor row - // @param ImageLabelPair pair - - // @param TensorRow row - image & label read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - // Check image file stream. - // @param const std::string *file_name - image file name - // @param std::ifstream *image_reader - image file stream - // @param uint32_t num_images - returns the number of images - // @return Status The status code returned - virtual Status CheckImage(const std::string &file_name, std::ifstream *image_reader, uint32_t *num_images); - - // Check label stream. - // @param const std::string &file_name - label file name - // @param std::ifstream *label_reader - label file stream - // @param uint32_t num_labels - returns the number of labels - // @return Status The status code returned - virtual Status CheckLabel(const std::string &file_name, std::ifstream *label_reader, uint32_t *num_labels); - - // Read 4 bytes of data from a file stream. - // @param std::ifstream *reader - file stream to read - // @return uint32_t - read out data - Status ReadFromReader(std::ifstream *reader, uint32_t *result); - - // Swap endian - // @param uint32_t val - - // @return uint32_t - swap endian data - uint32_t SwapEndian(uint32_t val) const; - - // Read the specified number of images and labels from the file stream - // @param std::ifstream *image_reader - image file stream - // @param std::ifstream *label_reader - label file stream - // @param int64_t read_num - number of image to read - // @return Status The status code returned - virtual Status ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index); - - // Parse all mnist dataset files - // @return Status The status code returned - Status PrepareData() override; - - // Read all files in the directory - // @return Status The status code returned - virtual Status WalkAllFiles(); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - std::string folder_path_; // directory of image folder - const std::string usage_; // can only be either "train" or "test" - std::unique_ptr data_schema_; - std::vector image_label_pairs_; - std::vector image_names_; - std::vector label_names_; - std::vector image_path_; - std::vector label_path_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MNIST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.cc deleted file mode 100644 index 5e924f0c2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.cc +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h" - -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -// constructor of Multi30k. -Multi30kOp::Multi30kOp(int32_t num_workers, int64_t num_samples, const std::vector &language_pair, - int32_t worker_connector_size, std::unique_ptr schema, - const std::vector &text_files_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, num_samples, worker_connector_size, std::move(schema), std::move(text_files_list), - op_connector_size, shuffle_files, num_devices, device_id), - language_pair_(language_pair) {} - -// Print info of operator. -void Multi30kOp::Print(std::ostream &out, bool show_all) { - // Print parameter to debug function. - std::vector multi30k_files_list = TextFileOp::FileNames(); - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nMulti30k files list:\n"; - for (int i = 0; i < multi30k_files_list.size(); ++i) { - out << " " << multi30k_files_list[i]; - } - out << "\n\n"; - } -} - -Status Multi30kOp::LoadTensor(const std::string &line, TensorRow *out_row, size_t index) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(line, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -Status Multi30kOp::LoadFile(const std::string &file_en, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath_en = FileUtils::GetRealPath(file_en.c_str()); - if (!realpath_en.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << DatasetName() + " Dataset file: " << file_en << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + DatasetName() + " Dataset file: " + file_en + " does not exist."); - } - - // We use English files to find Germany files, to make sure that data are ordered. - Path path_en(file_en); - Path parent_path(path_en.ParentPath()); - std::string basename = path_en.Basename(); - int suffix_len = 3; - std::string suffix_de = ".de"; - auto pos = basename.find("."); - CHECK_FAIL_RETURN_UNEXPECTED(pos != std::string::npos, "Invalid file, can not parse dataset file:" + file_en); - basename = basename.replace(pos, suffix_len, suffix_de); - Path BaseName(basename); - Path path_de = parent_path / BaseName; - std::string file_de = path_de.ToString(); - auto realpath_de = FileUtils::GetRealPath(file_de.c_str()); - if (!realpath_de.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << DatasetName() + " Dataset file: " << file_de << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + DatasetName() + " Dataset file: " + file_de + " does not exist."); - } - - std::ifstream handle_en(realpath_en.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(handle_en.is_open(), "Invalid file, failed to open en file: " + file_en); - std::ifstream handle_de(realpath_de.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(handle_de.is_open(), "Invalid file, failed to open de file: " + file_de); - - // Set path for path in class TensorRow. - std::string line_en; - std::string line_de; - std::vector path = {file_en, file_de}; - - int row_total = 0; - while (getline(handle_en, line_en) && getline(handle_de, line_de)) { - if (line_en.empty() && line_de.empty()) { - continue; - } - // If read to the end offset of this file, break. - if (row_total >= end_offset) { - break; - } - // Skip line before start offset. - if (row_total < start_offset) { - ++row_total; - continue; - } - - int tensor_size = 2; - TensorRow tRow(tensor_size, nullptr); - - Status rc_en; - Status rc_de; - if (language_pair_[0] == "en") { - rc_en = LoadTensor(line_en, &tRow, 0); - rc_de = LoadTensor(line_de, &tRow, 1); - } else if (language_pair_[0] == "de") { - rc_en = LoadTensor(line_en, &tRow, 1); - rc_de = LoadTensor(line_de, &tRow, 0); - } - if (rc_en.IsError() || rc_de.IsError()) { - handle_en.close(); - handle_de.close(); - RETURN_IF_NOT_OK(rc_en); - RETURN_IF_NOT_OK(rc_de); - } - (&tRow)->setPath(path); - - Status rc = jagged_rows_connector_->Add(worker_id, std::move(tRow)); - if (rc.IsError()) { - handle_en.close(); - handle_de.close(); - return rc; - } - ++row_total; - } - - handle_en.close(); - handle_de.close(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h deleted file mode 100644 index a3026dea6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MULTI30K_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MULTI30K_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; -using StringIndex = AutoIndexObj; - -class Multi30kOp : public TextFileOp { - public: - /// \brief Constructor of Multi30kOp - /// \note The builder class should be used to call this constructor. - /// \param[in] num_workers Number of worker threads reading data from multi30k_file files. - /// \param[in] num_samples Number of rows to read. - /// \param[in] language_pair List containing text and translation language. - /// \param[in] worker_connector_size List of filepaths for the dataset files. - /// \param[in] schema The data schema object. - /// \param[in] text_files_list File path of multi30k files. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Shards of data. - /// \param[in] device_id The device ID within num_devices. - Multi30kOp(int32_t num_workers, int64_t num_samples, const std::vector &language_pair, - int32_t worker_connector_size, std::unique_ptr schema, - const std::vector &text_files_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - /// \Default destructor. - ~Multi30kOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all); - - /// \brief Return the name of Operator. - /// \return Status - return the name of Operator. - std::string Name() const override { return "Multi30kOp"; } - - /// \brief DatasetName name getter. - /// \param[in] upper If true, the return value is uppercase, otherwise, it is lowercase. - /// \return std::string DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "Multi30k" : "multi30k"; } - - private: - /// \brief Load data into Tensor. - /// \param[in] line Data read from files. - /// \param[in] out_row Output tensor. - /// \param[in] index The index of Tensor. - Status LoadTensor(const std::string &line, TensorRow *out_row, size_t index); - - /// \brief Read data from files. - /// \param[in] file_en The paths of multi30k dataset files. - /// \param[in] start_offset The location of reading start. - /// \param[in] end_offset The location of reading finished. - /// \param[in] worker_id The id of the worker that is executing this function. - Status LoadFile(const std::string &file_en, int64_t start_offset, int64_t end_offset, int32_t worker_id); - - std::vector language_pair_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_MULTI30K_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.cc deleted file mode 100644 index a58e4ca0c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.cc +++ /dev/null @@ -1,420 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -NonMappableLeafOp::NonMappableLeafOp(int32_t num_workers, int32_t worker_connector_size, int64_t total_num_rows, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id, const CompressionType &compression_type) - : ParallelOp(num_workers, op_connector_size), - device_id_(device_id), - num_devices_(num_devices), - load_jagged_connector_(true), - filename_index_(std::make_unique()), - finished_reading_dataset_(false), - total_rows_(total_num_rows), - load_io_block_queue_(true), - shuffle_files_(shuffle_files), - num_rows_per_shard_(0), - compression_type_(compression_type), - num_rows_(0), - shuffled_keys_({}), - prepared_data_{false}, - curr_row_{0}, - workers_done_{0}, - seed_(0) { - worker_connector_size_ = worker_connector_size; -} - -// Class functor operator () override. -// All dataset operators operate by launching a thread (see ExecutionTree). This class functor will -// provide the master loop that drives the logic for performing the work -Status NonMappableLeafOp::operator()() { - RETURN_IF_NOT_OK(PrepareData()); - while (!finished_reading_dataset_) { - int32_t workers_done = 0; - int64_t rows_read = 0; - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = true; - } - - while (workers_done < num_workers_) { - TensorRow fetched_row; - RETURN_IF_NOT_OK(jagged_rows_connector_->Pop(0, &fetched_row)); - if (fetched_row.eoe()) { - workers_done++; - } else if ((compression_type_ == CompressionType::NONE || compression_type_ == CompressionType::GZIP_WITH_COUNT || - compression_type_ == CompressionType::ZLIB_WITH_COUNT) && - (total_rows_ == 0 || rows_read < total_rows_)) { - // we need to push a row - RETURN_IF_NOT_OK(out_connector_->Add(std::move(fetched_row))); - rows_read++; - } else if ((compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::ZLIB) && - (rows_read < total_rows_ * num_devices_)) { - // for compressed version, total_rows_ is total rows that will be read per shard - // we need to push a row - RETURN_IF_NOT_OK(out_connector_->Add(std::move(fetched_row))); - rows_read++; - } else { - // IOBlockQueue thread needs to: - // -stop pushing stuff to IOBlockQueue - // -call PostEndOfEpoch (will send EOE) - // -wait for reset - // - // Worker threads need to: - // -stop reading the file they are currently reading and throw it away - // -keep pulling, but dont read other files (eventually skips all IOBlocks and will get EOE) - // - // Master thread needs to: - // -tell IOBlockQueue thread to stop pushing - // -tell worker threads to stop reading the file they are currently reading - // -keep pulling until EOE - - // don't think we need a lock for now - { - std::unique_lock lock(load_jagged_connector_mutex_); - load_jagged_connector_ = false; - } - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = false; - } - } - } - - // all workers finished reading for this epoch, and we have read all the data from all workers - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - - RETURN_IF_NOT_OK(ResetAndUpdateRepeat()); - } - - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - - RETURN_IF_NOT_OK(PostEndOfData()); - - return Status::OK(); -} - -// The entry point for when workers are launched. -Status NonMappableLeafOp::WorkerEntry(int32_t worker_id) { - // must be called first if called by worker spawned by taskgroup - TaskManager::FindMe()->Post(); - - std::unique_ptr io_block; - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(PopIoBlockQueue(worker_id, &io_block)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - start_time = GetSyscnt(); - - while (!io_block->eof()) { - if (!io_block->eoe()) { - if (GetLoadJaggedConnector()) { - std::string filename; - RETURN_IF_NOT_OK(io_block->GetFilename(&filename, *filename_index_)); - int64_t start_offset = io_block->GetStartOffset(); - int64_t end_offset = io_block->GetEndOffset(); - RETURN_IF_NOT_OK(LoadFile(filename, start_offset, end_offset, worker_id)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - MS_LOG(DEBUG) << Name() << " operator worker " << worker_id << " loaded file " << filename << "."; - } - } else { - TensorRow eoe = TensorRow(TensorRow::kFlagEOE); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - RETURN_IF_NOT_OK(jagged_rows_connector_->Add(worker_id, std::move(eoe))); - } - start_time = GetSyscnt(); - RETURN_IF_NOT_OK(PopIoBlockQueue(worker_id, &io_block)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerGet", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - start_time = GetSyscnt(); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "WorkerProcess", start_time, {{"TensorRowFlags", io_block->FlagName()}})); - return Status::OK(); -} - -// Pushes a control indicator onto the IOBlockQueue for each worker to consume. -// When the worker pops this control indicator, it will shut itself down gracefully. -Status NonMappableLeafOp::PostEndOfData() { - for (int i = 0; i < num_workers_; ++i) { - std::unique_ptr eof = std::make_unique(IOBlock::kFlagEOF); - RETURN_IF_NOT_OK(PushIoBlockQueue(i, std::move(eof))); - } - - return Status::OK(); -} - -// Pushes a control indicator onto the IOBlockQueue for each worker to consume. When the worker -// pops this control indicator, it will wait until the next epoch starts and then resume execution. -Status NonMappableLeafOp::PostEndOfEpoch(int32_t queue_index) { - for (int i = 0; i < num_workers_; ++i) { - std::unique_ptr eoe = std::make_unique(IOBlock::kFlagEOE); - RETURN_IF_NOT_OK(PushIoBlockQueue((queue_index + i) % num_workers_, std::move(eoe))); - } - - return Status::OK(); -} - -// Notifies the thread which called WaitToFillIOBlockQueue to resume execution. -void NonMappableLeafOp::NotifyToFillIOBlockQueue() { io_block_queue_wait_post_.Set(); } - -// Pops an element from a queue in io_block_queues -Status NonMappableLeafOp::PopIoBlockQueue(int32_t index, std::unique_ptr *out_block) { - RETURN_IF_NOT_OK(io_block_queues_[index]->PopFront(out_block)); - return Status::OK(); -} - -// Pushes an element to a queue in io_block_queues -Status NonMappableLeafOp::PushIoBlockQueue(int32_t index, std::unique_ptr &&io_block) { - RETURN_IF_NOT_OK(io_block_queues_[index]->Add(std::move(io_block))); - return Status::OK(); -} - -// Overrides base class reset method. Cleans up any state info from it's previous execution and -// reinitializes itself so that it can be executed again, as if it was just created. -Status NonMappableLeafOp::Reset() { - MS_LOG(DEBUG) << Name() << " performing a self-reset."; - curr_row_ = 0; - workers_done_ = 0; - // start workers first, otherwise IOBlocks will fall through if workers see it before this is set to true - { - std::unique_lock lock(load_jagged_connector_mutex_); - load_jagged_connector_ = true; - } - - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = true; - } - - NotifyToFillIOBlockQueue(); - - return Status::OK(); -} - -bool NonMappableLeafOp::NeedPushFileToBlockQueue(const std::string &file_name, int64_t *start_offset, - int64_t *end_offset, const int64_t &pre_count) { - *start_offset = 0; - *end_offset = 0; - bool push = false; - int64_t start_index = device_id_ * num_rows_per_shard_; - if (device_id_ + 1 < 0) { - MS_LOG(ERROR) << "Invalid device id, device id should be greater than or equal 0, but got " - << std::to_string(device_id_); - return false; - } - - int64_t end_index = (static_cast(device_id_) + 1) * num_rows_per_shard_; - if (pre_count <= start_index && pre_count + filename_numrows_[file_name] > start_index) { - *start_offset = start_index - pre_count; - push = true; - if (pre_count < end_index && pre_count + filename_numrows_[file_name] >= end_index) { - *end_offset = end_index - pre_count; - } else { - *end_offset = filename_numrows_[file_name]; - } - } - - if (pre_count >= start_index && pre_count < end_index) { - *start_offset = 0; - push = true; - if (pre_count + filename_numrows_[file_name] >= end_index) { - *end_offset = end_index - pre_count; - } else { - *end_offset = filename_numrows_[file_name]; - } - } - - return push; -} - -void NonMappableLeafOp::ShuffleKeys() { - std::mt19937 rng(num_devices_ == 1 ? GetSeed() : ++seed_); - std::shuffle(shuffled_keys_.begin(), shuffled_keys_.end(), rng); -} - -Status NonMappableLeafOp::WaitToFillIOBlockQueue() { - // must be called first if called by worker spanwed by taskgroup - TaskManager::FindMe()->Post(); - - while (true) { - RETURN_IF_NOT_OK(io_block_queue_wait_post_.Wait()); - io_block_queue_wait_post_.Clear(); - - if (finished_reading_dataset_) { - break; - } - - if (shuffle_files_) { - ShuffleKeys(); - } - RETURN_IF_NOT_OK(FillIOBlockQueue(shuffled_keys_)); - } - return Status::OK(); -} - -Status NonMappableLeafOp::PrepareOperatorImplementation() { - if (shuffle_files_) { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - shuffled_keys_.push_back(it.key()); - } - // Please note that this code is added for future use. Resetting dataset is only used in sink mode and pull mode - // doesn't support sink mode. - if (GlobalContext::config_manager()->fast_recovery() && op_current_repeats_ > 0) { - // in reset mode, shuffled_keys needs to be ordered in the resetting epoch - for (auto i = 0; i < op_current_repeats_; i++) { - ShuffleKeys(); - } - } - } - return Status::OK(); -} - -Status NonMappableLeafOp::PrepareOperator() { - // Run any common code from super class first before adding our own - RETURN_IF_NOT_OK(DatasetOp::PrepareOperator()); - return PrepareOperatorImplementation(); -} - -Status NonMappableLeafOp::PrepareOperatorPullBased() { - // Run any common code from super class first before adding our own - RETURN_IF_NOT_OK(DatasetOp::PrepareOperatorPullBased()); - return PrepareOperatorImplementation(); -} - -Status NonMappableLeafOp::PrepareData() { - RETURN_IF_NOT_OK(CalculateNumRowsPerShard()); - - // Put here to avoid register failed when Worker_Entry thread exits unexpected - RETURN_IF_NOT_OK(io_block_queue_wait_post_.Register(tree_->AllTasks())); - - // launch one thread, responsible for filling IOBlockQueue - RETURN_IF_NOT_OK(tree_->LaunchWorkers(1, std::bind(&NonMappableLeafOp::WaitToFillIOBlockQueue, this), - Name() + "::WaitToFillIOBlockQueue", id())); - - // launch num_workers_ worker threads, responsible for pulling from the IOBlockQueue and reading - // data from disk into TensorRows - RETURN_IF_NOT_OK(RegisterAndLaunchThreads()); - - // must be called after launching workers. workers can't be spawned after this post, - // so workers have to be kept alive until the end of the program - TaskManager::FindMe()->Post(); - - NotifyToFillIOBlockQueue(); - - return Status::OK(); -} - -Status NonMappableLeafOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - row->reset(); - - // IOBlockQueue threads keep filling IOBlockQueue, and worker threads keep pulling files from IOBlockQueue, reading - // and then pushing tensors into jagged_rows_connector queue. This Preparation process is done asynchronously. Please - // note that even when num_parallel_workers is set to 1, there still be 3 async threads alive in the source op: 1 - // main thread, 1 worker thread and 1 IOBlockQueue thread. - if (!prepared_data_) { - RETURN_IF_NOT_OK(PrepareData()); - prepared_data_ = true; - } - if (finished_reading_dataset_) { - RETURN_UNEXPECTED_IF_NULL(row); - *row = TensorRow(TensorRow::kFlagEOF); - return Status::OK(); - } - TensorRow new_row; - RETURN_IF_NOT_OK(jagged_rows_connector_->Pop(0, &new_row)); - // Pull tensor from jagged_rows_connector queue. It has 4 cases: - // 1) If eoe signal reaches and all workers have finished reading, propagate eoe to the next op and do a self-reset. - // 2) If eoe signal reaches but not all the workers finishes reading, consume eoe and pull the next non-eoe tensor - // from the jagged_rows_connector queue. - // 3) If maximum count of rows to be read doesn't reach, returns the tensor data and increments curr_row_. - // 4) If maximum count of rows to be read reaches, notify IOBlockQueue thread and worker thread by setting - // load_jagged_connector_ and load_io_block_queue_ to false. Then, drain data from jagged_rows_connector queue - // until eoe is hit so that no data remains in all queues and they can be reset properly for the new iteration. - while (new_row.eoe()) { - workers_done_++; - if (static_cast(workers_done_) == num_workers_) { - RETURN_IF_NOT_OK(ResetAndUpdateRepeat()); - RETURN_UNEXPECTED_IF_NULL(row); - *row = TensorRow(TensorRow::kFlagEOE); - return Status::OK(); - } else { - RETURN_IF_NOT_OK(jagged_rows_connector_->Pop(0, &new_row)); - } - } - - if (((compression_type_ == CompressionType::NONE || compression_type_ == CompressionType::GZIP_WITH_COUNT || - compression_type_ == CompressionType::ZLIB_WITH_COUNT) && - (total_rows_ == 0 || curr_row_ < total_rows_)) || - ((compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::ZLIB) && - (curr_row_ < total_rows_ * num_devices_))) { - curr_row_++; - } else { - { - std::unique_lock lock(load_jagged_connector_mutex_); - load_jagged_connector_ = false; - } - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = false; - } - // drain data in jagged_rows_connector_ until eoe is hit. - while (static_cast(workers_done_) < num_workers_) { - TensorRow next_row; - jagged_rows_connector_->Pop(0, &next_row); - if (next_row.eoe()) { - workers_done_++; - } - } - RETURN_IF_NOT_OK(ResetAndUpdateRepeat()); - new_row = TensorRow(TensorRow::kFlagEOE); - } - RETURN_UNEXPECTED_IF_NULL(row); - *row = std::move(new_row); - return Status::OK(); -} - -Status NonMappableLeafOp::ResetAndUpdateRepeat() { - if (IsLastIteration()) { - finished_reading_dataset_ = true; - NotifyToFillIOBlockQueue(); - } else { - jagged_rows_connector_->DoReset(); - // Self-reset to start a new iteration - RETURN_IF_NOT_OK(Reset()); - } - UpdateRepeatAndEpochCounter(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h deleted file mode 100644 index 5044b4cdd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_NONMAPPABLE_LEAF_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_NONMAPPABLE_LEAF_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/util/auto_index.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" - -namespace mindspore { -namespace dataset { -template -class Queue; - -template -class Connector; - -class JaggedConnector; -class FilenameBlock; - -using StringIndex = AutoIndexObj; - -class NonMappableLeafOp : public ParallelOp { - public: - // NONE: No compression_type is used - // GZIP: GZIP compression_type with num_samples provided - // ZLIB: ZLIB compression_type with num_samples provided - // GZIP_WITH_COUNT: GZIP compression_type with num_samples not provided - // ZLIB_WITH_COUNT: ZLIB compression_type with num_samples not provided - enum class CompressionType { NONE = 0, GZIP = 1, ZLIB = 2, GZIP_WITH_COUNT = 3, ZLIB_WITH_COUNT = 4 }; - - // Constructor of TFReaderOp (2) - // @note The builder class should be used to call this constructor. - // @param num_workers - number of worker threads reading data from tf_file files. - // @param worker_connector_size - size of each internal queue. - // @param total_num_rows - Number of rows to read - // @param dataset_files_list - list of filepaths for the dataset files. - // @param op_connector_size - size of each queue in the connector that the child operator pulls from. - // @param columns_to_load - the names of the columns to load data from. - // @param shuffle_files - whether or not to shuffle the files before reading data. - // @param equal_rows_per_shard - whether or not to get equal rows for each process. - // @param compression_type - the compression type of the tf_file files - NonMappableLeafOp(int32_t num_workers, int32_t worker_connector_size, int64_t total_num_rows, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id, - const CompressionType &compression_type = CompressionType::NONE); - - // Default destructor - ~NonMappableLeafOp() override = default; - - // Instantiates the internal queues and connectors. - // @return Status - the error code returned. - virtual Status Init() = 0; - - // Class functor operator () override. - // All dataset operators operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status - the error code returned. - Status operator()() override; - - // Overrides base class reset method. Cleans up any state info from it's previous execution and - // reinitializes itself so that it can be executed again, as if it was just created. - // @return Status - the error code returned. - Status Reset() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "NonMappableLeafOp"; } - - // \Common implementation for PrepareOperators and PrepareOperatorPullBased - // @return Status The status code returned - Status PrepareOperatorImplementation(); - - // \brief During tree prepare phase, operators may have specific post-operations to perform depending on - // their role. - // \notes Derived versions of this function should always call their superclass version first - // before providing their own implementations. - // @return Status The status code returned - Status PrepareOperator() override; - - // \brief During tree prepare phase, operators may have specific post-operations to perform depending on - // their role. This is the implementation for pull mode. - // \notes Derived versions of this function should always call its superclass version first - // before providing their own implementations. - // \return Status The status code returned - Status PrepareOperatorPullBased() override; - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - // The entry point for when workers are launched. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status WorkerEntry(int32_t worker_id) override; - - // Pushes a control indicator onto the IOBlockQueue for each worker to consume. - // When the worker pops this control indicator, it will shut itself down gracefully. - // @return Status - the error code returned. - Status PostEndOfData(); - - // Pushes a control indicator onto the IOBlockQueue for each worker to consume. When the worker - // pops this control indicator, it will wait until the next epoch starts and then resume execution. - // @return Status - the error code returned. - Status PostEndOfEpoch(int32_t queue_index); - - // Called asynchronously by another thread. Will wait until notified to fill the IOBlockQueue. - // @return Status - the error code returned. - Status WaitToFillIOBlockQueue(); - - // Notifies the thread which called WaitToFillIOBlockQueue to resume execution. - void NotifyToFillIOBlockQueue(); - - // Pops an element from a queue in IOBlockQueue. - // @param index - the index of the queue to pop from. - // @param out_block - the popped element. - // @return Status - the error code returned. - Status PopIoBlockQueue(int32_t index, std::unique_ptr *out_block); - - // Pushes an element to a queue in IOBlockQueue. - // @param index - the index of the queue to push to. - // @param io_block - the element to push onto the queue. - // @return Status - the error code returned. - Status PushIoBlockQueue(int32_t index, std::unique_ptr &&io_block); - - // Reads a tf_file file and loads the data into multiple TensorRows. - // @param filename - the tf_file file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - virtual Status LoadFile(const std::string &filename, int64_t start_offset, int64_t end_offset, int32_t worker_id) = 0; - - // Select file and push it to the block queue. - // @param file_name - File name. - // @param start_file - If file contains the first sample of data. - // @param end_file - If file contains the end sample of data. - // @param pre_count - Total rows of previous files. - // @return Status - the error code returned. - bool NeedPushFileToBlockQueue(const std::string &file_name, int64_t *start_offset, int64_t *end_offset, - const int64_t &pre_count); - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - virtual Status CalculateNumRowsPerShard() = 0; - - void ShuffleKeys(); - - // Fill the IOBlockQueue. - // @para i_keys - keys of file to fill to the IOBlockQueue - // @return Status - the error code returned. - virtual Status FillIOBlockQueue(const std::vector &i_keys) = 0; - - virtual bool GetLoadIoBlockQueue() { - bool ret_load_io_block_queue = false; - { - std::unique_lock lock(load_io_block_queue_mutex_); - ret_load_io_block_queue = load_io_block_queue_; - } - return ret_load_io_block_queue; - } - - virtual bool GetLoadJaggedConnector() { - bool ret_load_jagged_connector = false; - { - std::unique_lock lock(load_jagged_connector_mutex_); - ret_load_jagged_connector = load_jagged_connector_; - } - return ret_load_jagged_connector; - } - - /// \brief Prepare data by reading from disk and caching tensors into the jagged_row_connector queue. - /// \return Status The status code returned - Status PrepareData(); - - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - /// \brief reset the op and update repeat and epoch number if the condition is met. - /// \return Status The status code returned - Status ResetAndUpdateRepeat(); - - int32_t device_id_; - int32_t num_devices_; - bool load_jagged_connector_; - std::mutex load_jagged_connector_mutex_; - std::unique_ptr filename_index_; - - QueueList> io_block_queues_; - std::map filename_numrows_; - bool finished_reading_dataset_; - // Note: If compression_type_ is not empty, then total_rows_ is the total rows that will be read per shard - int64_t total_rows_; - CompressionType compression_type_; - - WaitPost io_block_queue_wait_post_; - bool load_io_block_queue_; - std::mutex load_io_block_queue_mutex_; - std::unique_ptr jagged_rows_connector_; - bool shuffle_files_; - int64_t num_rows_per_shard_; - int64_t num_rows_; - bool prepared_data_; // flag to indicate whether the data is prepared before taking for pull mode - uint32_t curr_row_; // current row number count for pull mode - uint32_t workers_done_; // how many workers have done the tensors reading work for pull mode - - private: - std::vector shuffled_keys_; // to store shuffled filename indices - uint32_t seed_; // used to shuffle filename indices -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_NONMAPPABLE_LEAF_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.cc deleted file mode 100644 index 0862faac2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.cc +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -OmniglotOp::OmniglotOp(int32_t num_wkrs, const std::string &file_dir, int32_t queue_size, bool background, - bool do_decode, std::unique_ptr data_schema, - const std::shared_ptr &sampler) - : ImageFolderOp(num_wkrs, file_dir, queue_size, false, do_decode, {}, {}, std::move(data_schema), - std::move(sampler)) { - Path dir(file_dir); - if (background) { - folder_path_ = (dir / "images_background").ToString(); - } else { - folder_path_ = (dir / "images_evaluation").ToString(); - } -} - -void OmniglotOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nOmniglot directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// This helper function walks all folder_paths, and send each foldername to folder_name_queue_. -Status OmniglotOp::RecursiveWalkFolder(Path *dir) { - RETURN_UNEXPECTED_IF_NULL(dir); - std::queue folder_paths; - return WalkDir(dir, &folder_paths, folder_name_queue_.get(), dirname_offset_, false); -} - -Status OmniglotOp::WalkDir(Path *dir, std::queue *folder_paths, Queue *folder_name_queue, - uint64_t dirname_offset, bool std_queue) { - RETURN_UNEXPECTED_IF_NULL(dir); - RETURN_UNEXPECTED_IF_NULL(folder_paths); - RETURN_UNEXPECTED_IF_NULL(folder_name_queue); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(dir); - CHECK_FAIL_RETURN_UNEXPECTED(dir_itr != nullptr, "Invalid path, failed to open omniglot image dir: " + - (*dir).ToString() + ", permission denied."); - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - std::shared_ptr dir_itr_sec = Path::DirIterator::OpenDirectory(&subdir); - CHECK_FAIL_RETURN_UNEXPECTED(dir_itr_sec != nullptr, "Invalid path, failed to open omniglot image dir: " + - subdir.ToString() + ", permission denied."); - while (dir_itr_sec->HasNext()) { - Path subsubdir = dir_itr_sec->Next(); - if (subsubdir.IsDirectory()) { - if (std_queue) { - folder_paths->push(subsubdir.ToString()); - } else { - RETURN_IF_NOT_OK(folder_name_queue->EmplaceBack(subsubdir.ToString().substr(dirname_offset))); - } - } - } - } - } - return Status::OK(); -} - -Status OmniglotOp::CountRowsAndClasses(const std::string &path, int64_t *num_rows, int64_t *num_classes) { - Path dir(path); - CHECK_FAIL_RETURN_UNEXPECTED(dir.Exists() && dir.IsDirectory(), - "Invalid parameter, input path is invalid or not set, path: " + path); - CHECK_FAIL_RETURN_UNEXPECTED(num_classes != nullptr || num_rows != nullptr, - "[Internal ERROR] num_class and num_rows are null."); - int64_t row_cnt = 0; - std::queue folder_paths; - Queue tmp_queue = Queue(1); - RETURN_IF_NOT_OK(WalkDir(&dir, &folder_paths, &tmp_queue, 0, true)); - if (num_classes != nullptr) { - *num_classes = folder_paths.size(); - } - RETURN_OK_IF_TRUE(num_rows == nullptr); - while (!folder_paths.empty()) { - Path subdir(folder_paths.front()); - auto dir_itr = Path::DirIterator::OpenDirectory(&subdir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - while (dir_itr->HasNext()) { - ++row_cnt; - } - folder_paths.pop(); - } - (*num_rows) = row_cnt; - return Status::OK(); -} - -// Get number of classes -Status OmniglotOp::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (num_classes_ > 0) { - *num_classes = num_classes_; - return Status::OK(); - } - RETURN_IF_NOT_OK(CountRowsAndClasses(folder_path_, nullptr, num_classes)); - num_classes_ = *num_classes; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h deleted file mode 100644 index 8d48efe66..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_OMNIGLOT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_OMNIGLOT_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h" - -namespace mindspore { -namespace dataset { -// Forward declares. -template -class Queue; - -using ImageLabelPair = std::shared_ptr>; -using FolderImagesPair = std::shared_ptr>>; - -class OmniglotOp : public ImageFolderOp { - public: - /// Constructor - /// @param num_wkrs - Num of workers reading images in parallel. - /// @param file_dir - Directory of ImageNetFolder. - /// @param queue_size - Connector queue size. - /// @param background - Use the background dataset or the evaluation dataset. - /// @param do_decode - Decode the images after reading. - /// @param data_schema - Schema of Omniglot dataset. - /// @param sampler - Sampler tells OmniglotOp what to read. - OmniglotOp(int32_t num_wkrs, const std::string &file_dir, int32_t queue_size, bool background, bool do_decode, - std::unique_ptr data_schema, const std::shared_ptr &sampler); - - /// Destructor. - ~OmniglotOp() = default; - - /// A print method typically used for debugging. - /// @param out - The output stream to write output to. - /// @param show_all - A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// This is the common function to walk one directory. - /// @param dir - The directory path - /// @param folder_path - The queue in CountRowsAndClasses function. - /// @param folder_name_queue - The queue in base class. - /// @param dirname_offset - The offset of path of directory using in RecursiveWalkFolder function. - /// @param std_queue - A bool to use folder_path or the foler_name_queue. - /// @return Status - The error code returned. - static Status WalkDir(Path *dir, std::queue *folder_paths, Queue *folder_name_queue, - uint64_t dirname_offset, bool std_queue); - - /// This function is a hack! It is to return the num_class and num_rows. The result - /// returned by this function may not be consistent with what omniglot_op is going to return - /// use this at your own risk! - /// @param path - The folder path - /// @param num_rows - The point to the number of rows - /// @param num_classes - The point to the number of classes - /// @return Status - the error code returned. - static Status CountRowsAndClasses(const std::string &path, int64_t *num_rows, int64_t *num_classes); - - /// Op name getter - /// @return std::string - Name of the current Op. - std::string Name() const override { return "OmniglotOp"; } - - /// DatasetName name getter - /// @param upper - A bool to control if you want to return uppercase or lowercase Op name. - /// @return std::string - DatasetName of the current Op - std::string DatasetName(bool upper = false) const { return upper ? "Omniglot" : "omniglot"; } - - /// Base-class override for GetNumClasses. - /// @param num_classes - the number of classes. - /// @return Status - the error code returned. - Status GetNumClasses(int64_t *num_classes) override; - - private: - // Walk the folder - /// @param dir - The folder path - /// @return Status - the error code returned. - Status RecursiveWalkFolder(Path *dir) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_OMNIGLOT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.cc deleted file mode 100644 index 33fe7cc38..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.cc +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h" - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -PennTreebankOp::PennTreebankOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr schema, const std::vector &file_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, total_rows, worker_connector_size, std::move(schema), file_list, op_connector_size, - shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void PennTreebankOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nPennTreebank files list:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h deleted file mode 100644 index eb46377dd..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PENN_TREEBANK_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PENN_TREEBANK_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -class PennTreebankOp : public TextFileOp { - public: - /// \brief Constructor. - /// \param[in] num_workers Number of workers reading images in parallel - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] file_list List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. This argument should be - /// specified only when num_devices is also specified. - PennTreebankOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, std::unique_ptr, - const std::vector &file_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - /// \brief Default destructor. - ~PennTreebankOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out he output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "PennTreebankOp"; } - - /// \brief DatasetName name getter. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "PennTreebank" : "penn treebank"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PENN_TREEBANK_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.cc deleted file mode 100644 index 15d5acf6d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.cc +++ /dev/null @@ -1,421 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore/ccsrc/include/utils/common.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr uint32_t kPatchNumPerRow = 16; -constexpr uint32_t kPatchNumPerCol = 16; -constexpr uint32_t kColPerPatch = 64; -constexpr uint32_t kRowPerPatch = 64; - -const std::map kLens = { - {"notredame", 468159}, {"yosemite", 633587}, {"liberty", 450092}, - {"liberty_harris", 379587}, {"yosemite_harris", 450912}, {"notredame_harris", 325295}, -}; -constexpr char kImageExt[] = "bmp"; -constexpr char kInfoFile[] = "info.txt"; -constexpr char kMatchesFiles[] = "m50_100000_100000_0.txt"; -const std::map kTrain = {{"train", true}, {"test", false}}; - -PhotoTourOp::PhotoTourOp(const std::string &dataset_dir, const std::string &name, const std::string &usage, - int32_t num_workers, int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - name_(name), - usage_(usage), - buf_cnt_(0), - data_schema_(std::move(data_schema)), - train_(true), - image_names_({}), - image_bmps_({}), - matches_({}), - labels_({}) {} - -Status PhotoTourOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - if (train_) { - std::shared_ptr image; - // make a copy of cached tensor - RETURN_IF_NOT_OK(GetPhotoTourDataTensor(row_id, &image)); - (*trow) = TensorRow(row_id, {std::move(image)}); - trow->setPath({image_names_[row_id / (kPatchNumPerRow * kPatchNumPerCol)], - std::to_string(row_id % (kPatchNumPerRow * kPatchNumPerCol))}); - } else { - std::shared_ptr image1, image2, matches; - // make a copy of cached tensor - uint32_t row1 = std::get<0>(matches_[row_id]); - uint32_t row2 = std::get<1>(matches_[row_id]); - - RETURN_IF_NOT_OK(GetPhotoTourDataTensor(row1, &image1)); - RETURN_IF_NOT_OK(GetPhotoTourDataTensor(row2, &image2)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(std::get<2>(matches_[row_id]), &matches)); - (*trow) = TensorRow(row_id, {std::move(image1), std::move(image2), std::move(matches)}); - trow->setPath({image_names_[row1 / (kPatchNumPerRow * kPatchNumPerCol)], - std::to_string(row1 % (kPatchNumPerRow * kPatchNumPerCol)), - image_names_[row2 / (kPatchNumPerRow * kPatchNumPerCol)], - std::to_string(row2 % (kPatchNumPerRow * kPatchNumPerCol))}); - } - - return Status::OK(); -} - -void PhotoTourOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nPhotoTour directory: " << dataset_dir_ << "\nName: " << name_ - << "\nUsage: " << usage_ << "\n\n"; - } -} - -// Derived from RandomAccessOp. -Status PhotoTourOp::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || labels_.empty()) { - if (labels_.empty()) { - RETURN_STATUS_UNEXPECTED("No image found in dataset, please check if image was read successfully."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place, " - "it must be empty before using GetClassIds."); - } - } - if (train_) { - for (size_t i = 0; i < labels_.size(); ++i) { - (*cls_ids)[labels_[i]].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - } else { - for (size_t i = 0; i < matches_.size(); ++i) { - (*cls_ids)[std::get<2>(matches_[i])].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - } - - return Status::OK(); -} - -bool PhotoTourOp::EndsWith(const std::string &s, const std::string &sub) { - return s.rfind(sub) == (s.length() - sub.length()) ? true : false; -} - -Status PhotoTourOp::GetFileContent(const std::string &info_file, std::string *ans) { - RETURN_UNEXPECTED_IF_NULL(ans); - auto info_file_realpath = FileUtils::GetRealPath(info_file.c_str()); - if (!info_file_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + info_file + " does not exist."); - } - std::ifstream reader; - reader.open(info_file_realpath.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(!reader.fail(), "Invalid file, failed to open " + info_file + - ": PhotoTour info file is damaged or permission denied."); - (void)reader.seekg(0, std::ios::end); - std::size_t size = reader.tellg(); - (void)reader.seekg(0, std::ios::beg); - - CHECK_FAIL_RETURN_UNEXPECTED(size > 0, "Invalid file, the file size of " + info_file + " is unexpected, got size 0."); - std::string buffer(size, ' '); - reader.read(&buffer[0], size); - reader.close(); - - // remove \n character in the buffer - std::regex pattern("([\\s\\n]+)"); - std::string fmt = " "; - std::string s = std::regex_replace(buffer, pattern, fmt); - - // remove the head and tail whiteblanks of the s - (void)s.erase(0, s.find_first_not_of(" ")); - (void)s.erase(s.find_last_not_of(" ") + 1); - // append one whiteblanks to the end of s - s += " "; - *ans = s; - return Status::OK(); -} - -Status PhotoTourOp::ReadInfoFile(const std::string &data_dir, const std::string &info_file) { - std::vector tmp; - labels_.swap(tmp); - std::string info_file_path = (Path(data_dir) / Path(info_file)).ToString(); - std::string s; - RETURN_IF_NOT_OK(GetFileContent(info_file_path, &s)); - auto get_splited_str = [&s](std::size_t pos) { - std::string item = s.substr(0, pos); - s = s.substr(pos + 1); - return item; - }; - enum ColType { ID_3DPOINT, UNKNOWN }; - std::size_t pos = 0; - ColType col_idx = ID_3DPOINT; - while ((pos = s.find(" ")) != std::string::npos) { - switch (col_idx) { - case ID_3DPOINT: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, reading PhotoTour info file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - int id_3dpoint = std::atoi(item.c_str()); - labels_.push_back(id_3dpoint); - col_idx = UNKNOWN; - break; - } - case UNKNOWN: { - std::string item2 = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED( - !item2.empty(), "Invalid data, Reading PhotoTour info file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content in file should not be empty."); - col_idx = ID_3DPOINT; - break; - } - default: { - break; - } - } - } - - return Status::OK(); -} - -Status PhotoTourOp::ReadMatchedFile(const std::string &data_dir, const std::string &matches_file) { - std::vector tmp; - matches_.swap(tmp); - std::string info_file_path = (Path(data_dir) / Path(matches_file)).ToString(); - - std::string s; - RETURN_IF_NOT_OK(GetFileContent(info_file_path, &s)); - - auto get_splited_str = [&s](std::size_t pos) { - std::string item = s.substr(0, pos); - s = s.substr(pos + 1); - return item; - }; - enum ColType { PATCH_ID1, LABEL1, UNUSED1, PATCH_ID2, LABEL2, UNUSED2, UNUSED3 }; - uint32_t patch_id1, label1, patch_id2, label2; - std::size_t pos = 0; - ColType col_idx = PATCH_ID1; - while ((pos = s.find(" ")) != std::string::npos) { - switch (col_idx) { - case PATCH_ID1: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data,Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - patch_id1 = std::atoi(item.c_str()); - col_idx = LABEL1; - break; - } - case LABEL1: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - label1 = std::atoi(item.c_str()); - col_idx = UNUSED1; - break; - } - case UNUSED1: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - col_idx = PATCH_ID2; - break; - } - case PATCH_ID2: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - patch_id2 = std::atoi(item.c_str()); - col_idx = LABEL2; - break; - } - case LABEL2: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - label2 = std::atoi(item.c_str()); - col_idx = UNUSED2; - matches_.push_back(std::make_tuple(patch_id1, patch_id2, uint32_t(label1 == label2))); - break; - } - case UNUSED2: { - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - col_idx = UNUSED3; - break; - } - case UNUSED3: { - std::string item2 = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item2.empty(), - "Invalid data, Reading PhotoTour matched file failed: " + info_file_path + - " at line: " + std::to_string(pos) + ", the content should not be empty."); - col_idx = PATCH_ID1; - break; - } - default: { - break; - } - } - } - - return Status::OK(); -} - -Status PhotoTourOp::GetPhotoTourDataTensor(uint32_t index, std::shared_ptr *image_tensor) { - RETURN_UNEXPECTED_IF_NULL(image_tensor); - CHECK_FAIL_RETURN_UNEXPECTED( - index < kLens.at(name_), - "[Internal ERROR] Index exceeds the maximum count of image, got: " + std::to_string(index)); - - int image_id = index / (kPatchNumPerRow * kPatchNumPerCol); - int row_in_image = (index % (kPatchNumPerRow * kPatchNumPerCol)) / kPatchNumPerRow; - int col_in_image = (index % (kPatchNumPerRow * kPatchNumPerCol)) % kPatchNumPerRow; - { - std::unique_lock lock(access_mutex_); - if (image_bmps_[image_id].empty()) { - image_bmps_[image_id] = cv::imread(image_names_[image_id], 0); - } - } - - uint32_t x = col_in_image * kColPerPatch; - uint32_t y = row_in_image * kRowPerPatch; - - cv::Rect myROI(x, y, kColPerPatch, kRowPerPatch); - - // Crop the full image to that image contained by the rectangle myROI - // Note that this doesn't copy the data - cv::Mat croppedRef(image_bmps_[image_id], myROI); - cv::Mat cropped; - // Copy the data into new matrix - croppedRef.copyTo(cropped); - - uchar *uc_img = cropped.data; - TensorShape img_tensor_shape = TensorShape({kRowPerPatch, kColPerPatch, 1}); - - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(img_tensor_shape, data_schema_->Column(0).Type(), uc_img, image_tensor)); - - return Status::OK(); -} - -// Read all files in the directory. -// @return Status The status code returned. -Status PhotoTourOp::PrepareData() { - chosen_dataset_folder_path_ = (Path(dataset_dir_) / Path(name_)).ToString(); - train_ = kTrain.at(usage_); - auto real_folder_path = FileUtils::GetRealPath(chosen_dataset_folder_path_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_folder_path.has_value(), chosen_dataset_folder_path_ + " does not exist."); - - std::vector file_names; - cv::glob(real_folder_path.value(), file_names); - image_names_.clear(); - image_bmps_.clear(); - for (auto &&file_name : file_names) { - if (EndsWith(file_name, kImageExt)) { - image_names_.push_back(file_name); - } - } - std::sort(image_names_.begin(), image_names_.end()); - image_bmps_.resize(image_names_.size()); - RETURN_IF_NOT_OK(ReadInfoFile(real_folder_path.value(), kInfoFile)); - RETURN_IF_NOT_OK(ReadMatchedFile(real_folder_path.value(), kMatchesFiles)); - if (train_) { - num_rows_ = labels_.size(); - } else { - num_rows_ = matches_.size(); - } - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, data file may not suitable to read with PhotoTourDataset API." - "Check file in directory: " + - chosen_dataset_folder_path_); - } - return Status::OK(); -} - -Status PhotoTourOp::CountTotalRows(const std::string &dir, const std::string &name, const std::string &usage, - int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - if (usage == "train") { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - } else { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image1", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image2", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("matches", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - } - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(dir, name, usage, num_workers, op_connect_size, std::move(schema), - std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - - if (usage == "train") { - *count = op->labels_.size(); - } else { - *count = op->matches_.size(); - } - - return Status::OK(); -} - -Status PhotoTourOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h deleted file mode 100644 index f043a6c4a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PHOTO_TOUR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PHOTO_TOUR_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using MatchTuple = std::tuple; - -class PhotoTourOp : public MappableLeafOp { - public: - // Constructor - // @param const std::string &datasetDir - Path to the root directory that - // contains the dataset. - // @param const std::string &name - Name of the dataset to load. - // @param const std::string &usage - 'train' or 'test', If 'train', the - // generated dataset has one column ["image"], else three columns - // ["image1", "image2", "matches"]. - // @param int32_t num_workers - number of workers reading images in parallel. - // @param int32_t queue_size - connector queue size. - // @param std::unique_ptr data_schema - the schema of the photo tour dataset. - // @param std::unique_ptr sampler - sampler tells PhotoTourOp what to read. - PhotoTourOp(const std::string &dataset_dir, const std::string &name, const std::string &usage, int32_t num_workers, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler); - - // Destructor. - ~PhotoTourOp() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class. - // @param std::map> *cls_ids - key label, val all ids for this class. - // @return Status - The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging. - // @param std::ostream &out - out stream. - // @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the PhotoTour dataset. - // @param const std::string &dir - path to the PhotoTour directory. - // @param const std::string &name - name of the dataset to load. - // @param const std::string &usage - 'train' or 'test', If 'train', the - // generated dataset has one column ["image"], else three columns - // ["image1", "image2", "matches"]. - // @param int64_t *count - output arg that will hold the minimum of the actual dataset - // size and numSamples. - // @return Status - The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &name, const std::string &usage, - int64_t *count); - - // Op name getter. - // @return std::string - Name of the current Op. - std::string Name() const override { return "PhotoTourOp"; } - - private: - // Load a tensor row according to the row_id. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow *row - load one piece of data into this tensor row. - // @return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row); - - // Judge whether string s ends with string sub. - // @param const std::string &s - full string. - // @param const std::string &sub - suffix. - // @return bool The Result of whether string s ends with string sub. - bool EndsWith(const std::string &s, const std::string &sub); - - // Read the content in the given file path. - // @param const std::string &info_file - info file name. - // @param std::string *ans - store the content of the info file. - // @return Status - The status code returned. - Status GetFileContent(const std::string &info_file, std::string *ans); - - // Read the meta info for each patch. - // @param const std::string &data_dir - data_dir stores the info file. - // @param const std::string &info_file - info file name. - // @return Status - The status code returned. - Status ReadInfoFile(const std::string &data_dir, const std::string &info_file); - - // Read the matches meta info. - // @param const std::string &data_dir - data_dir stores the info file. - // @param const std::string &matches_file - matches info file name. - // @return Status - The status code returned. - Status ReadMatchedFile(const std::string &data_dir, const std::string &matches_file); - - // Get one piece of PhotoTour data. - // @param uint32_t index - index of data to read. - // @param std::shared_ptr *image_tensor - store the indexed data. - // @return Status - The status code returned. - Status GetPhotoTourDataTensor(uint32_t index, std::shared_ptr *image_tensor); - - // Read all files in the directory. - // @return Status - The status code returned. - Status PrepareData(); - - // Private function for computing the assignment of the column name map. - // @return Status - The status code returned. - Status ComputeColMap() override; - - int64_t buf_cnt_; - std::unique_ptr data_schema_; - - const std::string dataset_dir_; // directory of image folder - const std::string name_; // dataset name - const std::string usage_; // 'train' or 'test' - std::string chosen_dataset_folder_path_; // dataset_dir + name : folder - bool train_; // whether the usage_ is "train" or not - - std::vector image_names_; - std::vector image_bmps_; - - std::vector matches_; // train_ = false, stores the triplets (img1, img2, is_match) - std::vector labels_; // label of i_th patch - std::mutex access_mutex_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PHOTO_TOUR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.cc deleted file mode 100644 index 01a023e6f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.cc +++ /dev/null @@ -1,312 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h" - -#include -#include -#include -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr char kCategoriesMeta[] = "categories_places365.txt"; -const std::map kFileListMeta = {{"train-standard", "places365_train_standard.txt"}, - {"train-challenge", "places365_train_challenge.txt"}, - {"val", "places365_val.txt"}}; -const std::map, std::string> kImagesMeta = { - {std::pair("train-standard", false), "data_large_standard"}, - {std::pair("train-challenge", false), "data_large_challenge"}, - {std::pair("val", false), "val_large"}, - {std::pair("train-standard", true), "data_256_standard"}, - {std::pair("train-challenge", true), "data_256_challenge"}, - {std::pair("val", true), "val_256"}, -}; - -Places365Op::Places365Op(const std::string &root, const std::string &usage, bool small, bool decode, - int32_t num_workers, int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - root_(root), - usage_(usage), - small_(small), - decode_(decode), - buf_cnt_(0), - categorie2id_({}), - image_path_label_pairs_({}), - data_schema_(std::move(data_schema)) {} - -Status Places365Op::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr image, label; - // make a copy of cached tensor. - RETURN_IF_NOT_OK(GetPlaces365DataTensor(row_id, &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(image_path_label_pairs_[row_id].second, &label)); - - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - return Status::OK(); -} - -void Places365Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nPlaces365 directory: " << root_ << "\nUsage: " << usage_ - << "\nSmall: " << (small_ ? "yes" : "no") << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// Derived from RandomAccessOp. -Status Places365Op::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_path_label_pairs_.empty()) { - if (image_path_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("No image found in dataset. Check if image was read successfully."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < image_path_label_pairs_.size(); ++i) { - (*cls_ids)[image_path_label_pairs_[i].second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status Places365Op::GetFileContent(const std::string &info_file, std::string *ans) { - RETURN_UNEXPECTED_IF_NULL(ans); - auto info_file_realpath = FileUtils::GetRealPath(info_file.c_str()); - if (!info_file_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + info_file + " does not exist."); - } - std::ifstream reader; - reader.open(info_file_realpath.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED( - !reader.fail(), "Invalid file, failed to open " + info_file + ": Places365 file is damaged or permission denied."); - reader.seekg(0, std::ios::end); - std::size_t size = reader.tellg(); - reader.seekg(0, std::ios::beg); - - CHECK_FAIL_RETURN_UNEXPECTED(size > 0, "Invalid file, the file size of " + info_file + " is unexpected, got size 0."); - std::string buffer(size, ' '); - reader.read(&buffer[0], size); - reader.close(); - - // remove \n character in the buffer. - std::regex pattern("([\\s\\n]+)"); - std::string fmt = " "; - std::string s = std::regex_replace(buffer, pattern, fmt); - - // remove the head and tail whiteblanks of the s. - s.erase(0, s.find_first_not_of(" ")); - s.erase(s.find_last_not_of(" ") + 1); - // append one whiteblanks to the end of s. - s += " "; - *ans = s; - return Status::OK(); -} - -Status Places365Op::LoadCategories(const std::string &category_meta_name) { - categorie2id_.clear(); - std::string s; - RETURN_IF_NOT_OK(GetFileContent(category_meta_name, &s)); - auto get_splited_str = [&s, &category_meta_name](std::size_t pos) { - std::string item = s.substr(0, pos); - // If pos+1 is equal to the string length, the function returns an empty string. - s = s.substr(pos + 1); - return item; - }; - - std::string category; - uint32_t label; - // Category meta info is read into string s in the format: "category1 label1 category2 label2 category3 label3 ...". - // Use blank space delimiter to split the string and process each substring. - // Like state matching, the type of each substring needs to be switched. - enum ColType { CATEGORY, LABEL }; - std::size_t pos = 0; - ColType col_idx = CATEGORY; - while ((pos = s.find(" ")) != std::string::npos) { - switch (col_idx) { - case CATEGORY: { - CHECK_FAIL_RETURN_UNEXPECTED(pos + 1 <= s.size(), "Invalid data, Reading places365 category file failed: " + - category_meta_name + ", space characters not found."); - category = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!category.empty(), "Invalid data, Reading places365 category file failed: " + - category_meta_name + ", space characters not found."); - // switch the type of substring. - col_idx = LABEL; - break; - } - case LABEL: { - CHECK_FAIL_RETURN_UNEXPECTED(pos + 1 <= s.size(), "Invalid data, Reading places365 category file failed: " + - category_meta_name + ", space characters not found."); - std::string label_item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!label_item.empty(), "Invalid data, Reading places365 category file failed: " + - category_meta_name + ", space characters not found."); - label = std::atoi(label_item.c_str()); - // switch the type of substring. - col_idx = CATEGORY; - categorie2id_.insert({category, label}); - break; - } - default: { - break; - } - } - } - return Status::OK(); -} - -Status Places365Op::LoadFileLists(const std::string &filelists_meta_name) { - std::string folder_path = (Path(root_) / Path(kImagesMeta.at(std::make_pair(usage_, small_)))).ToString(); - image_path_label_pairs_.clear(); - - std::string s; - RETURN_IF_NOT_OK(GetFileContent(filelists_meta_name, &s)); - auto get_splited_str = [&s, &filelists_meta_name](std::size_t pos) { - std::string item = s.substr(0, pos); - s = s.substr(pos + 1); - return item; - }; - std::string path; - uint32_t label; - // Category meta info is read into string s in the format: "path1 label1 path2 label2 path2 label3 ...". - // Use blank space delimiter to split the string and process each substring. - // Like state matching, the type of each substring needs to be switched. - enum ColType { PATH, LABEL }; - std::size_t pos = 0; - ColType col_idx = PATH; - while ((pos = s.find(" ")) != std::string::npos) { - switch (col_idx) { - case PATH: { - CHECK_FAIL_RETURN_UNEXPECTED(pos + 1 <= s.size(), "Invalid data, Reading places365 category file failed: " + - filelists_meta_name + ", space characters not found."); - path = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!path.empty(), "Invalid data, Reading places365 filelist file failed: " + - filelists_meta_name + ", space characters not found."); - // switch the type of substring. - col_idx = LABEL; - break; - } - case LABEL: { - CHECK_FAIL_RETURN_UNEXPECTED(pos + 1 <= s.size(), "Invalid data, Reading places365 category file failed: " + - filelists_meta_name + ", space characters not found."); - std::string item = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!item.empty(), "Invalid data, Reading places365 filelist file failed: " + - filelists_meta_name + ", space characters not found."); - label = std::atoi(item.c_str()); - // switch the type of substring. - col_idx = PATH; - image_path_label_pairs_.push_back({(Path(folder_path) / Path(path)).ToString(), label}); - break; - } - default: { - break; - } - } - } - return Status::OK(); -} - -Status Places365Op::GetPlaces365DataTensor(uint32_t index, std::shared_ptr *image_tensor) { - std::string file_path = image_path_label_pairs_[index].first; - CHECK_FAIL_RETURN_UNEXPECTED(Path(file_path).Exists(), - "Invalid file path, Places365 image: " + file_path + " does not exists."); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(file_path, image_tensor)); - if (decode_) { - Status rc = Decode(*image_tensor, image_tensor); - if (rc.IsError()) { - *image_tensor = nullptr; - std::string err_msg = - "Invalid image, failed to decode " + file_path + ": the image is damaged or permission denied."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - return Status::OK(); -} - -Status Places365Op::PrepareData() { - auto real_folder_path = FileUtils::GetRealPath(root_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_folder_path.has_value(), "Invalid file path, " + root_ + " does not exist."); - - RETURN_IF_NOT_OK(LoadCategories((Path(real_folder_path.value()) / Path(kCategoriesMeta)).ToString())); - RETURN_IF_NOT_OK(LoadFileLists((Path(real_folder_path.value()) / Path(kFileListMeta.at(usage_))).ToString())); - num_rows_ = image_path_label_pairs_.size(); - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows_ > 0, - "Invalid data, no valid data matching the dataset API Places365Dataset. Please check dataset API or file path: " + - root_ + "."); - return Status::OK(); -} - -Status Places365Op::CountTotalRows(const std::string &dir, const std::string &usage, const bool small, - const bool decode, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(dir, usage, small, decode, num_workers, op_connect_size, std::move(schema), - std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - - for (size_t i = 0; i < op->image_path_label_pairs_.size(); ++i) { - CHECK_FAIL_RETURN_UNEXPECTED(Path(op->image_path_label_pairs_[i].first).Exists(), - "Invalid file path, " + op->image_path_label_pairs_[i].first + " does not exists."); - } - *count = op->image_path_label_pairs_.size(); - return Status::OK(); -} - -Status Places365Op::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h deleted file mode 100644 index 7dcc0b067..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PLACES365_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PLACES365_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using Places365LabelPair = std::pair, uint32_t>; - -class Places365Op : public MappableLeafOp { - public: - // Constructor. - // @param const std::string &root - Path to the root directory that contains the dataset. - // @param const std::string &usage - Usage of this dataset, can be - // 'train-standard', 'train-challenge' or 'val'. - // @param bool small - Use high resolution images or 256*256 resolution images. - // @param bool decode - Decode jpg format images. - // @param int32_t num_workers - number of workers reading images in parallel. - // @param int32_t queue_size - connector queue size. - // @param std::unique_ptr data_schema - the schema of the places365 dataset. - // @param std::unique_ptr sampler - sampler tells Places365Op what to read. - Places365Op(const std::string &root, const std::string &usage, bool small, bool decode, int32_t num_workers, - int32_t queue_size, std::unique_ptr data_schema, std::shared_ptr sampler); - - // Destructor. - ~Places365Op() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class. - // @param std::map> *cls_ids - key label, val all ids for this class. - // @return Status - The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging. - // @param std::ostream &out - out stream. - // @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the PhotoTour dataset. - // @param const std::string &dir - path to the PhotoTour directory. - // @param const std::string &usage - Usage of this dataset, can be - // 'train-standard', 'train-challenge' or 'val'. - // @param bool small - Use high resolution images or 256*256 resolution images. - // @param bool decode - Decode jpg format images. - // @param int64_t *count - output arg that will hold the minimum of the actual dataset - // size and numSamples. - // @return Status - The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, const bool small, const bool decode, - int64_t *count); - - // Op name getter. - // @return std::string - Name of the current Op. - std::string Name() const override { return "Places365Op"; } - - private: - // Load a tensor row according to the row_id. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow *row - load one piece of data into this tensor row. - // @return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row); - - // Read the content in the given file path. - // @param const std::string &info_file - info file name. - // @param std::string *ans - store the content of the info file. - // @return Status - The status code returned. - Status GetFileContent(const std::string &info_file, std::string *ans); - - // Load the meta information of categories. - // @param const std::string &category_meta_name - category file name. - // @return Status - The status code returned. - Status LoadCategories(const std::string &category_meta_name); - - // Load the meta information of file information. - // @param const std::string &filelists_meta_name - meta file name. - // @return Status - The status code returned. - Status LoadFileLists(const std::string &filelists_meta_name); - - // Get one piece of places365 data. - // @param uint32_t index Index of the data. - // @param std::shared_ptr *image_tensor - Store the result in image_tensor. - // @return Status - The status code returned. - Status GetPlaces365DataTensor(uint32_t index, std::shared_ptr *image_tensor); - - // Parse all places365 dataset files. - // @return Status The status code returned. - Status PrepareData() override; - - // Private function for computing the assignment of the column name map. - // @return Status - The status code returned. - Status ComputeColMap() override; - - int64_t buf_cnt_; - std::unique_ptr data_schema_; - - const std::string root_; // directory of image folder - const std::string usage_; // can only be "train-challenge", "train-standard" or "val" - const bool small_; - const bool decode_; - std::map categorie2id_; - std::vector> image_path_label_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_PLACES365_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.cc deleted file mode 100644 index b49566303..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.cc +++ /dev/null @@ -1,332 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h" - -#include -#include -#include -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const int32_t kQMnistLabelFileMagicNumber = 3074; -const size_t kQMnistImageRows = 28; -const size_t kQMnistImageCols = 28; -const size_t kQMnistLabelLength = 8; -const uint32_t kNum4 = 4; -const uint32_t kNum12 = 12; - -QMnistOp::QMnistOp(const std::string &folder_path, const std::string &usage, bool compat, - std::unique_ptr data_schema, std::shared_ptr sampler, int32_t num_workers, - int32_t queue_size) - : MnistOp(usage, num_workers, folder_path, queue_size, std::move(data_schema), std::move(sampler)), - compat_(compat) {} - -void QMnistOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\n" - << DatasetName(true) << " directory: " << folder_path_ << "\nUsage: " << usage_ - << "\nCompat: " << (compat_ ? "yes" : "no") << "\n\n"; - } -} - -// Load 1 TensorRow (image, label) using 1 MnistLabelPair or QMnistImageInfoPair. -Status QMnistOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - size_t unsigned_row_id = static_cast(row_id); - std::shared_ptr image, label; - if (compat_) { - MnistLabelPair qmnist_pair = image_label_pairs_[unsigned_row_id]; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(qmnist_pair.first, &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(qmnist_pair.second, &label)); - } else { - QMnistImageInfoPair qmnist_pair = image_info_pairs_[unsigned_row_id]; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(qmnist_pair.first, &image)); - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(qmnist_pair.second, &label)); - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({image_path_[unsigned_row_id], label_path_[unsigned_row_id]}); - return Status::OK(); -} - -Status QMnistOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connector_size = cfg->op_connector_size(); - - // compat does not affect the count result, so set it to true default. - auto op = - std::make_shared(dir, usage, true, std::move(schema), std::move(sampler), num_workers, op_connector_size); - - // the logic of counting the number of samples - RETURN_IF_NOT_OK(op->WalkAllFiles()); - for (size_t i = 0; i < op->image_names_.size(); ++i) { - auto image_realpath = FileUtils::GetRealPath(op->image_names_[i].c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + op->image_names_[i] + " does not exist."); - } - auto label_realpath = FileUtils::GetRealPath(op->label_names_[i].c_str()); - if (!label_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + op->label_names_[i] + " does not exist."); - } - std::ifstream image_reader; - image_reader.open(image_realpath.value(), std::ios::in | std::ios::binary); - std::ifstream label_reader; - label_reader.open(label_realpath.value(), std::ios::in | std::ios::binary); - - uint32_t num_images; - auto s = op->CheckImage(op->image_names_[i], &image_reader, &num_images); - if (s != Status::OK()) { - image_reader.close(); - label_reader.close(); - return s; - } - uint32_t num_labels; - s = op->CheckLabel(op->label_names_[i], &label_reader, &num_labels); - if (s != Status::OK()) { - image_reader.close(); - label_reader.close(); - return s; - } - if (num_images != num_labels) { - image_reader.close(); - label_reader.close(); - RETURN_STATUS_UNEXPECTED("Invalid data, num of images should be equal to num of labels loading from " + dir + - ", but got num of images: " + std::to_string(num_images) + - ", num of labels: " + std::to_string(num_labels) + "."); - } - - if (usage == "test10k") { - // only use the first 10k samples and drop the last 50k samples - uint32_t first_10k = 10000; - num_images = first_10k; - num_labels = first_10k; - } else if (usage == "test50k") { - // only use the last 50k samples and drop the first 10k samples - uint32_t last_50k = 50000; - num_images = last_50k; - num_labels = last_50k; - } - - *count = *count + num_images; - - // Close the readers - image_reader.close(); - label_reader.close(); - } - - return Status::OK(); -} - -Status QMnistOp::WalkAllFiles() { - const std::string image_ext = "images-idx3-ubyte"; - const std::string label_ext = "labels-idx2-int"; - const std::string train_prefix = "qmnist-train"; - const std::string test_prefix = "qmnist-test"; - const std::string nist_prefix = "xnist"; - - auto real_folder_path = FileUtils::GetRealPath(folder_path_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_folder_path.has_value(), - "Invalid QMnist folder, " + folder_path_ + " does not exist or permission denied!"); - Path root_dir(real_folder_path.value()); - - if (usage_ == "train") { - image_names_.push_back((root_dir / Path(train_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(train_prefix + "-" + label_ext)).ToString()); - } else if (usage_ == "test" || usage_ == "test10k" || usage_ == "test50k") { - image_names_.push_back((root_dir / Path(test_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(test_prefix + "-" + label_ext)).ToString()); - } else if (usage_ == "nist") { - image_names_.push_back((root_dir / Path(nist_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(nist_prefix + "-" + label_ext)).ToString()); - } else if (usage_ == "all") { - image_names_.push_back((root_dir / Path(train_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(train_prefix + "-" + label_ext)).ToString()); - image_names_.push_back((root_dir / Path(test_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(test_prefix + "-" + label_ext)).ToString()); - image_names_.push_back((root_dir / Path(nist_prefix + "-" + image_ext)).ToString()); - label_names_.push_back((root_dir / Path(nist_prefix + "-" + label_ext)).ToString()); - } else { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, usage should be \"train\", \"test\", \"test10k\", \"test50k\", " - "\"nist\" or \"all\", got " + - usage_); - } - - CHECK_FAIL_RETURN_UNEXPECTED( - image_names_.size() == label_names_.size(), - "Invalid data, num of Qmnist image files should be equal to num of Qmnist label files under directory:" + - folder_path_ + ", but got num of image files: " + std::to_string(image_names_.size()) + - ", num of label files: " + std::to_string(label_names_.size()) + "."); - - for (size_t i = 0; i < image_names_.size(); i++) { - Path file_path(image_names_[i]); - CHECK_FAIL_RETURN_UNEXPECTED( - file_path.Exists() && !file_path.IsDirectory(), - "Invalid file path, Qmnist data file: " + file_path.ToString() + " does not exist or is a directory."); - MS_LOG(INFO) << DatasetName(true) << " operator found image file at " << file_path.ToString() << "."; - } - - for (size_t i = 0; i < label_names_.size(); i++) { - Path file_path(label_names_[i]); - CHECK_FAIL_RETURN_UNEXPECTED( - file_path.Exists() && !file_path.IsDirectory(), - "Invalid file path, Qmnist data file: " + file_path.ToString() + " does not exist or is a directory."); - MS_LOG(INFO) << DatasetName(true) << " operator found label file at " << file_path.ToString() << "."; - } - - return Status::OK(); -} - -Status QMnistOp::ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index) { - RETURN_UNEXPECTED_IF_NULL(image_reader); - RETURN_UNEXPECTED_IF_NULL(label_reader); - uint32_t num_images, num_labels; - RETURN_IF_NOT_OK(CheckImage(image_names_[index], image_reader, &num_images)); - RETURN_IF_NOT_OK(CheckLabel(label_names_[index], label_reader, &num_labels)); - CHECK_FAIL_RETURN_UNEXPECTED((num_images == num_labels), - "Invalid data, num of images should be equal to num of labels loading from " + - folder_path_ + ", but got num of images: " + std::to_string(num_images) + - ", num of labels: " + std::to_string(num_labels) + "."); - - // The image size of the QMNIST dataset is fixed at [28,28] - size_t image_size = kQMnistImageRows * kQMnistImageCols; - size_t label_length = kQMnistLabelLength; - - uint32_t first_10k = 10000; - if (usage_ == "test10k") { - // only use the first 10k samples and drop the last 50k samples - num_images = first_10k; - num_labels = first_10k; - } else if (usage_ == "test50k") { - uint32_t last_50k = 50000; - int num_bytes_for_unint32_t = 4; - num_images = last_50k; - num_labels = last_50k; - // skip the first 10k samples for ifstream reader - (void)image_reader->ignore(image_size * first_10k); - (void)label_reader->ignore(label_length * first_10k * num_bytes_for_unint32_t); - } - - auto images_buf = std::make_unique(image_size * num_images); - auto labels_buf = std::make_unique(label_length * num_labels); - if (images_buf == nullptr || labels_buf == nullptr) { - std::string err_msg = "[Internal ERROR] Failed to allocate memory for " + DatasetName() + " buffer."; - MS_LOG(ERROR) << err_msg.c_str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - (void)image_reader->read(images_buf.get(), image_size * num_images); - if (image_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to read " + std::to_string(image_size * num_images) + - " bytes from " + image_names_[index] + - ": the data file is damaged or the content is incomplete."); - } - // uint32_t use 4 bytes in memory - (void)label_reader->read(reinterpret_cast(labels_buf.get()), label_length * num_labels * kNum4); - if (label_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to read " + std::to_string(label_length * num_labels * kNum4) + - " bytes from " + label_names_[index] + - ": the data file is damaged or content is incomplete."); - } - TensorShape image_tensor_shape = TensorShape({kQMnistImageRows, kQMnistImageCols, 1}); - TensorShape label_tensor_shape = TensorShape({kQMnistLabelLength}); - for (size_t data_index = 0; data_index != num_images; data_index++) { - auto image = &images_buf[data_index * image_size]; - std::shared_ptr image_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(image_tensor_shape, data_schema_->Column(0).Type(), - reinterpret_cast(image), &image_tensor)); - - auto label = &labels_buf[data_index * label_length]; - for (int64_t label_index = 0; label_index < static_cast(label_length); label_index++) { - label[label_index] = SwapEndian(label[label_index]); - } - std::shared_ptr label_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(label_tensor_shape, data_schema_->Column(1).Type(), - reinterpret_cast(label), &label_tensor)); - - (void)image_info_pairs_.emplace_back(std::make_pair(image_tensor, label_tensor)); - (void)image_label_pairs_.emplace_back(std::make_pair(image_tensor, label[0])); - image_path_.push_back(image_names_[index]); - label_path_.push_back(label_names_[index]); - } - return Status::OK(); -} - -Status QMnistOp::CheckLabel(const std::string &file_name, std::ifstream *label_reader, uint32_t *num_labels) { - RETURN_UNEXPECTED_IF_NULL(label_reader); - RETURN_UNEXPECTED_IF_NULL(num_labels); - CHECK_FAIL_RETURN_UNEXPECTED(label_reader->is_open(), - "Invalid file, failed to open " + file_name + ": the label file is permission denied."); - int64_t label_len = label_reader->seekg(0, std::ios::end).tellg(); - (void)label_reader->seekg(0, std::ios::beg); - // The first 12 bytes of the label file are type, number and length - CHECK_FAIL_RETURN_UNEXPECTED(label_len >= kNum12, - "Invalid file, load " + file_name + - " failed: the first 12 bytes of the label file should be type, number and length, " + - "but got the first read bytes : " + std::to_string(label_len)); - uint32_t magic_number; - RETURN_IF_NOT_OK(ReadFromReader(label_reader, &magic_number)); - CHECK_FAIL_RETURN_UNEXPECTED(magic_number == kQMnistLabelFileMagicNumber, - "Invalid label file, the number of labels loading from " + file_name + " should be " + - std::to_string(kQMnistLabelFileMagicNumber) + ", but got " + - std::to_string(magic_number) + "."); - uint32_t num_items; - RETURN_IF_NOT_OK(ReadFromReader(label_reader, &num_items)); - uint32_t length; - RETURN_IF_NOT_OK(ReadFromReader(label_reader, &length)); - CHECK_FAIL_RETURN_UNEXPECTED(length == kQMnistLabelLength, "Invalid data, length of every label loading from " + - file_name + " should be equal to 8, but got " + - std::to_string(length) + "."); - - CHECK_FAIL_RETURN_UNEXPECTED((label_len - kNum12) == static_cast(num_items * kQMnistLabelLength * kNum4), - "Invalid data, the total bytes of labels loading from Qmnist label file: " + file_name + - " should be " + std::to_string(label_len - kNum12) + ", but got " + - std::to_string(num_items * kQMnistLabelLength * kNum4) + "."); - *num_labels = num_items; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h deleted file mode 100644 index 57a97dc28..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_QMNIST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_QMNIST_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { - -using QMnistImageInfoPair = std::pair, std::shared_ptr>; - -class QMnistOp : public MnistOp { - public: - // Constructor. - // @param const std::string &folder_path - dir directory of QMNIST data file. - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'test10k', 'test50k', 'nist' or - // 'all'. - // @param bool compat - Compatibility with Mnist. - // @param std::unique_ptr data_schema - the schema of the QMNIST dataset. - // @param td::unique_ptr sampler - sampler tells QMnistOp what to read. - // @param int32_t num_workers - number of workers reading images in parallel. - // @param int32_t queue_size - connector queue size. - QMnistOp(const std::string &folder_path, const std::string &usage, bool compat, - std::unique_ptr data_schema, std::shared_ptr sampler, int32_t num_workers, - int32_t queue_size); - - // Destructor. - ~QMnistOp() = default; - - // Op name getter. - // @return std::string - Name of the current Op. - std::string Name() const override { return "QMnistOp"; } - - // DatasetName name getter - // \return std::string - DatasetName of the current Op - std::string DatasetName(bool upper = false) const { return upper ? "QMnist" : "qmnist"; } - - // A print method typically used for debugging. - // @param std::ostream &out - out stream. - // @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the QMNIST dataset. - // @param const std::string &dir - path to the QMNIST directory. - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'test10k', 'test50k', 'nist' or - // 'all'. - // @param int64_t *count - output arg that will hold the actual dataset size. - // @return Status -The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - private: - // Load a tensor row according to a pair. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow row - image & label read into this tensor row. - // @return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - // Get needed files in the folder_path_. - // @return Status - The status code returned. - Status WalkAllFiles() override; - - // Read images and labels from the file stream. - // @param std::ifstream *image_reader - image file stream. - // @param std::ifstream *label_reader - label file stream. - // @param size_t index - the index of file that is reading. - // @return Status The status code returned. - Status ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index) override; - - // Check label stream. - // @param const std::string &file_name - label file name. - // @param std::ifstream *label_reader - label file stream. - // @param uint32_t num_labels - returns the number of labels. - // @return Status The status code returned. - Status CheckLabel(const std::string &file_name, std::ifstream *label_reader, uint32_t *num_labels) override; - - const bool compat_; // compatible with mnist - - std::vector image_info_pairs_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_QMNIST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.cc deleted file mode 100644 index 98f608eac..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.cc +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h" - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" - -namespace mindspore { -namespace dataset { -// Constructor for RandomDataOp -RandomDataOp::RandomDataOp(int32_t num_workers, int32_t op_connector_size, int64_t total_rows, - std::unique_ptr data_schema) - : MappableLeafOp(num_workers, op_connector_size, std::make_shared(0, 0)), - total_rows_(total_rows), - data_schema_(std::move(data_schema)) { - rand_gen_.seed(GetSeed()); // seed the random generator - // If total rows was not given, then randomly pick a number - if (total_rows_ == 0) { - total_rows_ = GenRandomInt(1, kMaxTotalRows); - } - // If the user did not provide a schema, then we will ask the op to generate a pseudo-random schema. - // See details of generateSchema function to learn what type of schema it will create. - if (data_schema_ == nullptr) { - GenerateSchema(); - } -} - -// A print method typically used for debugging -void RandomDataOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [total rows: " << num_rows_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nTotal_rows: " << num_rows_ << " \nSchema:\n" << *data_schema_ << "\n\n"; - } -} - -// Helper function to produce a default/random schema if one didn't exist -void RandomDataOp::GenerateSchema() { - // To randomly create a schema, we need to choose: - // a) how many columns - // b) the type of each column - // c) the shape of each column (number of dimensions i.e. rank) - // d) the shape of each column (dimension values) - data_schema_ = std::make_unique(); - std::unique_ptr new_shape; - std::unique_ptr new_col; - - // Loop over the number of chosen columns - int32_t numColumns = GenRandomInt(1, kMaxNumColumns); - for (int32_t i = 0; i < numColumns; i++) { - // For each column: - // - choose a datatype - // - generate a shape that randomly chooses the number of dimensions and the dimension values. - auto newType = static_cast(GenRandomInt(1, DataType::DE_STRING - 1)); - int32_t rank = GenRandomInt(1, kMaxRank); - std::vector dims; - for (int32_t d = 0; d < rank; d++) { - // 0 is not a valid dimension value. however, we can support "*" or unknown, so map the random - // 0 value to the unknown attribute if 0 is chosen - auto dim_value = static_cast(GenRandomInt(0, kMaxDimValue)); - if (dim_value == 0) { - dim_value = TensorShape::kDimUnknown; - } - dims.push_back(dim_value); - } - new_shape = std::make_unique(dims); - - // Create the column descriptor - std::string col_name = "c" + std::to_string(i); - new_col = - std::make_unique(col_name, DataType(newType), TensorImpl::kFlexible, rank, new_shape.get()); - - Status rc = data_schema_->AddColumn(*new_col); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Failed to generate a schema. Message:" << rc; - } - } -} - -// A helper function to create random data for the row -Status RandomDataOp::CreateRandomRow(TensorRow *new_row) { - if (new_row == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Missing tensor row output."); - } - - // Create a tensor for each column, then add the tensor to the row - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - const ColDescriptor current_col = data_schema_->Column(i); - std::vector current_shape = current_col.Shape().AsVector(); - std::unique_ptr new_shape = nullptr; - std::unique_ptr buf = nullptr; - std::shared_ptr new_tensor = nullptr; - - // We need to resolve the shape to fill in any unknown dimensions with random - // values, then use that as our shape for this tensor. - for (int j = 0; j < current_shape.size(); ++j) { - if (current_shape[j] == TensorShape::kDimUnknown) { - current_shape[j] = static_cast(GenRandomInt(1, kMaxDimValue)); - } - } - - new_shape = std::make_unique(current_shape); - int64_t size_in_bytes = new_shape->NumOfElements() * current_col.Type().SizeInBytes(); - - // Generate a random byte of data. This may cause some funny data for things like doubles,floats, bools - // however the random data op is not too concerned about the physical data itself. - std::uniform_int_distribution uniDist(0, UINT8_MAX); - uint8_t random_byte = static_cast(uniDist(rand_gen_)); - - // Now, create a chunk of memory for the entire tensor and copy this byte in repeatedly. - buf = std::make_unique(size_in_bytes); - int ret_code = memset_s(buf.get(), size_in_bytes, random_byte, size_in_bytes); - if (ret_code != EOK) { - std::string error_msg = "RandomData: failed to set random data, "; - if (ret_code == ERANGE) { - RETURN_STATUS_UNEXPECTED(error_msg + "memory size of total data can not be zero or exceed " + - std::to_string(SECUREC_MEM_MAX_LEN) + ", but got: " + std::to_string(size_in_bytes)); - } else { - RETURN_STATUS_UNEXPECTED("memset_s method failed with errno_t: " + std::to_string(ret_code)); - } - } - - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(*new_shape, current_col.Type(), buf.get(), &new_tensor)); - - // Add this tensor to the tensor row for output - (*new_row).push_back(std::move(new_tensor)); - } - return Status::OK(); -} - -Status RandomDataOp::ComputeColMap() { - // Extract the column name mapping from the schema and save it in the class. - if (column_name_id_map_.empty()) { - RETURN_IF_NOT_OK(data_schema_->GetColumnNameMap(&(column_name_id_map_))); - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status RandomDataOp::LoadTensorRow(row_id_type row_id, TensorRow *row) { - CHECK_FAIL_RETURN_UNEXPECTED(row_id < total_rows_, "Wrong index."); - for (const auto &tensor : rows_[static_cast(row_id)]) { - TensorPtr new_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(tensor, &new_tensor)); - row->emplace_back(new_tensor); - } - return Status::OK(); -} - -Status RandomDataOp::PrepareData() { - for (int64_t i = 0; i < total_rows_; i++) { - TensorRow row; - RETURN_IF_NOT_OK(CreateRandomRow(&row)); - rows_.emplace_back(row); - } - num_rows_ = total_rows_; - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h deleted file mode 100644 index f7a075ee4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RANDOM_DATA_OP_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RANDOM_DATA_OP_ - -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -// The RandomDataOp is a leaf node storage operator that generates random data based -// on the schema specifications. Typically, it's used for testing and demonstrating -// various dataset operator pipelines. It is not "real" data to train with. -// The data that is random created is just random and repeated bytes, there is no -// "meaning" behind what these bytes are. -class RandomDataOp : public MappableLeafOp { - public: - // Some constants to provide limits to random generation. - static constexpr int32_t kMaxNumColumns = 4; - static constexpr int32_t kMaxRank = 4; - static constexpr int32_t kMaxDimValue = 32; - static constexpr int32_t kMaxTotalRows = 1024; - - /** - * Constructor for RandomDataOp - * @note Private constructor. Must use builder to construct. - * @param num_workers - The number of workers - * @param op_connector_size - The size of the output connector - * @param data_schema - A user-provided schema - * @param total_rows - The total number of rows in the dataset - * @return Builder - The modified builder by reference - */ - RandomDataOp(int32_t num_workers, int32_t op_connector_size, int64_t total_rows, - std::unique_ptr data_schema); - - protected: - Status PrepareData() override; - - public: - /** - * Destructor - */ - ~RandomDataOp() = default; - - /** - * A print method typically used for debugging - * @param out - The output stream to write output to - * @param show_all - A bool to control if you want to show all info or just a summary - */ - void Print(std::ostream &out, bool show_all) const override; - - /** - * << Stream output operator overload - * @notes This allows you to write the debug print info using stream operators - * @param out - reference to the output stream being overloaded - * @param so - reference to the ShuffleOp to display - * @return - the output stream must be returned - */ - friend std::ostream &operator<<(std::ostream &out, const RandomDataOp &op) { - op.Print(out, false); - return out; - } - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "RandomDataOp"; } - - protected: - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - private: - /** - * Helper function to produce a default/random schema if one didn't exist - */ - void GenerateSchema(); - - /** - * A helper function to create random data for the row - * @param new_row - The output row to produce - * @return Status The status code returned - */ - Status CreateRandomRow(TensorRow *new_row); - - /** - * A quick inline for producing a random number between (and including) min/max - * @param min - minimum number that can be generated - * @param max - maximum number that can be generated - * @return - The generated random number - */ - inline int32_t GenRandomInt(int32_t min, int32_t max) { - std::uniform_int_distribution uniDist(min, max); - return uniDist(rand_gen_); - } - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - int64_t total_rows_; - std::unique_ptr data_schema_; - std::mt19937 rand_gen_; - std::vector rows_; -}; // class RandomDataOp -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RANDOM_DATA_OP_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.cc deleted file mode 100644 index 359a3e926..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.cc +++ /dev/null @@ -1,392 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -RenderedSST2Op::RenderedSST2Op(int32_t num_wkrs, const std::string &file_dir, const std::string &usage, - int32_t queue_size, bool do_decode, const std::set &exts, - const std::map &map, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_wkrs, queue_size, std::move(sampler)), - folder_path_(file_dir), - usage_(usage), - decode_(do_decode), - extensions_(exts), - class_index_(map), - data_schema_(std::move(data_schema)), - sampler_ind_(0) { - folder_path_queue_ = std::make_unique>(num_wkrs * queue_size); - folder_classId_queue_ = std::make_unique>(num_wkrs * queue_size); - image_name_queue_ = std::make_unique>(num_wkrs * queue_size); -} - -// Master thread that pulls the prescan worker's results. -// Keep collecting results until all prescan workers quit -// Then consolidate 2 level shuffles together into 1 giant vector -// calculate numRows then return -Status RenderedSST2Op::PrepareData() { - std::vector v; - int64_t cnt = 0; - while (cnt != num_workers_) { // count number of end signals - FolderImagesPair p; - RETURN_IF_NOT_OK(image_name_queue_->PopFront(&p)); - if (p == nullptr) { - cnt++; - } else { - v.push_back(p); - } - } - std::sort(v.begin(), v.end(), - [](const FolderImagesPair &lhs, const FolderImagesPair &rhs) { return lhs->first < rhs->first; }); - // following loop puts the 2 level of shuffles together into 1 vector - for (size_t ind = 0; ind < v.size(); ++ind) { - while (!v[ind]->second.empty()) { - image_label_pairs_.push_back(v[ind]->second.front()); - image_prefix_.push_back(v[ind]->first); - v[ind]->second.pop(); - } - } - image_label_pairs_.shrink_to_fit(); - num_rows_ = image_label_pairs_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } - // free memory of two queues used for pre-scan - folder_path_queue_->Reset(); - folder_classId_queue_->Reset(); - image_name_queue_->Reset(); - return Status::OK(); -} - -// Load 1 TensorRow (image,label) using 1 ImageLabelPair. 1 function call produces 1 TensorTow -Status RenderedSST2Op::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - ImageLabelPair pair_ptr = image_label_pairs_[row_id]; - std::shared_ptr image; - std::shared_ptr label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(pair_ptr->second, &label)); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_prefix_[row_id] + pair_ptr->first, &image)); - - if (decode_) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = "Invalid image, " + folder_path_ + (pair_ptr->first) + - " decode failed, the image is broken or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({folder_path_ + (pair_ptr->first), std::string("")}); - return Status::OK(); -} - -void RenderedSST2Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\n" - << DatasetName(true) << " directory: " << folder_path_ << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// Derived from RandomAccessOp -Status RenderedSST2Op::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_label_pairs_.empty()) { - if (image_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + " file path: " + folder_path_); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR], Map containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < image_label_pairs_.size(); ++i) { - (*cls_ids)[image_label_pairs_[i]->second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -// Worker Entry for pre-scanning all the folders and do the 1st level shuffle -// Worker pull a file path from folder_path_queue_ (which is a Queue), walks all the images under that folderpath -// After walking is complete, sort all the file names (relative path to all png files under the same directory ) -// (Sort is automatically conducted using a set which is implemented using a Red-Black Tree) -// Add the sorted filenames in to a queue. The make a pair (folderpath, queue*), -// folderpath is used for 2nd level sorting. -// FYI: 1st level sorting: sort all images under the same directory. -// FYI: 2nd level sorting: sort all folder names -// push this pair to image_name_queue (which is again a Queue) -Status RenderedSST2Op::PrescanWorkerEntry(int32_t worker_id) { - TaskManager::FindMe()->Post(); - std::string folder_; - uint32_t current_class_id; - { - // since 2 queues are shared by all workers, use lock to keep samples fetched in sequence. - std::unique_lock lock(access_mutex_); - RETURN_IF_NOT_OK(folder_path_queue_->PopFront(&folder_)); - RETURN_IF_NOT_OK(folder_classId_queue_->PopFront(¤t_class_id)); - } - while (!folder_.empty()) { - Path folder(folder_); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (!folder.Exists() || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid dataset_dir, " + folder_ + " does not exist or permission denied."); - } - auto offset_ = folder_.size(); - std::set imgs; // use this for ordering - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (extensions_.empty() || extensions_.find(file.Extension()) != extensions_.end()) { - (void)imgs.insert(file.ToString().substr(offset_)); - } else { - MS_LOG(WARNING) << DatasetName(true) << " operator unsupported file found: " << file.ToString() - << ", extension: " << file.Extension() << "."; - } - } - FolderImagesPair p = std::make_shared>>(); - p->first = folder_; - for (const std::string &img : imgs) { - p->second.push(std::make_shared>(img, current_class_id)); - } - RETURN_IF_NOT_OK(image_name_queue_->EmplaceBack(p)); - { - // since 2 queues are shared by all workers, use lock to keep samples fetched in sequence. - std::unique_lock lock(access_mutex_); - RETURN_IF_NOT_OK(folder_path_queue_->PopFront(&folder_)); - RETURN_IF_NOT_OK(folder_classId_queue_->PopFront(¤t_class_id)); - } - } - RETURN_IF_NOT_OK(image_name_queue_->EmplaceBack(nullptr)); // end signal - return Status::OK(); -} - -// This helper function walks all folder_paths, and send each folderpath to folder_path_queue_ -Status RenderedSST2Op::WalkFolder(Path *dir) { - RETURN_UNEXPECTED_IF_NULL(dir); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - auto offset_ = dir->ToString().size(); - std::string current_class; - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - RETURN_IF_NOT_OK(folder_path_queue_->EmplaceBack(subdir.ToString())); - current_class = subdir.ToString().substr(offset_ + 1); - if (class_index_.find(current_class) == class_index_.end()) { - class_index_[current_class] = class_index_.size(); - } - RETURN_IF_NOT_OK(folder_classId_queue_->EmplaceBack(class_index_[current_class])); - } - } - return Status::OK(); -} - -// A thread that calls WalkFolder -Status RenderedSST2Op::StartAsyncWalk() { - TaskManager::FindMe()->Post(); - Path dir(folder_path_); - - if (!dir.Exists() || !dir.IsDirectory()) { - RETURN_STATUS_UNEXPECTED("Invalid path, " + folder_path_ + " may not exist or is not a directory."); - } - - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - auto offset_ = folder_path_.length(); - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - std::string name = subdir.ToString().substr(offset_ + 1); - if (usage_ == name) { - RETURN_IF_NOT_OK(WalkFolder(&subdir)); - } else if (usage_ == "val" && name == "valid") { - RETURN_IF_NOT_OK(WalkFolder(&subdir)); - } else if (usage_ == "all" && (name == "train" || name == "test" || name == "valid")) { - RETURN_IF_NOT_OK(WalkFolder(&subdir)); - } - } - } - // send out num_workers_ end signal to folder_path_queue_, 1 for each worker. - // Upon receiving end Signal, worker quits and set another end Signal to image_name_queue. - for (int32_t ind = 0; ind < num_workers_; ++ind) { - RETURN_IF_NOT_OK(folder_path_queue_->EmplaceBack("")); // end signal - RETURN_IF_NOT_OK(folder_classId_queue_->EmplaceBack(0)); - } - return Status::OK(); -} - -Status RenderedSST2Op::RegisterAndLaunchThreads() { - RETURN_IF_NOT_OK(ParallelOp::RegisterAndLaunchThreads()); - RETURN_IF_NOT_OK(folder_path_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(folder_classId_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(image_name_queue_->Register(tree_->AllTasks())); - - // The following code launch 3 threads group - // 1) A thread that walks all folders and push the folder names to a util:Queue folder_path_queue_. - // 2) Workers that pull foldername from folder_path_queue_, walk it and return the sorted images to image_name_queue - // 3) Launch main workers that load TensorRows by reading all images - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask(Name() + "::WalkDir", - std::bind(&RenderedSST2Op::StartAsyncWalk, this), nullptr, id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&RenderedSST2Op::PrescanWorkerEntry, this, std::placeholders::_1), - Name() + "::PrescanWorkerEntry", id())); - - return Status::OK(); -} - -Status RenderedSST2Op::WalkFolderForCountRows(Path *dir, std::queue *folder_paths, - std::map *class_index) { - RETURN_UNEXPECTED_IF_NULL(dir); - RETURN_UNEXPECTED_IF_NULL(folder_paths); - RETURN_UNEXPECTED_IF_NULL(class_index); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - std::string current_class; - auto offset_ = dir->ToString().size(); - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - folder_paths->push(subdir.ToString()); - current_class = subdir.ToString().substr(offset_ + 1); - if (class_index->find(current_class) == class_index->end()) { - (*class_index)[current_class] = class_index->size(); - } - } - } - return Status::OK(); -} - -Status RenderedSST2Op::CountRows(std::queue *folder_paths, int64_t *num_rows, - const std::set &exts) { - RETURN_UNEXPECTED_IF_NULL(folder_paths); - RETURN_UNEXPECTED_IF_NULL(num_rows); - int64_t row_cnt = 0; - while (!folder_paths->empty()) { - Path subdir(folder_paths->front()); - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&subdir); - if (!subdir.Exists() || dir_itr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid subdirectory, RenderedSST2 Dataset subdirectory: " + subdir.ToString() + - " does not exist or permission denied"); - } - while (dir_itr->HasNext()) { - if (exts.empty() || exts.find(dir_itr->Next().Extension()) != exts.end()) { - ++row_cnt; - } - } - folder_paths->pop(); - } - (*num_rows) = row_cnt; - return Status::OK(); -} - -Status RenderedSST2Op::CountRowsAndClasses(const std::string &path, const std::string &usage, - const std::set &exts, int64_t *num_rows, int64_t *num_classes) { - Path dir(path); - std::string err_msg = ""; - err_msg += (!dir.Exists() || !dir.IsDirectory()) - ? "Invalid dataset_dir, " + path + " does not exist or the path is not a directory. " - : ""; - err_msg += (num_classes == nullptr && num_rows == nullptr) ? "[Internal ERROR] num_class and num_rows are null." : ""; - if (!err_msg.empty()) { - RETURN_STATUS_UNEXPECTED(err_msg); - } - std::queue folder_paths; - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - std::map class_index; - auto offset_ = path.size(); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - - while (dir_itr->HasNext()) { - Path subdir = dir_itr->Next(); - if (subdir.IsDirectory()) { - std::string name = subdir.ToString().substr(offset_ + 1); - name = name == "valid" ? "val" : name; - if (usage == name) { - RETURN_IF_NOT_OK(WalkFolderForCountRows(&subdir, &folder_paths, &class_index)); - } else if (usage == "all" && (name == "train" || name == "test" || name == "val")) { - RETURN_IF_NOT_OK(WalkFolderForCountRows(&subdir, &folder_paths, &class_index)); - } - } - } - - if (num_classes != nullptr) { - *num_classes = class_index.size(); - } - // return here if only num_class is needed - RETURN_OK_IF_TRUE(num_rows == nullptr); - - RETURN_IF_NOT_OK(CountRows(&folder_paths, num_rows, exts)); - return Status::OK(); -} - -Status RenderedSST2Op::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -// Get number of classes -Status RenderedSST2Op::GetNumClasses(int64_t *num_classes) { - RETURN_UNEXPECTED_IF_NULL(num_classes); - if (num_classes_ > 0) { - *num_classes = num_classes_; - return Status::OK(); - } - RETURN_IF_NOT_OK(CountRowsAndClasses(folder_path_, usage_, extensions_, nullptr, num_classes)); - num_classes_ = *num_classes; - return Status::OK(); -} - -Status RenderedSST2Op::InitPullMode() { - // to avoid the concurrent and multi end signal in StartAsyncWalk, explicitly set num_workers_ to 1 - num_workers_ = 1; - RETURN_IF_NOT_OK(folder_path_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(image_name_queue_->Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask(Name() + "::WalkDir", - std::bind(&RenderedSST2Op::StartAsyncWalk, this), nullptr, id())); - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&RenderedSST2Op::PrescanWorkerEntry, this, std::placeholders::_1), - Name() + "::PrescanWorkerEntry", id())); - return PrepareData(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h deleted file mode 100644 index b9b3d1a02..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RENDERED_SST2_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RENDERED_SST2_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// Forward declares -template -class Queue; - -using ImageLabelPair = std::shared_ptr>; -using FolderImagesPair = std::shared_ptr>>; - -class RenderedSST2Op : public MappableLeafOp { - public: - /// Constructor. - /// @param int32_t num_wkrs - Num of workers reading images in parallel. - /// @param const std::string &file_dir - Directory of RenderedSST2Dataset. - /// @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'val' or 'all'. - /// @param int32_t queue_size - Connector queue size. - /// @param bool do_decode - Decode the images after reading. - /// @param std::set &exts - Set of file extensions to read, if empty, read everything under the dir. - /// @param std::map &map- Map of class name and class id. - /// @param std::unique_ptr data_schema - Schema of data. - /// @param std::shared_ptr sampler - Sampler tells RenderedSST2Op what to read. - RenderedSST2Op(int32_t num_wkrs, const std::string &file_dir, const std::string &usage, int32_t queue_size, - bool do_decode, const std::set &exts, const std::map &map, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// Destructor. - ~RenderedSST2Op() override = default; - - /// Initialize RenderedSST2Op related var, calls the function to walk all files. - /// @return Status The status code returned. - Status PrepareData() override; - - /// Worker thread pulls a number of IOBlock from IOBlock Queue, make a TensorRow and push it to Connector. - /// @param int32_t worker_id - Id of each worker. - /// @return Status The status code returned. - Status PrescanWorkerEntry(int32_t worker_id); - - /// Method derived from RandomAccess Op, enable Sampler to get all ids for each class. - /// @param (std::map> * cls_ids - Key label, val all ids for this class. - /// @return Status The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - /// A print method typically used for debugging. - /// @param std::ostream &out - Out stream. - /// @param bool show_all - Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// This function is a hack! It is to return the num_class and num_rows. The result - /// returned by this function may not be consistent with what RenderedSST2Op is going to return - /// user this at your own risk! - /// @param const std::string &path - Directory of RenderedSST2Dataset. - /// @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'valid' or 'all'. - /// @param const std::set &exts - Set of file extensions to read, if empty, read everything under the - /// dir. - /// @param int64_t *num_rows - The number of rows. - /// @param int64_t *num_classes - The number of classes. - /// @return Status of the function. - static Status CountRowsAndClasses(const std::string &path, const std::string &usage, - const std::set &exts, int64_t *num_rows, int64_t *num_classes); - - /// This help function is used to count the num_rows. - /// @param std::queue *folder_paths - A queue contains all the image folder paths. - /// @param int64_t *num_rows - The number of rows. - /// @param const std::set &exts - Set of file extensions to read, if empty, read everything under the - /// dir. - /// @return Status of the function. - static Status CountRows(std::queue *folder_paths, int64_t *num_rows, const std::set &exts); - - /// Op name getter. - /// @return Name of the current Op. - std::string Name() const override { return "RenderedSST2Op"; } - - /// DatasetName name getter. - /// @return DatasetName of the current Op. - virtual std::string DatasetName(bool upper = false) const { return upper ? "RenderedSST2" : "rendered sst2"; } - - /// Base-class override for GetNumClasses. - /// @param num_classes - The number of classes. - /// @return Status of the function. - Status GetNumClasses(int64_t *num_classes) override; - - protected: - /// Load a tensor row according to a pair. - /// @param row_id_type row_id - Id for this tensor row. - /// @param TensorRow *row - Image & label read into this tensor row. - /// @return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// @param Path * dir - Dir to walk all folders. - /// @return Status The status code returned. - virtual Status WalkFolder(Path *dir); - - /// @param Path * dir - Dir to walk all images. - /// @param std::queue *folder_paths - A queue contains all the image folder paths. - /// @param std::map *class_index - A map records the class and the class's Id. - /// @return Status The status code returned. - static Status WalkFolderForCountRows(Path *dir, std::queue *folder_paths, - std::map *class_index); - - /// start walking of all dirs. - /// @return Status The status code returned. - Status StartAsyncWalk(); - - /// Called first when function is called. - /// @return Status The status code returned. - Status RegisterAndLaunchThreads() override; - - /// Private function for computing the assignment of the column name map. - /// @return Status The status code returned. - Status ComputeColMap() override; - - /// Initialize pull mode, calls PrepareData() within - /// @return Status The status code returned - Status InitPullMode() override; - - std::string folder_path_; // directory of image folder - std::string usage_; - bool decode_; - std::set extensions_; // extensions allowed - std::map class_index_; - std::unique_ptr data_schema_; - int64_t sampler_ind_; - std::vector image_label_pairs_; - std::vector image_prefix_; - std::unique_ptr> folder_path_queue_; - std::unique_ptr> folder_classId_queue_; // the class Id of the images under the folder - std::unique_ptr> image_name_queue_; - std::mutex access_mutex_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_RENDERED_SST2_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.cc deleted file mode 100644 index f9d72ba85..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.cc +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -DistributedSamplerRT::DistributedSamplerRT(int64_t num_shards, int64_t shard_id, bool shuffle, int64_t num_samples, - uint32_t seed, int64_t offset, bool even_dist) - : SamplerRT(num_samples, std::numeric_limits::max()), - cnt_(0), - seed_(seed == std::numeric_limits::max() ? GetSeed() : seed), - device_id_(shard_id), - num_devices_(num_shards), - shuffle_(shuffle), - even_dist_(even_dist), - offset_(offset), - non_empty_(true) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_devices_); -} - -Status DistributedSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - // Special value of 0 for num_samples means that the user wants to sample the entire set of data. - // If the user asked to sample more rows than exists in the dataset, adjust the num_samples accordingly. - if (num_samples_ == 0 || num_samples_ > num_rows_) { - num_samples_ = num_rows_; - } - CHECK_FAIL_RETURN_UNEXPECTED(num_samples_ > 0, "Invalid parameter, num_samples must be greater than 0, but got " + - std::to_string(num_samples_) + ".\n"); - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows_ > 0, "[Internal ERROR] num_rows must be greater than 0, but got " + std::to_string(num_rows_) + ".\n"); - CHECK_FAIL_RETURN_UNEXPECTED( - device_id_ < num_devices_ && device_id_ >= 0 && num_rows_ > 0 && num_samples_ > 0, - "Invalid parameter, num_shard must be greater than shard_id and greater than 0, got num_shard: " + - std::to_string(num_devices_) + ", shard_id: " + std::to_string(device_id_) + ".\n"); - rnd_.seed(seed_++); - - if (offset_ != -1 || !even_dist_) { - if (offset_ == -1) { - offset_ = 0; - } - samples_per_tensor_ = (num_rows_ + offset_) / num_devices_; - int64_t remainder = (num_rows_ + offset_) % num_devices_; - if (device_id_ < remainder) { - samples_per_tensor_++; - } - if (device_id_ < offset_) { - samples_per_tensor_--; - } - } else { - offset_ = 0; - samples_per_tensor_ = (num_rows_ + num_devices_ - 1) / num_devices_; // equals to ceil(num_rows/num_devices) - } - samples_per_tensor_ = num_samples_ < samples_per_tensor_ ? num_samples_ : samples_per_tensor_; - if (shuffle_) { - shuffle_vec_.reserve(num_rows_); - for (int64_t i = 0; i < num_rows_; i++) { - shuffle_vec_.push_back(i); - } - std::shuffle(shuffle_vec_.begin(), shuffle_vec_.end(), rnd_); - } - if (!samples_per_tensor_) { - non_empty_ = false; - } - - is_initialized = true; - return Status::OK(); -} - -Status DistributedSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (cnt_ > samples_per_tensor_) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Sampler index must be less than or equal to num_samples(total rows in dataset), but got:" + - std::to_string(cnt_) + ", samples_per_tensor(num_samples): " + std::to_string(samples_per_tensor_)); - } else if (cnt_ == samples_per_tensor_ && (non_empty_ || !even_dist_)) { - (*out) = TensorRow(TensorRow::kFlagEOE); - if (samples_per_tensor_ == 0) { - non_empty_ = false; - } - } else if (samples_per_tensor_ == 0 && !non_empty_) { - // If the Tensor is empty, we add samples with subscript 0 in the current dataset. - // This step is to make up for the solution that the code default Tensor is not empty before. - // We will remove this value in the concat phase - non_empty_ = true; - std::shared_ptr sample_ids; - RETURN_IF_NOT_OK(CreateSamplerTensor(&sample_ids, 1)); - auto id_ptr = sample_ids->begin(); - // add index 0 - *id_ptr = 0; - (*out) = {sample_ids}; - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr sample_ids; - RETURN_IF_NOT_OK(CreateSamplerTensor(&sample_ids, samples_per_tensor_)); - auto id_ptr = sample_ids->begin(); - bool flag_add_1 = false; - while (cnt_ < samples_per_tensor_ && id_ptr != sample_ids->end()) { - int64_t middle_value = num_devices_ * cnt_ + device_id_ - offset_; - // if index < 0, we move back one place - if (middle_value < 0) { - samples_per_tensor_++; - cnt_++; - flag_add_1 = true; - middle_value = num_devices_ * cnt_ + device_id_ - offset_; - } - int64_t sampled_id = middle_value % num_rows_; - - if (shuffle_) { - sampled_id = shuffle_vec_[static_cast(sampled_id)]; - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&sampled_id, sampled_id)); - } - - *id_ptr = sampled_id; - ++id_ptr; - cnt_++; - } - - // If 1 was added before, we will cut off 1 here - if (flag_add_1) { - samples_per_tensor_--; - cnt_--; - } - (*out) = {sample_ids}; - } - return Status::OK(); -} - -Status DistributedSamplerRT::ResetSampler(const bool failover_reset) { - CHECK_FAIL_RETURN_UNEXPECTED(failover_reset || cnt_ == samples_per_tensor_, - "[Internal ERROR] ResetSampler() called early or late."); - cnt_ = 0; - - if (shuffle_ == true) { - rnd_.seed(seed_); - seed_++; - std::shuffle(shuffle_vec_.begin(), shuffle_vec_.end(), rnd_); - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -int64_t DistributedSamplerRT::CalculateNumSamples(int64_t num_rows) { - int64_t child_num_rows = num_rows; - if (!child_.empty()) { - child_num_rows = child_[0]->CalculateNumSamples(num_rows); - } - int64_t num_samples = (num_samples_ > 0) ? std::min(child_num_rows, num_samples_) : child_num_rows; - int64_t remainder = (child_num_rows + offset_) % num_devices_; - int64_t shard_size = (child_num_rows + offset_) / num_devices_; - if (offset_ != -1 || !even_dist_) { - if (offset_ == -1) { - offset_ = 0; - } - if (device_id_ < remainder) { - shard_size++; - } - if (device_id_ < offset_) { - shard_size--; - } - } else { - shard_size = (child_num_rows + num_devices_ - 1) / num_devices_; - } - // add 1 to an empty shard - // this logic is needed to follow the logic in initSampler that is written for ConcatDataset - if (shard_size == 0) { - shard_size++; - } - - return std::min(num_samples, shard_size); -} - -void DistributedSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: DistributedSampler"; - if (show_all) { - SamplerRT::SamplerPrint(out, show_all); - out << "\nseed: " << seed_ << "\ndevice_id: " << device_id_ << "\nnum_devices: " << num_devices_ - << "\nshuffle: " << shuffle_; - } -} - -Status DistributedSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "DistributedSampler"; - args["num_shards"] = num_devices_; - args["shard_id"] = device_id_; - args["shuffle"] = shuffle_; - args["offset"] = offset_; - *out_json = args; - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h deleted file mode 100644 index ca7982af1..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_DISTRIBUTED_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_DISTRIBUTED_SAMPLER_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -class DistributedSamplerRT : public SamplerRT { - public: - /// \brief Constructor - /// \param[in] num_shards Total number of shards for the distributed sampler - /// \param[in] shard_id Device id of the shard - /// \param[in] shuffle Option to shuffle - /// \param[in] num_samples The total number of rows in the dataset - /// \param seed Seed parameter to shuffle, default to max unsigned int (different seed in sampler will - /// result in different samples being picked - /// \param[in] offset The starting device id where the elements in the dataset are send to, which should be no more - /// than num_dev. The application scenario of this parameter is when the concatdataset is set distributedSampler - /// \param even_dist The option to indicate whether or not each shard returns the same number of rows. - /// This option is not exposed in the python API. Current behavior is that the remainder will always - /// be handled by the first n shards, n being the corresponding device id. Please notice that when offset is set, - /// even_dist will be forcibly converted to false for sending rest datasets in concatdataset scenario. - DistributedSamplerRT(int64_t num_shards, int64_t shard_id, bool shuffle, int64_t num_samples, - uint32_t seed = std::numeric_limits::max(), int64_t offset = -1, - bool even_dist = true); - - /// \brief default destructor - ~DistributedSamplerRT() = default; - - /// \param TensorRow out - /// \param int32_t workerId - /// \return Status code - Status GetNextSample(TensorRow *out) override; - - /// Init sampler, called by base class or python - Status InitSampler() override; - - /// \brief Reset for next epoch. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset) override; - - int64_t GetDeviceID() { return device_id_; } - - int64_t GetDeviceNum() { return num_devices_; } - - /// \brief Recursively calls this function on its children to get the actual number of samples on a tree of samplers - /// \note This is not a getter for num_samples_. For example, if num_samples_ is 0 or if it's smaller than num_rows, - /// then num_samples_ is not returned at all. - /// \param[in] num_rows The total number of rows in the dataset - /// \return int64_t Calculated number of samples - int64_t CalculateNumSamples(int64_t num_rows) override; - - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - int64_t cnt_; // number of samples that have already been filled in to Tensor - uint32_t seed_; - int64_t device_id_; - int64_t num_devices_; - bool shuffle_; - std::mt19937 rnd_; - std::vector shuffle_vec_; - bool even_dist_; - int64_t offset_; - bool non_empty_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_DISTRIBUTED_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.cc deleted file mode 100644 index 9caae5bb0..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.cc +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h" -#include -#include -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -PKSamplerRT::PKSamplerRT(int64_t num_val, bool shuffle, int64_t num_samples, int64_t samples_per_tensor) - : SamplerRT(num_samples, samples_per_tensor), - shuffle_(shuffle), - seed_(GetSeed()), - next_id_(0), - samples_per_class_(num_val) {} - -Status PKSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - labels_.reserve(label_to_ids_.size()); - for (const auto &pair : label_to_ids_) { - if (!pair.second.empty()) { - labels_.push_back(pair.first); - } - } - rnd_.seed(seed_++); - - // The special handshake gives the list of classes and id's, but it did not set the num_rows_ to - // capture the total number of possible sample ids. - // Compute that here for this case to find the total number of samples that are available to return. - // (in this case, samples per class * total classes). - if (samples_per_class_ > std::numeric_limits::max() / static_cast(labels_.size())) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Overflow in counting num_rows"); - } - num_rows_ = samples_per_class_ * static_cast(labels_.size()); - - // The user may have chosen to sample less than the total amount. - // Special value of 0 for num_samples means that the user wants to sample the entire set of data. - // If the user asked to sample more rows than exists in the dataset, adjust the num_samples accordingly. - if (num_samples_ == 0 || num_samples_ > num_rows_) { - num_samples_ = num_rows_; - } - - samples_per_tensor_ = (samples_per_tensor_ > num_samples_) ? num_samples_ : samples_per_tensor_; - if (shuffle_ == true) { - std::shuffle(labels_.begin(), labels_.end(), rnd_); - } else { - std::sort(labels_.begin(), labels_.end()); - } - CHECK_FAIL_RETURN_UNEXPECTED( - num_samples_ > 0, "Invalid parameter, num_class or num samples per class must be greater than 0, but got " + - std::to_string(num_samples_)); - is_initialized = true; - return Status::OK(); -} - -Status PKSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (next_id_ > num_samples_ || num_samples_ == 0) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Sampler index must be less than or equal to num_samples(total rows in dataset), but got: " + - std::to_string(next_id_) + ", num_samplers:" + std::to_string(num_samples_)); - } else if (next_id_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr sample_ids; - int64_t last_id = (samples_per_tensor_ + next_id_ > num_samples_) ? num_samples_ : samples_per_tensor_ + next_id_; - RETURN_IF_NOT_OK(CreateSamplerTensor(&sample_ids, last_id - next_id_)); - auto id_ptr = sample_ids->begin(); - CHECK_FAIL_RETURN_UNEXPECTED(samples_per_class_ != 0, "Invalid Parameter, num samples per class can't be zero."); - while (next_id_ < last_id && id_ptr != sample_ids->end()) { - int64_t cls_id = next_id_++ / samples_per_class_; - const std::vector &samples = label_to_ids_[labels_[cls_id]]; - int64_t rnd_ind = std::uniform_int_distribution(0, samples.size() - 1)(rnd_); - int64_t sampled_id = samples[rnd_ind]; - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&sampled_id, sampled_id)); - } - - *id_ptr = sampled_id; - ++id_ptr; - } - - (*out) = {sample_ids}; - } - return Status::OK(); -} - -Status PKSamplerRT::ResetSampler(const bool failover_reset) { - CHECK_FAIL_RETURN_UNEXPECTED(failover_reset || next_id_ == num_samples_, - "[Internal ERROR] ResetSampler() called early or late."); - next_id_ = 0; - rnd_.seed(seed_++); - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -Status PKSamplerRT::HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count) { - RETURN_UNEXPECTED_IF_NULL(op); - RETURN_IF_NOT_OK(op->GetClassIds(&label_to_ids_)); - RETURN_IF_NOT_OK(InitSampler()); - // Move forward sampler's random generator if resetting the pipeline in fast_recovery mode - if (GlobalContext::config_manager()->fast_recovery()) { - for (auto i = 0; i < reset_count; i++) { - RETURN_IF_NOT_OK(ResetSampler(true)); // failover_reset = true - } - } - return Status::OK(); -} - -void PKSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: PKSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} - -Status PKSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "PKSampler"; - args["num_val"] = samples_per_class_; - args["shuffle"] = shuffle_; - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h deleted file mode 100644 index d9e412815..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_PK_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_PK_SAMPLER_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -class PKSamplerRT : public SamplerRT { // NOT YET FINISHED - public: - // @param int64_t num_val - // @param bool shuffle - shuffle all classIds or not, if true, classes may be 5,1,4,3,2 - // @param num_samples - the number of samples to draw. value of 0 means to take the full amount - // @param int64_t samples_per_tensor - Num of Sampler Ids to fetch via 1 GetNextSample call - PKSamplerRT(int64_t num_val, bool shuffle, int64_t num_samples, - int64_t samples_per_tensor = std::numeric_limits::max()); - - // default destructor - ~PKSamplerRT() = default; - - // @param TensorRow - // @param int32_t workerId - // @return Status The status code returned - Status GetNextSample(TensorRow *out) override; - - // first handshake between leaf source op and Sampler. This func will determine the amount of data - // in the dataset that we can sample from. - // @param op - leaf op pointer, pass in so Sampler can ask it about how much data there is - // @param reset_count - reset the random generator these many times (used in fast_recovery mode of reset) - // @return - Status HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count = 0) override; - - // init sampler, to be called by python or Handshake - Status InitSampler() override; - - /// \brief Reset for next epoch. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - // Printer for debugging purposes. - // @param out - output stream to write to - // @param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief PK cannot return an exact value because num_classes is not known until runtime, hence -1 is used - /// \param[out] num_rows - /// \return -1, which means PKSampler doesn't know how much data - int64_t CalculateNumSamples(int64_t num_rows) override { return -1; } - - private: - bool shuffle_; - uint32_t seed_; - int64_t next_id_; - int64_t samples_per_class_; - std::mt19937 rnd_; - std::vector labels_; - std::map> label_to_ids_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_PK_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.cc deleted file mode 100644 index 543f823d9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -RandomSamplerRT::RandomSamplerRT(bool replacement, int64_t num_samples, bool reshuffle_each_epoch, - int64_t samples_per_tensor) - : SamplerRT(num_samples, samples_per_tensor), - seed_(GetSeed()), - replacement_(replacement), - next_id_(0), - dist(nullptr), - reshuffle_each_epoch_(reshuffle_each_epoch) {} - -Status RandomSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (next_id_ > num_samples_) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Sampler index must be less than or equal to num_samples(total rows in dataset), but got" + - std::to_string(next_id_) + ", num_samplers:" + std::to_string(num_samples_)); - } else if (next_id_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr sampleIds; - int64_t last_id = std::min(samples_per_tensor_ + next_id_, num_samples_); - RETURN_IF_NOT_OK(CreateSamplerTensor(&sampleIds, last_id - next_id_)); - auto id_ptr = sampleIds->begin(); - - for (int64_t i = 0; i < (last_id - next_id_); i++) { - int64_t sampled_id = 0; - if (replacement_) { - sampled_id = (*dist)(rnd_); - } else { - sampled_id = shuffled_ids_[static_cast(i + next_id_)]; - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&sampled_id, sampled_id)); - } - - *(id_ptr + static_cast(i)) = sampled_id; - } - next_id_ = last_id; - (*out) = {sampleIds}; - } - return Status::OK(); -} - -Status RandomSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - // Special value of 0 for num_samples means that the user wants to sample the entire set of data. - // If the user asked to sample more rows than exists in the dataset, adjust the num_samples accordingly. - if (num_samples_ == 0 || num_samples_ > num_rows_) { - num_samples_ = num_rows_; - } - CHECK_FAIL_RETURN_UNEXPECTED( - num_samples_ > 0 && num_rows_ > 0, - "[Internal ERROR] num_samples and num_rows must be greater than 0, but got num_samples: " + - std::to_string(num_samples_) + ", num_rows: " + std::to_string(num_rows_)); - samples_per_tensor_ = samples_per_tensor_ > num_samples_ ? num_samples_ : samples_per_tensor_; - rnd_.seed(seed_); - - if (!replacement_) { - shuffled_ids_.reserve(num_rows_); - for (int64_t i = 0; i < num_rows_; i++) { - shuffled_ids_.push_back(i); - } - std::shuffle(shuffled_ids_.begin(), shuffled_ids_.end(), rnd_); - } else { - dist = std::make_unique>(0, num_rows_ - 1); - } - - is_initialized = true; - return Status::OK(); -} - -Status RandomSamplerRT::ResetSampler(const bool failover_reset) { - CHECK_FAIL_RETURN_UNEXPECTED(failover_reset || next_id_ == num_samples_, - "[Internal ERROR] ResetSampler() called early or late."); - next_id_ = 0; - - if (reshuffle_each_epoch_) { - seed_++; - } - - rnd_.seed(seed_); - - if (!replacement_ && reshuffle_each_epoch_) { - std::shuffle(shuffled_ids_.begin(), shuffled_ids_.end(), rnd_); - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -void RandomSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: RandomSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} - -Status RandomSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "RandomSampler"; - args["replacement"] = replacement_; - args["reshuffle_each_epoch"] = reshuffle_each_epoch_; - - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h deleted file mode 100644 index 0d05def32..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_RANDOM_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_RANDOM_SAMPLER_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -class RandomSamplerRT : public SamplerRT { - public: - // Constructor - // @param bool replacement - put he id back / or not after a sample - // @param int64_t num_samples - number samples to draw - // @param reshuffle_each_epoch - T/F to reshuffle after epoch - // @param int64_t samples_per_tensor - Num of Sampler Ids to fetch via 1 GetNextSample call - RandomSamplerRT(bool replacement, int64_t num_samples, bool reshuffle_each_epoch, - int64_t samples_per_tensor = std::numeric_limits::max()); - - // Destructor. - ~RandomSamplerRT() = default; - - // Op calls this to get next Sample that contains all the sampleIds - // @param TensorRow to be returned to StorageOp - // @param int32_t workerId - not meant to be used - // @return Status The status code returned - Status GetNextSample(TensorRow *out) override; - - // meant to be called by base class or python - Status InitSampler() override; - - /// \brief Reset for next epoch. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - uint32_t seed_; - bool replacement_; - std::vector shuffled_ids_; // only used for NO REPLACEMENT - int64_t next_id_; - std::mt19937 rnd_; - std::unique_ptr> dist; - bool reshuffle_each_epoch_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_RANDOM_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.cc deleted file mode 100644 index d814e873a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.cc +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -#include -#include - -namespace mindspore { -namespace dataset { -Status RandomAccessOp::GetNumRowsInDataset(int64_t *num) const { - RETURN_UNEXPECTED_IF_NULL(num); - // The sampler base class itself does not compute it's own num_rows_ value. - // Instead, this value is computed by the derived leaf op during it's own initialization - // after it has interacted with it's storage layers. - // Here, it is just a getter method to return the value. However, it is invalid if there is - // not a value set for this count, so generate a failure if that is the case. - if (num == nullptr || num_rows_ == -1) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Get num rows in Dataset failed, num_rows has not been set yet."); - } - (*num) = num_rows_; - return Status::OK(); -} - -SamplerRT::SamplerRT(int64_t num_samples, int64_t samples_per_tensor) - : num_rows_(0), - num_samples_(num_samples), - samples_per_tensor_(samples_per_tensor), - col_desc_(nullptr), - is_initialized(false), - child_ids_(TensorRow()) {} - -Status SamplerRT::HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count) { - RETURN_UNEXPECTED_IF_NULL(op); - std::shared_ptr child_sampler; - if (HasChildSampler()) { - child_sampler = std::dynamic_pointer_cast(child_[0]); - if (!child_sampler) { - std::string err_msg("[Internal ERROR] Cannot handshake, child is not a sampler object."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Handshake and init child first. - RETURN_IF_NOT_OK(child_sampler->HandshakeRandomAccessOp(op)); - } - - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "[Internal ERROR] RandomAccessOp init failed, as it is nullptr."); - - // If there's a child sampler, set the row count to be it's sample count - if (HasChildSampler()) { - num_rows_ = child_sampler->num_samples_; - } else { - RETURN_IF_NOT_OK(op->GetNumRowsInDataset(&num_rows_)); - } - - // It's up to the derived class to check the validity of the two args - // Because some sampler only needs one of the arg (weighted_random_sampler) - RETURN_IF_NOT_OK(InitSampler()); // init sampler after callback - // Move forward sampler's random generator if resetting the pipeline in fast_recovery mode - if (GlobalContext::config_manager()->fast_recovery()) { - for (auto i = 0; i < reset_count; i++) { - RETURN_IF_NOT_OK(ResetSampler(true)); // failover_reset = true - } - } - return Status::OK(); -} - -Status SamplerRT::CreateSamplerTensor(std::shared_ptr *sample_ids, int64_t num_elements) { - RETURN_UNEXPECTED_IF_NULL(sample_ids); - if (col_desc_ == nullptr) { - // a ColDescriptor for Tensor that holds SampleIds - col_desc_ = std::make_unique("sampleIds", DataType(DataType::DE_INT64), TensorImpl::kFlexible, 1); - } - TensorShape shape(std::vector(1, num_elements)); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, col_desc_->Type(), sample_ids)); - return Status::OK(); -} - -void SamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - // Sampler printing is usually only called in the show_all mode. - // Derived classes will display the name, then call back to this base - // for common info. - // No-op in the summary mode. - if (show_all) { - out << "\nnum_rows_: " << num_rows_ << "\nnum_samples_: " << num_samples_; - } -} - -#ifdef ENABLE_PYTHON -Status SamplerRT::GetAllIdsThenReset(py::array *data) { - RETURN_UNEXPECTED_IF_NULL(data); - std::shared_ptr sample_ids; - TensorRow sample_row; - - // Get the only tensor inside the row that contains the actual SampleIds for the entire epoch - RETURN_IF_NOT_OK(GetNextSample(&sample_row)); - sample_ids = sample_row[0]; - - // check this tensorRow is not a ctrl tensorRow - CHECK_FAIL_RETURN_UNEXPECTED(sample_row.Flags() == TensorRow::kFlagNone, "[Internal ERROR] ctrl row received."); - - // perform error checking! Next TensorRow supposed to be EOE since last one already contains all ids for current epoch - RETURN_IF_NOT_OK(GetNextSample(&sample_row)); - CHECK_FAIL_RETURN_UNEXPECTED(sample_row.eoe(), "[Internal ERROR] Non EOE received in the end of epoch."); - // Reset Sampler since this is the end of the epoch - RETURN_IF_NOT_OK(ResetSampler()); - - { - py::gil_scoped_acquire gil_acquire; - if (Py_IsInitialized() == 0) { - return Status(StatusCode::kMDPythonInterpreterFailure, "[Internal ERROR] Python Interpreter is finalized"); - } - try { - RETURN_IF_NOT_OK(sample_ids->GetDataAsNumpy(data)); - } catch (const std::runtime_error &e) { - return Status(StatusCode::kMDPyFuncException, e.what()); - } - } - return Status::OK(); -} -#endif - -Status SamplerRT::SetNumSamples(int64_t num_samples) { - CHECK_FAIL_RETURN_UNEXPECTED( - num_samples >= 0, - "Invalid parameter, 'num_samples' must be greater than or equal to 0, but got " + std::to_string(num_samples)); - num_samples_ = num_samples; - return Status::OK(); -} - -int64_t SamplerRT::GetNumSamples() const { return num_samples_; } - -int64_t SamplerRT::GetSamplesPerTensor() const { return samples_per_tensor_; } - -int64_t SamplerRT::CalculateNumSamples(int64_t num_rows) { - int64_t child_num_rows = num_rows; - if (!child_.empty()) { - child_num_rows = child_[0]->CalculateNumSamples(num_rows); - // return -1 if child_num_rows is undetermined - if (child_num_rows == -1) { - return child_num_rows; - } - } - - return (num_samples_ > 0) ? std::min(child_num_rows, num_samples_) : child_num_rows; -} - -Status SamplerRT::SetNumRowsInDataset(int64_t num_rows) { - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows > 0, - "Invalid data, data rows of input dataset must not be less than or equal to 0, please check the input dataset."); - num_rows_ = num_rows; - return Status::OK(); -} - -Status SamplerRT::AddChild(std::shared_ptr child) { - if (child == nullptr) { - return Status::OK(); - } - - // Only samplers can be added, not any other DatasetOp. - std::shared_ptr sampler = std::dynamic_pointer_cast(child); - if (!sampler) { - std::string err_msg("[Internal ERROR] Cannot add child, child is not a sampler object."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Samplers can have at most 1 child. - if (!child_.empty()) { - std::string err_msg("[Internal ERROR] Cannot add child sampler, this sampler already has a child."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - child_.push_back(child); - - return Status::OK(); -} - -bool SamplerRT::HasChildSampler() const { return !child_.empty(); } - -Status SamplerRT::GetAssociatedChildId(int64_t *out_associated_id, int64_t id) { - RETURN_UNEXPECTED_IF_NULL(out_associated_id); - if (child_ids_.empty()) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Trying to get associated child id, but there are no child ids!"); - } - - std::shared_ptr sample_ids = child_ids_[0]; - RETURN_IF_NOT_OK(sample_ids->GetItemAt(out_associated_id, {id})); - return Status::OK(); -} -Status SamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_samples"] = num_samples_; - if (this->HasChildSampler()) { - std::vector children_args; - for (const auto &child : child_) { - nlohmann::json child_arg; - RETURN_IF_NOT_OK(child->to_json(&child_arg)); - children_args.push_back(child_arg); - } - args["child_sampler"] = children_args; - } - *out_json = args; - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h deleted file mode 100644 index 1e8a0c4f3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SAMPLER_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -namespace mindspore { -namespace dataset { -// RandomAccessOp is a base class that all data-producing leaf operators -// must inherit from if those leaf operator wish to support sampling. -class RandomAccessOp { - public: - // Sampler get number of rows in the dataset - // @param int64_t num - return number of rows for this dataset - // @return Status The status code returned - Status GetNumRowsInDataset(int64_t *num_rows) const; - - // sampler gets label , imageIds from corresponding Dataset Op, this function is unique to PK - // @param std::map> * map - // @return Status The status code returned - virtual Status GetClassIds(std::map> *map) const { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] GetClassIds needs to be override to support PK."); - } - - // default destructor - virtual ~RandomAccessOp() = default; - - /// Set num_rows - /// \param num_rows - void SetNumRows(int64_t num_rows) { num_rows_ = num_rows; } - - protected: - // The amount of rows in the dataset itself. This is the before-sampling value, the - // total count of rows. A sampler may choose to sample less than this amount. - int64_t num_rows_ = -1; -}; - -class SamplerRT { - public: - // Constructor - // @param int64_t num_samples: the user-requested number of samples ids to generate. A value of 0 - // indicates that the sampler should produce the complete set of ids. - // @param int64_t samples_per_tensor: Num of Sampler Ids to fetch via 1 GetNextSample call - SamplerRT(int64_t num_samples, int64_t samples_per_tensor); - - SamplerRT(const SamplerRT &s) : SamplerRT(s.num_samples_, s.samples_per_tensor_) {} - - // Copy assignment operator - SamplerRT &operator=(const SamplerRT &other) { - num_samples_ = other.num_samples_; - samples_per_tensor_ = other.samples_per_tensor_; - return *this; - } - - // default destructor - virtual ~SamplerRT() = default; - - // Get a list of sample ids. - // @note It is Sampler responsibility to make sure that the id is not out of bound. - // @param TensorRow to be returned to StorageOp - // @param int32_t workerId - not meant to be used - // @return Status The status code returned - virtual Status GetNextSample(TensorRow *out) = 0; - -// This function only called by python layer. Not needed by Android. -#ifdef ENABLE_PYTHON - // return all ids in one epoch as a numpy array, then call reset - Status GetAllIdsThenReset(py::array *data); -#endif - - /// \brief Reset for next epoch. - /// \note If failover_reset is set, any override of this function must support the scenario where consecutive calls to - /// it are executed successfully (to prepare the sampler for a specific epoch, including updating any random - /// generator's internal state) - /// \param[in] failover_reset - A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - virtual Status ResetSampler(const bool failover_reset = false) = 0; - - // first handshake between leaf source op and Sampler. This func will determine the amount of data - // in the dataset that we can sample from. - // @param op - leaf op pointer, pass in so Sampler can ask it about how much data there is - // @param reset_count - reset the random generator these many times (used in fast_recovery mode of reset) - // @return status error code - virtual Status HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count = 0); - - // initialize sampler and perform checks on certain vars - virtual Status InitSampler() { return Status::OK(); } - - // setter for num samples - // @param num_samples - the number of samples to assign. - // @return status error code - Status SetNumSamples(int64_t num_samples); - - // getter for num samples - // @return number of samples - int64_t GetNumSamples() const; - - virtual int64_t GetSamplesPerTensor() const; - - // Calculate num samples. Unlike GetNumSamples, it is not a getter and doesn't necessarily return the value of - // num_samples_ - // @return number of samples, return -1 if sampler cannot determine this value (e.g. PKSampler) - virtual int64_t CalculateNumSamples(int64_t num_rows); - - // setter for num or records in the dataset - // @param num_rows - the number of records - // @return status error code - Status SetNumRowsInDataset(int64_t num_rows); - - // Adds a sampler to become our child. - // @param std::shared_ptr - The sampler to add as a child. - // @return Status The status code returned - Status AddChild(std::shared_ptr child); - - // A helper function to create an int64_t 1-D Tensor specifically used to hold sampleIds for Sampler - // @param std::shared_ptr* sampleIds - // @param int64_t numElements - must be a non 0 number - // @return Status The status code returned - Status CreateSamplerTensor(std::shared_ptr *sample_ids, int64_t num_elements); - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - virtual void SamplerPrint(std::ostream &out, bool show_all) const; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // @param out - reference to the output stream being overloaded - // @param sampler - reference to teh sampler to print - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const SamplerRT &sampler) { - sampler.SamplerPrint(out, false); - return out; - } - - // Checks if this sampler has a child sampler. - // @return - tre if there is a child sampler, false otherwise. - bool HasChildSampler() const; - - // Uses id as an index for the list of ids generated by the child sampler, and gets the - // associated id. - // @param int64_t* out_associated_id - Out parameter, contains the associated id. - // @param int64_t id - The id used as an index to get the associated child id. - // @return Status The status code returned - Status GetAssociatedChildId(int64_t *out_associated_id, int64_t id); - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - virtual Status to_json(nlohmann::json *out_json); - - protected: - // Number of rows of data from the place this sampler is sampling from. If this sampler - // has a child sampler, num_rows_ is the number of ids the child sampler will - // output. Otherwise, num_rows_ is the number of rows in the dataset. - int64_t num_rows_; - - // The user may want to sample less than the full amount of data. num_samples_ reduces the number - // of id's returned as request by the user. Derived classes will choose how to sample the smaller - // amount. - int64_t num_samples_; - - bool is_initialized; - int64_t samples_per_tensor_; - std::unique_ptr col_desc_; - std::vector> child_; // Child nodes - TensorRow child_ids_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.cc deleted file mode 100644 index 7195d557b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.cc +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" - -#include -#include -#include - -namespace mindspore { -namespace dataset { -SequentialSamplerRT::SequentialSamplerRT(int64_t start_index, int64_t num_samples, int64_t samples_per_tensor) - : SamplerRT(num_samples, samples_per_tensor), - current_index_(start_index), - start_index_(start_index), - index_produced_(0) {} - -Status SequentialSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (index_produced_ > num_samples_) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Sampler index must be less than or equal to num_samples(total rows in dataset), but got:" + - std::to_string(index_produced_) + ", num_samples_: " + std::to_string(num_samples_)); - } else if (index_produced_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr sampleIds; - - // Compute how many ids are left to pack, and pack this amount into a new Tensor. Respect the setting for - // samples per Tensor though. - int64_t remaining_ids = num_samples_ - index_produced_; - int64_t num_elements = std::min(remaining_ids, samples_per_tensor_); - - RETURN_IF_NOT_OK(CreateSamplerTensor(&sampleIds, num_elements)); - auto idPtr = sampleIds->begin(); - for (int64_t i = 0; i < num_elements; i++) { - int64_t sampled_id = current_index_; - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&sampled_id, sampled_id)); - } - - *idPtr = sampled_id; - current_index_++; // Move the current id to the next one in the sequence - ++idPtr; - } - - index_produced_ += num_elements; // Count the packed ids towards our overall sample count - - (*out) = {sampleIds}; - } - return Status::OK(); -} - -Status SequentialSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - CHECK_FAIL_RETURN_UNEXPECTED(start_index_ >= 0, - "Invalid parameter, start_index must be greater than or equal to 0, but got " + - std::to_string(start_index_) + ".\n"); - CHECK_FAIL_RETURN_UNEXPECTED(start_index_ < num_rows_ || (num_rows_ == 0 && start_index_ == 0), - "Invalid parameter, start_index must be less than num_rows, but got start_index: " + - std::to_string(start_index_) + ", num_rows: " + std::to_string(num_rows_) + ".\n"); - CHECK_FAIL_RETURN_UNEXPECTED(num_samples_ >= 0, - "Invalid parameter, num_samples must be greater than or equal to 0, but got " + - std::to_string(num_samples_) + ".\n"); - // Adjust the num_samples count based on the range of ids we are sequencing. If num_samples is 0, we sample - // the entire set. If it's non-zero, we will implicitly cap the amount sampled based on available data. - int64_t available_row_count = num_rows_ - start_index_; - if (num_samples_ == 0 || num_samples_ > available_row_count) { - num_samples_ = available_row_count; - } - CHECK_FAIL_RETURN_UNEXPECTED((num_samples_ > 0 && samples_per_tensor_ > 0) || num_samples_ == 0, - "Invalid parameter, samples_per_tensor(num_samplers) must be greater than 0, but got " + - std::to_string(samples_per_tensor_)); - samples_per_tensor_ = samples_per_tensor_ > num_samples_ ? num_samples_ : samples_per_tensor_; - - is_initialized = true; - return Status::OK(); -} - -Status SequentialSamplerRT::ResetSampler(const bool failover_reset) { - CHECK_FAIL_RETURN_UNEXPECTED(failover_reset || index_produced_ == num_samples_, - "[Internal ERROR] ResetSampler() called early or late."); - current_index_ = start_index_; - index_produced_ = 0; - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -int64_t SequentialSamplerRT::CalculateNumSamples(int64_t num_rows) { - // Holds the number of rows available for Sequential sampler. It can be the rows passed from its child sampler or the - // num_rows from the dataset - int64_t child_num_rows = num_rows; - if (!child_.empty()) { - child_num_rows = child_[0]->CalculateNumSamples(num_rows); - // return -1 if child_num_rows is undetermined - if (child_num_rows == -1) { - return child_num_rows; - } - } - int64_t num_samples = (num_samples_ > 0) ? std::min(child_num_rows, num_samples_) : child_num_rows; - // For this sampler we need to take start_index into account. Because for example in the case we are given n rows - // and start_index != 0 and num_samples >= n then we can't return all the n rows. - if (child_num_rows - start_index_ <= 0) { - return 0; - } - if (child_num_rows - start_index_ < num_samples) { - num_samples = child_num_rows - start_index_ > num_samples ? num_samples : num_samples - start_index_; - } - return num_samples; -} - -void SequentialSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: SequentialSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info - out << "\nStart index: " << start_index_; - } -} - -Status SequentialSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "SequentialSampler"; - args["start_index"] = start_index_; - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h deleted file mode 100644 index 147e97163..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SEQUENTIAL_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SEQUENTIAL_SAMPLER_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -class SequentialSamplerRT : public SamplerRT { - public: - // Constructor - // @param start_index - The starting index value - // @param num_samples - The number of samples to draw. A value of 0 indicates the sampler should produce the - // full amount of ids from the dataset - // @param int64_t samples_per_tensor - Num of Sampler Ids to fetch via 1 GetNextSample call - SequentialSamplerRT(int64_t start_index, int64_t num_samples, - int64_t samples_per_tensor = std::numeric_limits::max()); - - // Destructor. - ~SequentialSamplerRT() override = default; - - // init sampler, called by python - Status InitSampler() override; - - /// \brief Reset for next epoch. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset) override; - - // Op calls this to get next Sample that contains all the sampleIds - // @param TensorRow to be returned to corresponding Dataset Op - // @param int32_t workerId - not meant to be used - // @return Status The status code returned - Status GetNextSample(TensorRow *out) override; - - /// \brief Recursively calls this function on its children to get the actual number of samples on a tree of samplers - /// \note This is not a getter for num_samples_. For example, if num_samples_ is 0 or if it's smaller than num_rows, - /// then num_samples_ is not returned at all. - /// \param[in] num_rows The total number of rows in the dataset - /// \return int64_t Calculated number of samples - int64_t CalculateNumSamples(int64_t num_rows) override; - - // Printer for debugging purposes. - // @param out - output stream to write to - // @param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - protected: - int64_t current_index_; // The id sequencer. Each new id increments from this - int64_t start_index_; // The starting id. current_id_ begins from here. - int64_t index_produced_; // An internal counter that tracks how many ids have been produced -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SEQUENTIAL_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc deleted file mode 100644 index e54fc11df..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h" - -namespace mindspore { -namespace dataset { -SkipFirstEpochSamplerRT::SkipFirstEpochSamplerRT(int64_t start_index, int64_t num_samples, int64_t samples_per_tensor) - : SequentialSamplerRT(start_index, num_samples, samples_per_tensor), sample_need_to_skip_(start_index) {} - -Status SkipFirstEpochSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (index_produced_ > num_samples_) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Sampler index must be less than or equal to num_samples(total rows in dataset), but got:" + - std::to_string(index_produced_) + ", num_samples_: " + std::to_string(num_samples_)); - } else if (index_produced_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - int64_t num_index_valid; - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - CHECK_FAIL_RETURN_UNEXPECTED(!child_ids_.empty(), - "SkipFirstEpochSampler: got empty output index from child sampler."); - num_index_valid = child_ids_[0]->Size(); - while (sample_need_to_skip_ >= num_index_valid) { - sample_need_to_skip_ -= num_index_valid; - current_index_ += num_index_valid; - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - CHECK_FAIL_RETURN_UNEXPECTED(!child_ids_.empty(), - "SkipFirstEpochSampler: got empty output index from child sampler."); - num_index_valid = child_ids_[0]->Size(); - } - CHECK_FAIL_RETURN_UNEXPECTED( - child_ids_[0]->type().value() == DataType::Type::DE_INT64, - "SkipFirstEpochSampler: output index from child sampler must be of int64 type, but got: " + - child_ids_[0]->type().ToString()); - } else { - // calculate how many ids left to produce - num_index_valid = num_rows_ - index_produced_; - } - - // Compute how many ids are left to pack, and pack this amount into a new Tensor. - // Respect the setting for samples per Tensor though. - int64_t remaining_ids = num_index_valid - sample_need_to_skip_; - int64_t num_elements = std::min(remaining_ids, samples_per_tensor_); - - std::shared_ptr sampleIds; - RETURN_IF_NOT_OK(CreateSamplerTensor(&sampleIds, num_elements)); - - if (HasChildSampler()) { - std::string err_msg = "Failed to copy full sample ids into child sampler."; - int64_t copy_data_length = num_elements * sizeof(int64_t); - if (copy_data_length < SECUREC_MEM_MAX_LEN) { - int ret_code = - memcpy_s(sampleIds->GetMutableBuffer(), copy_data_length, - child_ids_[0]->GetMutableBuffer() + sample_need_to_skip_ * sizeof(int64_t), copy_data_length); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == EOK, err_msg + " errno: " + std::to_string(ret_code)); - } else { - auto dest = - std::memcpy(sampleIds->GetMutableBuffer(), - child_ids_[0]->GetMutableBuffer() + sample_need_to_skip_ * sizeof(int64_t), copy_data_length); - CHECK_FAIL_RETURN_UNEXPECTED(dest == sampleIds->GetMutableBuffer(), err_msg); - } - current_index_ += num_elements; - } else { - auto idPtr = sampleIds->begin(); - for (int64_t i = 0; i < num_elements; i++) { - *idPtr = current_index_; - current_index_++; // Move the current id to the next one in the sequence - ++idPtr; - } - } - index_produced_ += num_elements; // Count the packed ids towards our overall sample count - sample_need_to_skip_ = 0; - (*out) = {sampleIds}; - } - return Status::OK(); -} - -Status SkipFirstEpochSamplerRT::ResetSampler(const bool failover_reset) { - // This is a special sampler for Failover Reset, its internal state should - // not reset when failover_reset is set to true. - if (!failover_reset) { - if (index_produced_ != num_samples_) { - std::string err_msg = - "[Internal ERROR] ResetSampler() called early or late. index_produced_: " + std::to_string(index_produced_) + - " num_samples_: " + std::to_string(num_samples_); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - current_index_ = 0; - index_produced_ = 0; - - if (!first_epoch_done_) { - num_samples_ += start_index_; - start_index_ = 0; - samples_per_tensor_ = num_samples_; - first_epoch_done_ = true; - } - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -int64_t SkipFirstEpochSamplerRT::CalculateNumSamples(const int64_t num_rows) { return -1; } - -Status SkipFirstEpochSamplerRT::HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count) { - RETURN_UNEXPECTED_IF_NULL(op); - std::shared_ptr child_sampler; - if (HasChildSampler()) { - child_sampler = std::dynamic_pointer_cast(child_[0]); - if (!child_sampler) { - std::string err_msg("[Internal ERROR] Cannot handshake, child is not a sampler object."); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Handshake and init child first. - RETURN_IF_NOT_OK(child_sampler->HandshakeRandomAccessOp(op)); - } - - CHECK_FAIL_RETURN_UNEXPECTED(op != nullptr, "[Internal ERROR] RandomAccessOp init failed, as it is nullptr."); - - // If there's a child sampler, set the row count to be it's sample count - if (HasChildSampler()) { - auto distributed_sampler = std::dynamic_pointer_cast(child_sampler); - if (distributed_sampler) { - num_rows_ = child_sampler->GetSamplesPerTensor(); - } else { - num_rows_ = child_sampler->GetNumSamples(); - } - } else { - RETURN_IF_NOT_OK(op->GetNumRowsInDataset(&num_rows_)); - } - - // It's up to the derived class to check the validity of the two args - // Because some sampler only needs one of the arg (weighted_random_sampler) - RETURN_IF_NOT_OK(InitSampler()); // init sampler after callback - // Move forward sampler's random generator if resetting the pipeline in fast_recovery mode - if (GlobalContext::config_manager()->fast_recovery()) { - for (auto i = 0; i < reset_count; i++) { - RETURN_IF_NOT_OK(ResetSampler(true)); // failover_reset = true - } - } - return Status::OK(); -} - -void SkipFirstEpochSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: SkipFirstEpochSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info - out << "\nStart index: " << start_index_; - out << "\nFirst epoch done: " << first_epoch_done_; - out << "\nCurrent index: " << current_index_; - out << "\nIndex produced:" << index_produced_; - } -} - -Status SkipFirstEpochSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "SkipFirstEpochSampler"; - args["start_index"] = start_index_; - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h deleted file mode 100644 index a6384832d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SKIP_FIRST_EPOCH_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SKIP_FIRST_EPOCH_SAMPLER_H_ - -#include - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" - -namespace mindspore { -namespace dataset { -class SkipFirstEpochSamplerRT : public SequentialSamplerRT { - public: - // Constructor - SkipFirstEpochSamplerRT(int64_t start_index, int64_t num_samples, - int64_t samples_per_tensor = std::numeric_limits::max()); - - // Destructor. - ~SkipFirstEpochSamplerRT() override = default; - - Status GetNextSample(TensorRow *out) override; - - /// \brief Reset for next epoch. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - /// \brief Gets the number of samples available - /// \note Since this sampler returns different number of samples in the first epoch (compared to other epochs), this - /// function always returns -1 - /// \param[in] num_rows The total number of rows in the dataset - /// \return int64_t Calculated number of samples (always -1) - int64_t CalculateNumSamples(const int64_t num_rows) override; - - Status HandshakeRandomAccessOp(const RandomAccessOp *op, const int32_t reset_count = 0) override; - - // Printer for debugging purposes. - // @param out - output stream to write to - // @param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - int64_t sample_need_to_skip_; - bool first_epoch_done_ = false; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SKIP_FIRST_EPOCH_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.cc deleted file mode 100644 index fb69cd44a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.cc +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -// Constructor. -SubsetRandomSamplerRT::SubsetRandomSamplerRT(const std::vector &indices, int64_t num_samples, - int64_t samples_per_tensor) - : SubsetSamplerRT(indices, num_samples, samples_per_tensor) {} - -// Initialized this Sampler. -Status SubsetRandomSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - - // Initialize random generator with seed from config manager - rand_gen_.seed(GetSeed()); - - // num_samples_ could be smaller than the total number of input id's. - // We will shuffle the full set of id's, but only select the first num_samples_ of them later. - std::shuffle(indices_.begin(), indices_.end(), rand_gen_); - - return SubsetSamplerRT::InitSampler(); -} - -// Reset the internal variable to the initial state. -Status SubsetRandomSamplerRT::ResetSampler(const bool failover_reset) { - // Randomized the indices again. - rand_gen_.seed(GetSeed()); - std::shuffle(indices_.begin(), indices_.end(), rand_gen_); - - return SubsetSamplerRT::ResetSampler(failover_reset); -} - -void SubsetRandomSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: SubsetRandomSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} - -Status SubsetRandomSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SubsetSamplerRT::to_json(&args)); - args["sampler_name"] = "SubsetRandomSampler"; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h deleted file mode 100644 index 5b295afc1..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_RANDOM_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_RANDOM_SAMPLER_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h" - -namespace mindspore { -namespace dataset { -/// Randomly samples elements from a given list of indices, without replacement. -class SubsetRandomSamplerRT : public SubsetSamplerRT { - public: - /// Constructor. - /// \param indices List of indices from where we will randomly draw samples. - /// \param num_samples The number of samples to draw. 0 for the full amount. - /// \param samples_per_tensor The number of ids we draw on each call to GetNextSample(). - /// When samples_per_tensor=0, GetNextSample() will draw all the sample ids and return them at once. - SubsetRandomSamplerRT(const std::vector &indices, int64_t num_samples, - int64_t samples_per_tensor = std::numeric_limits::max()); - - /// Destructor. - ~SubsetRandomSamplerRT() = default; - - /// Initialize the sampler. - /// \return Status - Status InitSampler() override; - - /// \brief Reset the internal variable(s) to the initial state and reshuffle the indices. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - /// Printer for debugging purposes. - /// \param out - output stream to write to - /// \param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - // A random number generator. - std::mt19937 rand_gen_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_RANDOM_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.cc deleted file mode 100644 index 3b6968524..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.cc +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h" - -#include -#include - -namespace mindspore { -namespace dataset { -// Constructor. -SubsetSamplerRT::SubsetSamplerRT(const std::vector &indices, int64_t num_samples, int64_t samples_per_tensor) - : SamplerRT(num_samples, samples_per_tensor), indices_(indices), sample_id_(0) {} - -// Initialized this Sampler. -Status SubsetSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows_ > 0, "Invalid parameter, num_rows must be greater than 0, but got " + std::to_string(num_rows_) + ".\n"); - - // Special value of 0 for num_samples means that the user wants to sample the entire set of data. - // In this case, the id's are provided by the user. Cap the num_samples on the number of id's given. - if (num_samples_ == 0 || num_samples_ > static_cast(indices_.size())) { - num_samples_ = static_cast(indices_.size()); - } - - if (samples_per_tensor_ > num_samples_) { - samples_per_tensor_ = num_samples_; - } - - is_initialized = true; - return Status::OK(); -} - -// Reset the internal variable to the initial state. -Status SubsetSamplerRT::ResetSampler(const bool failover_reset) { - // Reset the internal counters. - sample_id_ = 0; - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -// Get the sample ids. -Status SubsetSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - // All samples have been drawn - if (sample_id_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr outputIds; - - int64_t last_id = sample_id_ + samples_per_tensor_; - // Handling the return all samples at once, and when last draw is not a full batch. - if (last_id > num_samples_) { - last_id = num_samples_; - } - - // Allocate tensor - RETURN_IF_NOT_OK(CreateSamplerTensor(&outputIds, last_id - sample_id_)); - - // Initialize tensor - auto id_ptr = outputIds->begin(); - while (sample_id_ < last_id) { - if (indices_[sample_id_] >= num_rows_ || indices_[sample_id_] < 0) { - std::string err_msg = "Sample ID (" + std::to_string(indices_[sample_id_]) + - ") is out of bound, expected range [0, " + std::to_string(num_rows_ - 1) + "]"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - int64_t sampled_id = ((indices_[sample_id_] % num_rows_) + num_rows_) % num_rows_; - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&sampled_id, sampled_id)); - } - - *id_ptr = sampled_id; - ++id_ptr; - sample_id_++; - } - - (*out) = {outputIds}; - } - - return Status::OK(); -} - -void SubsetSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: SubsetSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} - -Status SubsetSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "SubsetSampler"; - args["indices"] = indices_; - - *out_json = args; - return Status::OK(); -} - -int64_t SubsetSamplerRT::CalculateNumSamples(int64_t num_rows) { - int64_t child_num_rows = num_rows; - if (!child_.empty()) { - child_num_rows = child_[0]->CalculateNumSamples(num_rows); - // return -1 if child_num_rows is undetermined - if (child_num_rows == -1) { - return child_num_rows; - } - } - int64_t res = (num_samples_ > 0) ? std::min(child_num_rows, num_samples_) : child_num_rows; - res = std::min(res, static_cast(indices_.size())); - return res; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h deleted file mode 100644 index 1e73e34b9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_SAMPLER_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -/// Samples elements from a given list of indices. -class SubsetSamplerRT : public SamplerRT { - public: - /// Constructor. - /// \param indices List of indices. - /// \param num_samples The number of elements to sample. 0 for the full amount. - /// \param samples_per_tensor The number of ids we draw on each call to GetNextSample(). - /// When samples_per_tensor=0, GetNextSample() will draw all the sample ids and return them at once. - SubsetSamplerRT(const std::vector &indices, int64_t num_samples, - std::int64_t samples_per_tensor = std::numeric_limits::max()); - - /// Destructor. - ~SubsetSamplerRT() = default; - - /// Initialize the sampler. - /// \return Status - Status InitSampler() override; - - /// \brief Reset the internal variable(s) to the initial state and reshuffle the indices. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - /// Get the sample ids. - /// \param[out] TensorRow where the sample ids will be placed. - /// @note the sample ids (int64_t) will be placed in one Tensor - Status GetNextSample(TensorRow *out) override; - - /// Printer for debugging purposes. - /// \param out - output stream to write to - /// \param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// Calculate num samples. Unlike GetNumSamples, it is not a getter and doesn't necessarily return the value of - /// num_samples_ - /// \param num_rows the size of the dataset this sampler will be applied to. - /// \return number of samples - int64_t CalculateNumSamples(int64_t num_rows) override; - - protected: - /// A list of indices (already randomized in constructor). - std::vector indices_; - - private: - /// Current sample id. - int64_t sample_id_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_SUBSET_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.cc deleted file mode 100644 index 455929c2d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.cc +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -// Constructor. -WeightedRandomSamplerRT::WeightedRandomSamplerRT(const std::vector &weights, int64_t num_samples, - bool replacement, int64_t samples_per_tensor) - : SamplerRT(num_samples, samples_per_tensor), weights_(weights), replacement_(replacement), sample_id_(0) {} - -// Initialized this Sampler. -Status WeightedRandomSamplerRT::InitSampler() { - if (is_initialized) { - return Status::OK(); - } - // Special value of 0 for num_samples means that the user wants to sample the entire set of data. - // If the user asked to sample more rows than exists in the dataset, adjust the num_samples accordingly. - if (num_samples_ == 0 || num_samples_ > num_rows_) { - num_samples_ = num_rows_; - } - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0 && num_samples_, - "[Internal ERROR] num_samples and num_rows must be greater than 0, but got num_rows: " + - std::to_string(num_rows_) + ", num_samples: " + std::to_string(num_samples_)); - CHECK_FAIL_RETURN_UNEXPECTED(samples_per_tensor_ > 0, - "Invalid parameter, samples_per_tensor(num_samples) must be greater than 0, but got " + - std::to_string(samples_per_tensor_) + ".\n"); - - if (weights_.size() > static_cast(num_rows_)) { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, size of sample weights must be less than or equal to num of data, " - "otherwise might cause generated id out of bound or other errors, but got weight size: " + - std::to_string(weights_.size()) + ", num of data: " + std::to_string(num_rows_)); - } - if (!replacement_ && (weights_.size() < static_cast(num_samples_))) { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, without replacement, weight size must be greater than or equal to num_samples, " - "but got weight size: " + - std::to_string(weights_.size()) + ", num_samples: " + std::to_string(num_samples_)); - } - - // Initialize random generator with seed from config manager - rand_gen_.seed(GetSeed()); - - samples_per_tensor_ = (samples_per_tensor_ > num_samples_) ? num_samples_ : samples_per_tensor_; - - if (!replacement_) { - exp_dist_ = std::make_unique>(1); - InitOnePassSampling(); - } else { - discrete_dist_ = std::make_unique>(weights_.begin(), weights_.end()); - } - - is_initialized = true; - return Status::OK(); -} - -// Initialized the computation for generating weighted random numbers without replacement using onepass method. -void WeightedRandomSamplerRT::InitOnePassSampling() { - exp_dist_->reset(); - onepass_ids_.clear(); - std::vector> val_idx; - for (size_t i = 0; i < weights_.size(); i++) { - val_idx.emplace_back(std::make_pair((*exp_dist_)(rand_gen_) / weights_[i], i)); - } - - // Partial sort the first `numSamples` elements. - std::partial_sort(val_idx.begin(), val_idx.begin() + num_samples_, val_idx.end()); - for (int64_t i = 0; i < num_samples_; i++) { - onepass_ids_.push_back(val_idx[i].second); - } -} - -// Reset the internal variable to the initial state and reshuffle the indices. -Status WeightedRandomSamplerRT::ResetSampler(const bool failover_reset) { - sample_id_ = 0; - rand_gen_.seed(GetSeed()); - if (!replacement_) { - InitOnePassSampling(); - } else { - discrete_dist_->reset(); - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->ResetSampler(failover_reset)); - } - - return Status::OK(); -} - -// Get the sample ids. -Status WeightedRandomSamplerRT::GetNextSample(TensorRow *out) { - RETURN_UNEXPECTED_IF_NULL(out); - if (weights_.size() > static_cast(num_rows_)) { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, size of sample weights must be less than or equal to num of data, " - "otherwise might cause generated id out of bound or other errors, but got weight size: " + - std::to_string(weights_.size()) + ", num of data: " + std::to_string(num_rows_)); - } - - if (!replacement_ && (weights_.size() < static_cast(num_samples_))) { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, without replacement, weight size must be greater than or equal to num_samples, " - "but got weight size: " + - std::to_string(weights_.size()) + ", num_samples: " + std::to_string(num_samples_)); - } - - if (sample_id_ == num_samples_) { - (*out) = TensorRow(TensorRow::kFlagEOE); - } else { - if (HasChildSampler()) { - RETURN_IF_NOT_OK(child_[0]->GetNextSample(&child_ids_)); - } - - std::shared_ptr outputIds; - - int64_t last_id = sample_id_ + samples_per_tensor_; - // Handling the return all samples at once, and when last draw is not a full batch. - if (last_id > num_samples_) { - last_id = num_samples_; - } - - // Allocate tensor. - RETURN_IF_NOT_OK(CreateSamplerTensor(&outputIds, last_id - sample_id_)); - - // Initialize tensor. - auto id_ptr = outputIds->begin(); - // Assign the data to tensor element. - while (sample_id_ < last_id) { - int64_t genId; - if (replacement_) { - genId = (*discrete_dist_)(rand_gen_); - } else { - // Draw sample without replacement. - genId = onepass_ids_.front(); - onepass_ids_.pop_front(); - } - - if (genId >= num_rows_) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Generated indice is out of bound, expect range [0, num_data-1], got indice: " + - std::to_string(genId) + ", num_data: " + std::to_string(num_rows_ - 1)); - } - - if (HasChildSampler()) { - RETURN_IF_NOT_OK(GetAssociatedChildId(&genId, genId)); - } - - *id_ptr = genId; - ++id_ptr; - sample_id_++; - } - - (*out) = {outputIds}; - } - - return Status::OK(); -} - -void WeightedRandomSamplerRT::SamplerPrint(std::ostream &out, bool show_all) const { - out << "\nSampler: WeightedRandomSampler"; - if (show_all) { - // Call the super class for displaying any common detailed info - SamplerRT::SamplerPrint(out, show_all); - // Then add our own info if any - } -} - -Status WeightedRandomSamplerRT::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerRT::to_json(&args)); - args["sampler_name"] = "WeightedRandomSampler"; - args["weights"] = weights_; - args["replacement"] = replacement_; - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h deleted file mode 100644 index 406e1ca5c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_WEIGHTED_RANDOM_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SAMPLER_WEIGHTED_RANDOM_SAMPLER_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -namespace mindspore { -namespace dataset { -// Samples elements from id `0, 1, ..., weights.size()-1` with given probabilities (weights). -class WeightedRandomSamplerRT : public SamplerRT { - public: - // Constructor. - // @param weights A lift of sample weights. - // @param num_samples Number of samples to be drawn. - // @param replacement Determine if samples are drawn with/without replacement. - // @param samples_per_tensor The number of ids we draw on each call to GetNextSample(). - // When samples_per_tensor=0, GetNextSample() will draw all the sample ids and return them at once. - WeightedRandomSamplerRT(const std::vector &weights, int64_t num_samples, bool replacement, - int64_t samples_per_tensor = std::numeric_limits::max()); - - // Destructor. - ~WeightedRandomSamplerRT() = default; - - // Initialize the sampler. - // @param op (Not used in this sampler) - // @return Status - Status InitSampler() override; - - /// \brief Reset the internal variable(s) to the initial state and reshuffle the indices. - /// \param[in] failover_reset A boolean to show whether we are resetting the pipeline - /// \return Status The status code returned - Status ResetSampler(const bool failover_reset = false) override; - - // Get the sample ids. - // @param[out] TensorRow where the sample ids will be placed. - // @note the sample ids (int64_t) will be placed in one Tensor - Status GetNextSample(TensorRow *out) override; - - // Printer for debugging purposes. - // @param out - output stream to write to - // @param show_all - bool to show detailed vs summary - void SamplerPrint(std::ostream &out, bool show_all) const override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - // A list of weights for each sample. - std::vector weights_; - - // A flag indicating if samples are drawn with/without replacement. - bool replacement_; - - // Current sample id. - int64_t sample_id_; - - // Random engine and device - std::mt19937 rand_gen_; - - // Discrete distribution for generating weighted random numbers with replacement. - std::unique_ptr> discrete_dist_; - - // Exponential distribution for generating weighted random numbers without replacement. - // based on "Accelerating weighted random sampling without replacement" by Kirill Muller. - std::unique_ptr> exp_dist_; - - // Initialized the computation for generating weighted random numbers without replacement - // using onepass method. - void InitOnePassSampling(); - - // Store the random weighted ids generated by onepass method in `InitOnePassSampling` - std::deque onepass_ids_; -}; -} // namespace dataset -} // namespace mindspore - -#endif diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.cc deleted file mode 100644 index eec71b2bf..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.cc +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/ms_utils.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -SBUOp::SBUOp(const std::string &folder_path, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, int32_t num_workers, int32_t queue_size) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(folder_path), - decode_(decode), - url_path_(""), - caption_path_(""), - image_folder_(""), - data_schema_(std::move(data_schema)) {} - -void SBUOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nSBU directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// Load 1 TensorRow (image, caption) using 1 SBUImageCaptionPair. -Status SBUOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - - SBUImageCaptionPair image_caption_pair = image_caption_pairs_[row_id]; - Path path = image_caption_pair.first; - - std::shared_ptr image, caption; - RETURN_IF_NOT_OK(ReadImageToTensor(path.ToString(), &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(image_caption_pair.second, &caption)); - - (*trow) = TensorRow(row_id, {std::move(image), std::move(caption)}); - trow->setPath({path.ToString()}); - return Status::OK(); -} - -Status SBUOp::ReadImageToTensor(const std::string &path, std::shared_ptr *tensor) const { - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); - if (decode_ == true) { - Status rc = Decode(*tensor, tensor); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("Invalid image, failed to decode image:" + path + - ", the image is damaged or permission denied."); - } - } - return Status::OK(); -} - -Status SBUOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < static_cast(data_schema_->NumColumns()); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status SBUOp::CountTotalRows(const std::string &dir, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("caption", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connector_size = cfg->op_connector_size(); - - // compat does not affect the count result, so set it to true default. - auto op = std::make_shared(dir, true, std::move(schema), std::move(sampler), num_workers, op_connector_size); - - // the logic of counting the number of samples - RETURN_IF_NOT_OK(op->PrepareData()); - *count = op->image_caption_pairs_.size(); - - return Status::OK(); -} - -Status SBUOp::PrepareData() { - const Path url_file_name("SBU_captioned_photo_dataset_urls.txt"); - const Path caption_file_name("SBU_captioned_photo_dataset_captions.txt"); - const Path image_folder_name("sbu_images"); - auto real_folder_path = FileUtils::GetRealPath(common::SafeCStr(folder_path_)); - CHECK_FAIL_RETURN_UNEXPECTED(real_folder_path.has_value(), "Get real path failed: " + folder_path_); - Path root_dir(real_folder_path.value()); - - url_path_ = root_dir / url_file_name; - CHECK_FAIL_RETURN_UNEXPECTED( - url_path_.Exists() && !url_path_.IsDirectory(), - "Invalid file, SBU url file: " + url_path_.ToString() + " does not exist or is a directory."); - MS_LOG(INFO) << "SBU operator found url file " << url_path_.ToString() << "."; - - caption_path_ = root_dir / caption_file_name; - CHECK_FAIL_RETURN_UNEXPECTED( - caption_path_.Exists() && !caption_path_.IsDirectory(), - "Invalid file, SBU caption file: " + caption_path_.ToString() + " does not exist or is a directory."); - MS_LOG(INFO) << "SBU operator found caption file " << caption_path_.ToString() << "."; - - image_folder_ = root_dir / image_folder_name; - CHECK_FAIL_RETURN_UNEXPECTED( - image_folder_.Exists() && image_folder_.IsDirectory(), - "Invalid folder, SBU image folder:" + image_folder_.ToString() + " does not exist or is not a directory."); - MS_LOG(INFO) << "SBU operator found image folder " << image_folder_.ToString() << "."; - - std::ifstream url_file_reader; - std::ifstream caption_file_reader; - - url_file_reader.open(url_path_.ToString(), std::ios::in); - caption_file_reader.open(caption_path_.ToString(), std::ios::in); - - CHECK_FAIL_RETURN_UNEXPECTED(url_file_reader.is_open(), "Invalid file, failed to open " + url_path_.ToString() + - ": the SBU url file is permission denied."); - CHECK_FAIL_RETURN_UNEXPECTED( - caption_file_reader.is_open(), - "Invalid file, failed to open " + caption_path_.ToString() + ": the SBU caption file is permission denied."); - - Status rc = GetAvailablePairs(url_file_reader, caption_file_reader); - url_file_reader.close(); - caption_file_reader.close(); - if (rc.IsError()) { - return rc; - } - - return Status::OK(); -} - -Status SBUOp::GetAvailablePairs(std::ifstream &url_file_reader, std::ifstream &caption_file_reader) { - std::string url_line; - std::string caption_line; - int64_t line_num = 0; - - while (std::getline(url_file_reader, url_line) && std::getline(caption_file_reader, caption_line)) { - CHECK_FAIL_RETURN_UNEXPECTED( - (url_line.empty() && caption_line.empty()) || (!url_line.empty() && !caption_line.empty()), - "Invalid data, SBU url: " + url_path_.ToString() + " and caption file: " + caption_path_.ToString() + - " load empty data at line: " + std::to_string(line_num) + "."); - if (!url_line.empty() && !caption_line.empty()) { - line_num++; - RETURN_IF_NOT_OK(this->ParsePair(url_line, caption_line)); - } - } - - image_caption_pairs_.shrink_to_fit(); - - CHECK_FAIL_RETURN_UNEXPECTED(image_caption_pairs_.size() > 0, - "Invalid data, no valid images in " + image_folder_.ToString() + ", check SBU dataset."); - - // base field of RandomAccessOp - num_rows_ = static_cast(image_caption_pairs_.size()); - - return Status::OK(); -} - -Status SBUOp::ParsePair(const std::string &url, const std::string &caption) { - constexpr int64_t max_url_length = 23; - CHECK_FAIL_RETURN_UNEXPECTED(url.length() > max_url_length, "Invalid url in " + url_path_.ToString() + ": " + url); - std::string image_name = url.substr(23); - RETURN_IF_NOT_OK(this->ReplaceAll(&image_name, "/", "_")); - - Path image_path = image_folder_ / Path(image_name); - if (image_path.Exists() && !image_path.IsDirectory()) { - // rstrip caption - image_caption_pairs_.emplace_back(std::make_pair(image_path, caption.substr(0, caption.find_last_not_of(" ") + 1))); - } - - return Status::OK(); -} - -Status SBUOp::ReplaceAll(std::string *str, const std::string &from, const std::string &to) const { - size_t pos = 0; - while ((pos = str->find(from, pos)) != std::string::npos) { - str->replace(pos, from.length(), to); - pos += to.length(); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h deleted file mode 100644 index 6447f2de3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SBU_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SBU_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { - -using SBUImageCaptionPair = std::pair; - -class SBUOp : public MappableLeafOp { - public: - // Constructor. - // @param const std::string &folder_path - dir directory of SBU data file. - // @param bool decode - whether to decode images. - // @param std::unique_ptr data_schema - the schema of the SBU dataset. - // @param std::unique_ptr sampler - sampler tells SBUOp what to read. - // @param int32_t num_workers - number of workers reading images in parallel. - // @param int32_t queue_size - connector queue size. - SBUOp(const std::string &folder_path, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler, int32_t num_workers, int32_t queue_size); - - // Destructor. - ~SBUOp() = default; - - // Op name getter. - // @return std::string - Name of the current Op. - std::string Name() const override { return "SBUOp"; } - - // A print method typically used for debugging. - // @param std::ostream &out - out stream. - // @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the SBU dataset. - // @param const std::string &dir - path to the SBU directory. - // @param int64_t *count - output arg that will hold the minimum of the actual dataset size and numSamples. - // @return Status - The status code returned. - static Status CountTotalRows(const std::string &dir, int64_t *count); - - protected: - // Parse SBU data file. - // @return Status - The status code returned. - Status PrepareData() override; - - private: - // Load a tensor row according to a pair. - // @param row_id_type row_id - id for this tensor row. - // @param TensorRow row - image & label read into this tensor row. - // @return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - // Private function for computing the assignment of the column name map. - // @return Status - The status code returned. - Status ComputeColMap() override; - - // @param const std::string &path - path to the image file. - // @param std::shared_ptr tensor - tensor to store image. - // @return Status - The status code returned. - Status ReadImageToTensor(const std::string &path, std::shared_ptr *tensor) const; - - // Get available image-caption pairs. - // @param std::ifstream &url_file_reader - url file reader. - // @param std::ifstream &caption_file_reader - caption file reader. - // @return Status - The status code returned. - Status GetAvailablePairs(std::ifstream &url_file_reader, std::ifstream &caption_file_reader); - - // Parse path-caption pair. - // @param const std::string &url - image url. - // @param const std::string &caption - caption. - // @return Status - The status code returned. - Status ParsePair(const std::string &url, const std::string &caption); - - // A util for string replace. - // @param std::string *str - string to be replaces. - // @param const std::string &from - string from. - // @param const std::string &to - string to. - // @return Status - The status code returned. - Status ReplaceAll(std::string *str, const std::string &from, const std::string &to) const; - - std::string folder_path_; // directory of data files - const bool decode_; - std::unique_ptr data_schema_; - - Path url_path_; - Path caption_path_; - Path image_folder_; - std::vector image_caption_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SBU_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.cc deleted file mode 100644 index dc3dfff20..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.cc +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -constexpr uint32_t kSemeionImageSize = 256; -constexpr uint32_t kSemeionLabelSize = 10; - -SemeionOp::SemeionOp(const std::string &dataset_dir, int32_t num_parallel_workers, - std::unique_ptr data_schema, std::shared_ptr sampler, int32_t queue_size) - : MappableLeafOp(num_parallel_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - data_schema_(std::move(data_schema)), - semeionline_rows_({}) {} - -void SemeionOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nSemeionOp directory: " << dataset_dir_; - } -} - -Status SemeionOp::PrepareData() { - auto real_path = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_path.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, Semeion Dataset folder: " + dataset_dir_ + " does not exist."); - } - Path data_folder(real_path.value()); - - Path file_path = data_folder / "semeion.data"; - CHECK_FAIL_RETURN_UNEXPECTED(file_path.Exists() && !file_path.IsDirectory(), - "Invalid file, failed to find semeion file: " + data_folder.ToString()); - - MS_LOG(INFO) << "Semeion file found: " << file_path << "."; - - std::ifstream handle(file_path.ToString(), std::ios::in); - - CHECK_FAIL_RETURN_UNEXPECTED(handle.is_open(), "Invalid file, failed to open file: " + file_path.ToString()); - - std::string line; - while (getline(handle, line)) { - semeionline_rows_.push_back(line); - } - handle.close(); - - num_rows_ = semeionline_rows_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(num_rows_ > 0, - "Invalid data, SemeionDataset API can't read the data file (interface mismatch or no " - "data found). Check file path: " + - dataset_dir_); - return Status::OK(); -} - -Status SemeionOp::TransRowIdResult(row_id_type index, std::shared_ptr *img_tensor, - std::shared_ptr *label_tensor) { - RETURN_UNEXPECTED_IF_NULL(img_tensor); - RETURN_UNEXPECTED_IF_NULL(label_tensor); - std::vector img; - uint32_t label; - std::string line = semeionline_rows_[index]; - uint32_t i = 0; - while (i < kSemeionImageSize) { - auto pos = line.find(" "); - CHECK_FAIL_RETURN_UNEXPECTED(pos != std::string::npos, "Invalid data, file content does not match SemeionDataset."); - std::string s = line.substr(0, pos); - uint8_t value_img; - try { - value_img = std::stoi(s); - } catch (std::exception &e) { - RETURN_STATUS_UNEXPECTED("Invalid data, image data in file should be in type of uint8, but got: " + s + "."); - } - img.push_back(value_img); - line.erase(0, pos + 1); // to dedele space - ++i; - } - i = 0; - while (i < kSemeionLabelSize) { - auto pos = line.find(" "); - CHECK_FAIL_RETURN_UNEXPECTED(pos != std::string::npos, "Invalid data, file content does not match SemeionDataset."); - std::string s = line.substr(0, pos); - line.erase(0, pos + 1); - uint8_t value_label; - try { - value_label = std::stoi(s); - } catch (std::exception &e) { - RETURN_STATUS_UNEXPECTED("Invalid data, label data in file should be in type of uint8, but got: " + s + "."); - } - if (value_label != 0) { - label = i; - break; - } - ++i; - } - RETURN_IF_NOT_OK(Tensor::CreateScalar(label, label_tensor)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(img, img_tensor)); - RETURN_IF_NOT_OK((*img_tensor)->Reshape(TensorShape{16, 16})); - return Status::OK(); -} - -Status SemeionOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - Path dir_path(dataset_dir_); - std::shared_ptr img_tensor, label_tensor; - RETURN_IF_NOT_OK(TransRowIdResult(row_id, &img_tensor, &label_tensor)); - (*trow) = TensorRow(row_id, {img_tensor, label_tensor}); - trow->setPath({dir_path.ToString(), dir_path.ToString()}); - - return Status::OK(); -} - -Status SemeionOp::CountTotalRows(const std::string &dataset_dir, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - // build a new unique schema object - auto new_schema = std::make_unique(); - RETURN_IF_NOT_OK(new_schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape label_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &label_scalar))); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(dataset_dir, num_workers, std::move(new_schema), std::move(new_sampler), - op_connect_size); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = static_cast(op->semeionline_rows_.size()); - return Status::OK(); -} - -Status SemeionOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (uint32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h deleted file mode 100644 index 1d44fd0f4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SEMEION_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SEMEION_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class SemeionOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Directory of semeion dataset. - /// \param[in] num_parallel_workers Num of workers in parallel. - /// \param[in] data_schema Schema of dataset. - /// \param[in] sampler Sampler tells SemeionOp what to read. - /// \param[in] queue_size Connector queue size. - SemeionOp(const std::string &dataset_dir, int32_t num_parallel_workers, std::unique_ptr data_schema, - std::shared_ptr sampler, int32_t queue_size); - - /// \brief Destructor. - ~SemeionOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out Out stream. - /// \param[in] show_all Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "SemeionOp"; } - - /// \brief Count total rows. - /// \param[in] dataset_dir File path. - /// \param[out] count Get total row. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dataset_dir, int64_t *count); - - /// \brief Function to count the number of samples in the SemeionOp. - /// \return Status The status code returned. - Status PrepareData() override; - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] index Index need to load. - /// \param[out] trow Image & label read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - /// \brief Get the img and label according the row_id. - /// \param[in] index Index of row need to load. - /// \param[out] img_tensor The image data. - /// \param[out] label_tensor The label data. - /// \return Status The status code returned. - Status TransRowIdResult(row_id_type index, std::shared_ptr *img_tensor, - std::shared_ptr *label_tensor); - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - const std::string dataset_dir_; - std::unique_ptr data_schema_; - std::vector semeionline_rows_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SEMEION_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.cc deleted file mode 100644 index 1f5341200..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h" - -#include - -#include "include/utils/common.h" - -namespace mindspore { -namespace dataset { -SogouNewsOp::SogouNewsOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id, - char field_delim, const std::vector> &column_default, - const std::vector &column_name, - const std::vector &sogou_news_files_list) - : CsvOp(sogou_news_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id) {} - -void SogouNewsOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nSogouNews files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h deleted file mode 100644 index 928faad1b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SOGOU_NEWS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SOGOU_NEWS_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -/// \class SogouNewsOp -/// \brief A Op derived class to represent SogouNews Op. -class SogouNewsOp : public CsvOp { - public: - /// \brief Constructor of SogouNewsOp. - /// \param[in] num_workers Number of worker threads reading data from sogou_news files. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. - /// \param[in] field_delim A char that indicates the delimiter to separate fields. - /// \param[in] column_default List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name List of column names of the dataset. - /// \param[in] sogounews_files_list List of file paths for the dataset files. - SogouNewsOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, const std::vector &sogou_news_files_list); - - /// \brief Destructor. - ~SogouNewsOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "SogouNews" : "sogou news"; } - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "SogouNewsOp"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SOGOU_NEWS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.cc deleted file mode 100644 index 29e564edc..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.cc +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr char kTestFiles[] = "testing_list.txt"; -constexpr char kValFiles[] = "validation_list.txt"; -constexpr char kExtension[] = ".wav"; -#ifndef _WIN32 -constexpr char kSplitSymbol[] = "/"; -#else -constexpr char kSplitSymbol[] = "\\"; -#endif - -SpeechCommandsOp::SpeechCommandsOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, - int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - usage_(usage), - data_schema_(std::move(data_schema)) {} - -Status SpeechCommandsOp::PrepareData() { - // Get file lists according to usage. - // When usage == "train", need to get all filenames then subtract files of usage: "test" and "valid". - std::set selected_files; - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_dataset_dir.has_value()) { - MS_LOG(ERROR) << "Get real path failed, path=" << dataset_dir_; - RETURN_STATUS_UNEXPECTED("Get real path failed, path=" + dataset_dir_); - } - std::string real_path = real_dataset_dir.value(); - if (usage_ == "all") { - RETURN_IF_NOT_OK(WalkAllFiles(real_path)); - selected_files = all_wave_files; - } else if (usage_ == "test" || usage_ == "valid") { - RETURN_IF_NOT_OK(ParseFileList(real_path, usage_)); - selected_files = loaded_names; - } else { - RETURN_IF_NOT_OK(WalkAllFiles(real_path)); - RETURN_IF_NOT_OK(ParseFileList(real_path, "test")); - RETURN_IF_NOT_OK(ParseFileList(real_path, "valid")); - set_difference(all_wave_files.begin(), all_wave_files.end(), loaded_names.begin(), loaded_names.end(), - inserter(selected_files, selected_files.begin())); - } - selected_files_vec.assign(selected_files.begin(), selected_files.end()); - num_rows_ = selected_files_vec.size(); - return Status::OK(); -} - -Status SpeechCommandsOp::ParseFileList(const std::string &pf_path, const std::string &pf_usage) { - std::string line; - std::string file_list = (pf_usage == "test" ? kTestFiles : kValFiles); - Path path(pf_path); - std::string list_path = (Path(pf_path) / Path(file_list)).ToString(); - std::ifstream file_reader(list_path, std::ios::in); - while (getline(file_reader, line)) { - Path file_path(path / line); - loaded_names.insert(file_path.ToString()); - } - file_reader.close(); - return Status::OK(); -} - -Status SpeechCommandsOp::WalkAllFiles(const std::string &walk_path) { - Path dir(walk_path); - if (dir.IsDirectory() == false) { - RETURN_STATUS_UNEXPECTED("Invalid parameter, no folder found in: " + walk_path); - } - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - std::vector folder_names; - while (dir_itr->HasNext()) { - Path sub_dir = dir_itr->Next(); - if (sub_dir.IsDirectory() && (sub_dir.ToString().find("_background_noise_") == std::string::npos)) { - folder_names.emplace_back(sub_dir.ToString()); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!folder_names.empty(), "Invalid file, failed to open directory: " + dir.ToString()); - for (int i = 0; i < folder_names.size(); i++) { - Path folder_path(folder_names[i]); - if (folder_path.IsDirectory()) { - auto folder_it = Path::DirIterator::OpenDirectory(&folder_path); - CHECK_FAIL_RETURN_UNEXPECTED(folder_it != nullptr, "Invalid path, failed to open dir: " + folder_path.ToString() + - ", not exists or permission denied."); - while (folder_it->HasNext()) { - Path file = folder_it->Next(); - if (file.Extension() == kExtension) { - all_wave_files.insert(file.ToString()); - } - } - } else { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open directory: " + folder_path.ToString()); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!all_wave_files.empty(), "Invalid file, no .wav files found under " + dataset_dir_); - return Status::OK(); -} - -Status SpeechCommandsOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string file_name = selected_files_vec[row_id]; - std::shared_ptr waveform, sample_rate_scalar, label_scalar, speaker_id_scalar, utterance_number_scalar; - std::string label, speaker_id; - int32_t utterance_number, sample_rate; - std::vector waveform_vec; - RETURN_IF_NOT_OK(ReadWaveFile(file_name, &waveform_vec, &sample_rate)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, &waveform)); - RETURN_IF_NOT_OK(waveform->ExpandDim(0)); - RETURN_IF_NOT_OK(GetFileInfo(file_name, &label, &speaker_id, &utterance_number)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &sample_rate_scalar)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(label, &label_scalar)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(speaker_id, &speaker_id_scalar)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(utterance_number, &utterance_number_scalar)); - (*trow) = TensorRow(row_id, {waveform, sample_rate_scalar, label_scalar, speaker_id_scalar, utterance_number_scalar}); - trow->setPath({file_name, file_name, file_name, file_name, file_name}); - return Status::OK(); -} - -void SpeechCommandsOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying and common 1-liner info - ParallelOp::Print(out, show_all); - // Then show and custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nSpeechCommands directory: " << dataset_dir_ << "\n\n"; - } -} - -Status SpeechCommandsOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status SpeechCommandsOp::GetFileInfo(const std::string &file_path, std::string *label, std::string *speaker_id, - int32_t *utterance_number) { - // Using regex to get wave infos from filename. - RETURN_UNEXPECTED_IF_NULL(label); - RETURN_UNEXPECTED_IF_NULL(speaker_id); - RETURN_UNEXPECTED_IF_NULL(utterance_number); - int32_t split_index = 0; - split_index = file_path.find_last_of(kSplitSymbol); - std::string label_string = file_path.substr(0, split_index); - *label = label_string.substr(label_string.find_last_of(kSplitSymbol) + 1); // plus "1" for index start from 0. - std::string filename = file_path.substr(split_index + 1); - std::smatch result; - { - std::unique_lock _lock(mux_); - (void)regex_match(filename, result, std::regex("(.*)_nohash_(\\d+)\\.wav")); - } - CHECK_FAIL_RETURN_UNEXPECTED(!(result[0] == "" || result[1] == ""), - "Invalid file name, failed to get file info: " + filename); - *speaker_id = result[1]; - std::string utt_id = result[2]; - *utterance_number = atoi(utt_id.c_str()); - return Status::OK(); -} - -Status SpeechCommandsOp::CountTotalRows(int64_t *num_rows) { - RETURN_UNEXPECTED_IF_NULL(num_rows); - if (all_wave_files.size() == 0) { - auto real_path = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_path.has_value()) { - MS_LOG(ERROR) << "Get real path failed, path=" << dataset_dir_; - RETURN_STATUS_UNEXPECTED("Get real path failed, path=" + dataset_dir_); - } - RETURN_IF_NOT_OK(WalkAllFiles(real_path.value())); - } - (*num_rows) = static_cast(all_wave_files.size()); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h deleted file mode 100644 index 8b8f5ca34..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SPEECH_COMMANDS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SPEECH_COMMANDS_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class SpeechCommandsOp : public MappableLeafOp { - public: - /// Constructor. - /// \param[in] std::string - dataset_dir - directory of SpeechCommands dataset. - /// \param[in] std::string - usage - directory of SpeechCommands dataset. - /// \param[in] uint32_t - num_workers - Num of workers reading audios in parallel. - /// \param[in] uint32_t - queue_size - connector queue size. - /// \param[in] std::unique_ptr - data_schema - data schema of SpeechCommands dataset. - /// \param[in] std::unique_ptr - sampler - sampler tells SpeechCommands what to read. - SpeechCommandsOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// Destructor. - ~SpeechCommandsOp() override = default; - - /// A print method typically used for debugging. - /// \param[out] out - out stream. - /// \param[in] show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// Function to count the number of samples in the SpeechCommands dataset. - /// \param[in] num_rows output arg that will hold the actual dataset size. - /// \return Status - The status code returned. - Status CountTotalRows(int64_t *num_rows); - - /// Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "SpeechCommandsOp"; } - - private: - /// Load a tensor row. - /// \param[in] row_id - row id. - /// \param[in] trow - waveform & sample_rate & label & speaker_id & utterance_number - /// read into this tensor row. - /// \return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - /// \param[in] pf_path - the real path of root directory. - /// \param[in] pf_usage - usage. - /// \return Status - The status code returned. - Status ParseFileList(const std::string &pf_path, const std::string &pf_usage); - - /// Called first when function is called. - /// \return Status - The status code returned. - Status PrepareData(); - - /// Walk all folders to read all ".wav" files. - /// \param[in] walk_path - real path to traverse. - /// \return Status - The status code returned. - Status WalkAllFiles(const std::string &walk_path); - - /// Get detail info of wave filename by regex. - /// \param[in] file_path - wave file path. - /// \param[out] label - label. - /// \param[out] speaker_id - speaker id. - /// \param[out] utterance_number - utterance number. - /// \return Status - The status code returned. - Status GetFileInfo(const std::string &file_path, std::string *label, std::string *speaker_id, - int32_t *utterance_number); - - // Private function for computing the assignment of the column name map. - /// \return Status - The status code returned. - Status ComputeColMap() override; - - std::string dataset_dir_; - std::string usage_; // can only be "test", "train", "valid" or "all". - std::unique_ptr data_schema_; - - std::set all_wave_files; // all wave files in dataset_dir. - std::set loaded_names; // loaded file names from txt files. - std::vector selected_files_vec; // vector of filenames for sequential loading. - - std::mutex mux_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SPEECH_COMMANDS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.cc deleted file mode 100644 index cc3ebc4f7..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.cc +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -SQuADOp::SQuADOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, std::unique_ptr schema, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id) - : NonMappableLeafOp(num_workers, worker_connector_size, num_samples, op_connector_size, shuffle_files, num_devices, - device_id), - dataset_dir_(std::move(dataset_dir)), - usage_(std::move(usage)), - data_schema_(std::move(schema)) {} - -Status SQuADOp::Init() { - RETURN_IF_NOT_OK(GetFiles(dataset_dir_, usage_, &squad_files_list_)); - RETURN_IF_NOT_OK(filename_index_->insert(squad_files_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(squad_files_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - - return Status::OK(); -} - -void SQuADOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nSQuAD files list:\n"; - for (int i = 0; i < squad_files_list_.size(); ++i) { - out << " " << squad_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status SQuADOp::GetFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *data_files_list) { - RETURN_UNEXPECTED_IF_NULL(data_files_list); - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir.c_str()); - if (!real_dataset_dir.has_value()) { - MS_LOG(ERROR) << "Invalid file path, SQuAD Dataset dir: " << dataset_dir << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, SQuAD Dataset dir: " + dataset_dir + " does not exist."); - } - Path root_dir(real_dataset_dir.value()); - - const Path train_file_v1("train-v1.1.json"); - const Path train_file_v2("train-v2.0.json"); - const Path dev_file_v1("dev-v1.1.json"); - const Path dev_file_v2("dev-v2.0.json"); - - if (usage == "train" || usage == "all") { - Path train_path_v1 = root_dir / train_file_v1; - Path train_path_v2 = root_dir / train_file_v2; - bool train_check_v1 = train_path_v1.Exists() && !train_path_v1.IsDirectory(); - bool train_check_v2 = train_path_v2.Exists() && !train_path_v2.IsDirectory(); - CHECK_FAIL_RETURN_UNEXPECTED(train_check_v1 || train_check_v2, - "Invalid path, failed to find SQuAD train data file: " + dataset_dir); - if (train_check_v1) { - data_files_list->emplace_back(train_path_v1.ToString()); - MS_LOG(INFO) << "SQuAD operator found train data file " << train_path_v1.ToString() << "."; - } - if (train_check_v2) { - data_files_list->emplace_back(train_path_v2.ToString()); - MS_LOG(INFO) << "SQuAD operator found train data file " << train_path_v2.ToString() << "."; - } - } - if (usage == "dev" || usage == "all") { - Path dev_path_v1 = root_dir / dev_file_v1; - Path dev_path_v2 = root_dir / dev_file_v2; - bool dev_check_v1 = dev_path_v1.Exists() && !dev_path_v1.IsDirectory(); - bool dev_check_v2 = dev_path_v2.Exists() && !dev_path_v2.IsDirectory(); - CHECK_FAIL_RETURN_UNEXPECTED(dev_check_v1 || dev_check_v2, - "Invalid path, failed to find SQuAD dev data file: " + dataset_dir); - if (dev_check_v1) { - data_files_list->emplace_back(dev_path_v1.ToString()); - MS_LOG(INFO) << "SQuAD operator found dev data file " << dev_path_v1.ToString() << "."; - } - if (dev_check_v2) { - data_files_list->emplace_back(dev_path_v2.ToString()); - MS_LOG(INFO) << "SQuAD operator found dev data file " << dev_path_v2.ToString() << "."; - } - } - return Status::OK(); -} - -template -Status SQuADOp::SearchNodeInJson(const nlohmann::json &input_tree, std::string node_name, T *output_node) { - RETURN_UNEXPECTED_IF_NULL(output_node); - auto node = input_tree.find(node_name); - CHECK_FAIL_RETURN_UNEXPECTED(node != input_tree.end(), "Invalid data, required node not found in JSON: " + node_name); - (*output_node) = *node; - return Status::OK(); -} - -Status SQuADOp::AnswersLoad(const nlohmann::json &answers_tree, std::vector *answer_text_vec, - std::vector *answer_start_vec) { - RETURN_UNEXPECTED_IF_NULL(answer_text_vec); - RETURN_UNEXPECTED_IF_NULL(answer_start_vec); - for (nlohmann::json answers : answers_tree) { - uint32_t answer_start; - std::string answer_text; - RETURN_IF_NOT_OK(SearchNodeInJson(answers, "text", &answer_text)); - RETURN_IF_NOT_OK(SearchNodeInJson(answers, "answer_start", &answer_start)); - answer_text_vec->push_back(answer_text); - answer_start_vec->push_back(answer_start); - } - if (answer_start_vec->size() == 0) { - answer_start_vec->push_back(-1); - } - if (answer_text_vec->size() == 0) { - answer_text_vec->push_back(""); - } - return Status::OK(); -} - -Status SQuADOp::LoadTensorFromScalar(const std::string &scalar_item, TensorRow *out_row, size_t index) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(scalar_item, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -template -Status SQuADOp::LoadTensorFromVector(const std::vector &vector_item, TensorRow *out_row, size_t index) { - RETURN_UNEXPECTED_IF_NULL(out_row); - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(vector_item, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -Status SQuADOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - std::ifstream handle(file, std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file); - } - - nlohmann::json root; - int64_t rows_total = 0; - int num_columns = data_schema_->NumColumns(); - - try { - handle >> root; - } catch (const std::exception &err) { - handle.close(); - // Catch any exception and convert to Status return code. - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse JSON file: " + file); - } - handle.close(); - - nlohmann::json data_list; - RETURN_IF_NOT_OK(SearchNodeInJson(root, "data", &data_list)); - for (nlohmann::json data : data_list) { - nlohmann::json paragraphs_list; - RETURN_IF_NOT_OK(SearchNodeInJson(data, "paragraphs", ¶graphs_list)); - - for (nlohmann::json paragraphs : paragraphs_list) { - std::string context; - RETURN_IF_NOT_OK(SearchNodeInJson(paragraphs, "context", &context)); - nlohmann::json qas_list; - RETURN_IF_NOT_OK(SearchNodeInJson(paragraphs, "qas", &qas_list)); - - for (nlohmann::json qas : qas_list) { - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - std::string question; - RETURN_IF_NOT_OK(SearchNodeInJson(qas, "question", &question)); - nlohmann::json answers_list; - RETURN_IF_NOT_OK(SearchNodeInJson(qas, "answers", &answers_list)); - - std::vector text; - std::vector answer_start; - - RETURN_IF_NOT_OK(AnswersLoad(answers_list, &text, &answer_start)); - - TensorRow tRow(num_columns, nullptr); - // Add file path info. - std::vector file_path(num_columns, file); - tRow.setPath(file_path); - - int64_t context_index = 0; - int64_t question_index = 1; - int64_t text_index = 2; - int64_t answer_start_index = 3; - // Put the data into a tensor table. - RETURN_IF_NOT_OK(LoadTensorFromScalar(context, &tRow, context_index)); - RETURN_IF_NOT_OK(LoadTensorFromScalar(question, &tRow, question_index)); - RETURN_IF_NOT_OK(LoadTensorFromVector(text, &tRow, text_index)); - RETURN_IF_NOT_OK(LoadTensorFromVector(answer_start, &tRow, answer_start_index)); - - rows_total++; - RETURN_IF_NOT_OK(jagged_rows_connector_->Add(worker_id, std::move(tRow))); - } - } - } - - return Status::OK(); -} - -Status SQuADOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - if (!GetLoadIoBlockQueue()) { - break; - } - file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - if (!GetLoadIoBlockQueue()) { - break; - } - file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -Status SQuADOp::CountTensorRowsPreFile(const std::string &file, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - std::ifstream handle(file, std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file); - } - - nlohmann::json root; - *count = 0; - - try { - handle >> root; - } catch (const std::exception &err) { - handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse JSON file: " + file); - } - handle.close(); - - nlohmann::json data_list; - RETURN_IF_NOT_OK(SearchNodeInJson(root, "data", &data_list)); - - for (nlohmann::json data : data_list) { - nlohmann::json paragraphs_list; - RETURN_IF_NOT_OK(SearchNodeInJson(data, "paragraphs", ¶graphs_list)); - - for (nlohmann::json paragraphs : paragraphs_list) { - nlohmann::json qas_list; - RETURN_IF_NOT_OK(SearchNodeInJson(paragraphs, "qas", &qas_list)); - *count += qas_list.size(); - } - } - - return Status::OK(); -} - -Status SQuADOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count; - RETURN_IF_NOT_OK(CountTensorRowsPreFile(it.value(), &count)); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (int i = 0; i < squad_files_list_.size(); ++i) { - ss << " " << squad_files_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, data file may not be suitable to read with SQuADDataset API. Check file path:" + file_list); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -Status SQuADOp::CountAllFileRows(const std::string &dataset_dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - std::vector files_list; - RETURN_IF_NOT_OK(GetFiles(dataset_dir, usage, &files_list)); - *count = 0; - for (auto file : files_list) { - int64_t count_pre_file; - RETURN_IF_NOT_OK(CountTensorRowsPreFile(file, &count_pre_file)); - *count += count_pre_file; - } - return Status::OK(); -} - -Status SQuADOp::ComputeColMap() { - // Set the column name mapping (base class field). - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h deleted file mode 100644 index e586b564a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SQUAD_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SQUAD_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { -/// \class SQuADOp -/// \brief Loading Operator of SQuAD Dataset. -class SQuADOp : public NonMappableLeafOp { - public: - /// \brief Constructor of SQuADOp. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SQuAD. - /// \param[in] num_workers Number of worker threads reading data from tf_file files. - /// \param[in] num_samples Number of samples. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] schema The data schema object. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices. - /// \param[in] device_id Device id. - SQuADOp(const std::string &dataset_dir, const std::string &usage, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, std::unique_ptr schema, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id); - - /// \brief Default destructor of SQuADOp. - ~SQuADOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Instantiates the internal queues and connectors. - /// \return Status The error code returned. - Status Init() override; - - /// \brief Get total tensor rows in files. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SQuAD. - /// \param[out] count Number of tensor rows. - /// \return Status The error code returned. - static Status CountAllFileRows(const std::string &dataset_dir, const std::string &usage, int64_t *count); - - /// \brief File names getter. - /// \return Vector of the input file names. - std::vector FileNames() { return squad_files_list_; } - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "SQuADOp"; } - - private: - /// \brief A single row created from scalar and puts the data into a tensor table. - /// \param[in] scalar_item The content of the row. - /// \param[out] out_row The id of the row filled in the tensor table. - /// \param[in] index The index of the Tecsor Row. - /// \return Status The error code returned. - Status LoadTensorFromScalar(const std::string &scalar_item, TensorRow *out_row, size_t index); - - /// \brief A single row created from vector and puts the data into a tensor table. - /// \param[in] vector_item The content of the row. - /// \param[out] out_row The id of the row filled in the tensor table. - /// \param[in] index The index of the Tecsor Row. - /// \return Status The error code returned. - template - Status LoadTensorFromVector(const std::vector &vector_item, TensorRow *out_row, size_t index); - - /// \brief Reads a squad file and loads the data into multiple TensorRows. - /// \param[in] file The file to read. - /// \param[in] start_offset The start offset of file. - /// \param[in] end_offset The end offset of file. - /// \param[in] worker_id The id of the worker that is executing this function. - /// \return Status The error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - /// \brief Fill the IOBlockQueue. - /// \param[in] i_keys Kys of file to fill to the IOBlockQueue. - /// \return Status The error code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - /// \brief Calculate number of rows in each shard. - /// \return Status The error code returned. - Status CalculateNumRowsPerShard() override; - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The error code returned. - Status ComputeColMap() override; - - /// \brief Get all files in the dataset_dir_. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SQuAD. - /// \param[out] data_files_list The files list in the root directory. - /// \return Status The status code returned. - static Status GetFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *data_files_list); - - /// \brief Search the node in json object. - /// \param[in] input_tree The json object. - /// \param[in] node_name The name of the node to search. - /// \param[out] output_node The output node. - /// \return Status The error code returned. - template - static Status SearchNodeInJson(const nlohmann::json &input_tree, std::string node_name, T *output_node); - - /// \brief Count the number of tensorRows in the file. - /// \param[in] file The SQuAD file name. - /// \param[out] count The number of tensorRows in the file. - /// \return Status The error code returned. - static Status CountTensorRowsPreFile(const std::string &file, int64_t *count); - - /// \brief Load the vector of answer start and answer text. - /// \param[in] answers_tree Answer list of json. - /// \param[out] answer_text_vec The vector of answer text. - /// \param[out] answer_start_vec The vector of answer start. - /// \return Status The error code returned. - Status AnswersLoad(const nlohmann::json &answers_tree, std::vector *answer_text_vec, - std::vector *answer_start_vec); - - std::string dataset_dir_; - std::string usage_; - std::vector squad_files_list_; - std::unique_ptr data_schema_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SQUAD_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.cc deleted file mode 100644 index b17211811..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.cc +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h" - -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -SST2Op::SST2Op(const std::vector &dataset_files_list, const std::string &usage, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id) - : CsvOp(dataset_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id), - usage_(usage) {} - -void SST2Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nST2 files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} - -std::vector SST2Op::split(const std::string &s, char delim) { - std::vector res; - std::stringstream ss(s); - std::string item; - bool skip = usage_ == "test"; - while (getline(ss, item, delim)) { - if (skip) { - skip = false; - } else { - res.push_back(item); - } - } - return res; -} - -Status SST2Op::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - CsvParser csv_parser(worker_id, jagged_rows_connector_.get(), field_delim_, column_default_list_, file); - RETURN_IF_NOT_OK(csv_parser.InitCsvParser()); - csv_parser.SetStartOffset(start_offset); - csv_parser.SetEndOffset(end_offset); - - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file + " does not exist."); - } - - std::ifstream ifs; - ifs.open(realpath.value(), std::ifstream::in); - if (!ifs.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + file + ", the file is damaged or permission denied."); - } - if (column_name_list_.empty()) { - std::string tmp; - getline(ifs, tmp); - } - bool skip = usage_ == "test"; - csv_parser.Reset(); - try { - while (ifs.good()) { - // when ifstream reaches the end of file, the function get() return std::char_traits::eof() - // which is a 32-bit -1, it's not equal to the 8-bit -1 on Euler OS. So instead of char, we use - // int to receive its return value. - int chr = ifs.get(); - if (skip) { - if (chr == field_delim_) { - skip = false; - } - continue; - } - if (usage_ == "test" && chr == '\n') { - skip = true; - } - int err = csv_parser.ProcessMessage(chr); - if (err != 0) { - // if error code is -2, the returned error is interrupted - if (err == -2) { - ifs.close(); - return Status(kMDInterrupted); - } - ifs.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse csv file: " + file + " at line " + - std::to_string(csv_parser.GetTotalRows() + 1) + - ". Error message: " + csv_parser.GetErrorMessage()); - } - } - } catch (std::invalid_argument &ia) { - ifs.close(); - std::string err_row = std::to_string(csv_parser.GetTotalRows() + 1); - RETURN_STATUS_UNEXPECTED("Invalid csv, csv file: " + file + " parse failed at line " + err_row + - ", type does not match."); - } catch (std::out_of_range &oor) { - ifs.close(); - std::string err_row = std::to_string(csv_parser.GetTotalRows() + 1); - RETURN_STATUS_UNEXPECTED("Invalid csv, " + file + " parse failed at line " + err_row + " : value out of range."); - } - ifs.close(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h deleted file mode 100644 index d73c55c92..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SST2_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SST2_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class SST2Op : public CsvOp { - public: - /// \brief Constructor. - /// \param[in] dataset_files_list List of file paths for the dataset files. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] field_delim A char that indicates the delimiter to separate fields. - /// \param[in] column_default List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name List of column names of the dataset file. - /// \param[in] num_workers Num of workers reading files in parallel. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. - SST2Op(const std::vector &dataset_files_list, const std::string &usage, char field_delim, - const std::vector> &column_default, const std::vector &column_name, - int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id); - - /// \brief Destructor. - ~SST2Op() override = default; - - /// \brief A print method typically used for debugging - /// \param[out] out The output stream to write output to - /// \param[in] show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "SST2" : "sst2"; } - - /// \brief Op name getter - /// \return Name of the current Op. - std::string Name() const override { return "SST2Op"; } - - /// \brief Split string based on a character delimiter - /// @param[in] s The input string - /// @param[in] delim The delimiter - /// @return The a string vector - std::vector split(const std::string &s, char delim) override; - - /// \brief Reads a csv file and loads the data into multiple tensors. - /// @param[in] file The file to read. - /// @param[in] start_offset The start offset of file. - /// @param[in] end_offset The end offset of file. - /// @param[in] worker_id The id of the worker that is executing this function. - /// @return Status The error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - std::string usage_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SST2_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.cc deleted file mode 100644 index 0a444d6f0..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.cc +++ /dev/null @@ -1,417 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h" - -#include -#include -#include -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr uint32_t kSTLImageRows = 96; -constexpr uint32_t kSTLImageCols = 96; -constexpr uint32_t kSTLImageChannel = 3; -constexpr uint32_t kSTLImageSize = kSTLImageRows * kSTLImageCols * kSTLImageChannel; - -STL10Op::STL10Op(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(folder_path), - usage_(usage), - data_schema_(std::move(data_schema)), - image_path_({}), - label_path_({}) {} - -Status STL10Op::LoadTensorRow(row_id_type index, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::pair, int32_t> stl10_pair = stl10_image_label_pairs_[index]; - std::shared_ptr image, label; - // make a copy of cached tensor. - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(stl10_pair.first, &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(stl10_pair.second, &label)); - - (*trow) = TensorRow(index, {std::move(image), std::move(label)}); - trow->setPath({image_path_[index], label_path_[index]}); - - return Status::OK(); -} - -Status STL10Op::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || stl10_image_label_pairs_.empty()) { - if (stl10_image_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("No image found in dataset. Check if image was generated successfully."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place, " - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < stl10_image_label_pairs_.size(); ++i) { - (*cls_ids)[stl10_image_label_pairs_[i].second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -void STL10Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nSTL10 directory: " << folder_path_ << "\n\n"; - } -} - -Status STL10Op::WalkAllFiles() { - auto real_dataset_dir = FileUtils::GetRealPath(folder_path_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_dataset_dir.has_value(), - "Invalid file, get real path failed, path: " + folder_path_); - Path root_dir(real_dataset_dir.value()); - - const Path train_data_file("train_X.bin"); - const Path train_label_file("train_y.bin"); - const Path test_data_file("test_X.bin"); - const Path test_label_file("test_y.bin"); - const Path unlabeled_data_file("unlabeled_X.bin"); - - bool use_train = false; - bool use_test = false; - bool use_unlabeled = false; - - if (usage_ == "train") { - use_train = true; - } else if (usage_ == "test") { - use_test = true; - } else if (usage_ == "unlabeled") { - use_unlabeled = true; - } else if (usage_ == "train+unlabeled") { - use_train = true; - use_unlabeled = true; - } else if (usage_ == "all") { - use_train = true; - use_test = true; - use_unlabeled = true; - } else { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, usage should be \"train\", \"test\", \"unlabeled\", " - "\"train+unlabeled\", \"all\", got " + - usage_); - } - - if (use_train) { - Path train_data_path = root_dir / train_data_file; - Path train_label_path = root_dir / train_label_file; - CHECK_FAIL_RETURN_UNEXPECTED( - train_data_path.Exists() && !train_data_path.IsDirectory(), - "Invalid file, failed to find STL10 " + usage_ + " data file: " + train_data_path.ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - train_label_path.Exists() && !train_label_path.IsDirectory(), - "Invalid file, failed to find STL10 " + usage_ + " label file: " + train_label_path.ToString()); - image_names_.push_back(train_data_path.ToString()); - label_names_.push_back(train_label_path.ToString()); - MS_LOG(INFO) << "STL10 operator found train data file " << train_data_path.ToString() << "."; - MS_LOG(INFO) << "STL10 operator found train label file " << train_label_path.ToString() << "."; - } - - if (use_test) { - Path test_data_path = root_dir / test_data_file; - Path test_label_path = root_dir / test_label_file; - CHECK_FAIL_RETURN_UNEXPECTED( - test_data_path.Exists() && !test_data_path.IsDirectory(), - "Invalid file, failed to find STL10 " + usage_ + " data file: " + test_data_path.ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - test_label_path.Exists() && !test_label_path.IsDirectory(), - "Invalid file, failed to find STL10 " + usage_ + " label file: " + test_label_path.ToString()); - image_names_.push_back(test_data_path.ToString()); - label_names_.push_back(test_label_path.ToString()); - MS_LOG(INFO) << "STL10 operator found test data file " << test_data_path.ToString() << "."; - MS_LOG(INFO) << "STL10 operator found test label file " << test_label_path.ToString() << "."; - } - - if (use_unlabeled) { - Path unlabeled_data_path = root_dir / unlabeled_data_file; - CHECK_FAIL_RETURN_UNEXPECTED( - unlabeled_data_path.Exists() && !unlabeled_data_path.IsDirectory(), - "Invalid file, failed to find STL10 " + usage_ + " data file: " + unlabeled_data_path.ToString()); - image_names_.push_back(unlabeled_data_path.ToString()); - MS_LOG(INFO) << "STL10 operator found unlabeled data file " << unlabeled_data_path.ToString() << "."; - } - - std::sort(image_names_.begin(), image_names_.end()); - std::sort(label_names_.begin(), label_names_.end()); - - return Status::OK(); -} - -Status STL10Op::ParseSTLData() { - // STL10 contains 5 files, *_X.bin are image files, *_y.bin are labels. - // training files contain 5k images and testing files contain 8K examples. - // unlabeled file contain 10k images and they DO NOT have labels (i.e. no "unlabeled_y.bin" file). - for (size_t i = 0; i < image_names_.size(); ++i) { - std::ifstream image_reader, label_reader; - if (image_names_[i].find("unlabeled") == std::string::npos) { - auto image_realpath = FileUtils::GetRealPath(image_names_[i].c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + image_names_[i] + " does not exist."); - } - auto label_realpath = FileUtils::GetRealPath(label_names_[i].c_str()); - if (!label_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + label_names_[i] + " does not exist."); - } - image_reader.open(image_realpath.value(), std::ios::binary | std::ios::ate); - label_reader.open(label_realpath.value(), std::ios::binary | std::ios::ate); - - Status s = ReadImageAndLabel(&image_reader, &label_reader, i); - // Close the readers. - image_reader.close(); - label_reader.close(); - - RETURN_IF_NOT_OK(s); - } else { // unlabeled data -> no labels. - auto image_realpath = FileUtils::GetRealPath(image_names_[i].c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + image_names_[i] + " does not exist."); - } - image_reader.open(image_realpath.value(), std::ios::binary | std::ios::ate); - - Status s = ReadImageAndLabel(&image_reader, nullptr, i); - // Close the readers. - image_reader.close(); - - RETURN_IF_NOT_OK(s); - } - } - stl10_image_label_pairs_.shrink_to_fit(); - num_rows_ = stl10_image_label_pairs_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API STL10Dataset. Please check file path or dataset API."); - } - - return Status::OK(); -} - -Status STL10Op::ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index) { - RETURN_UNEXPECTED_IF_NULL(image_reader); - - Path image_path(image_names_[index]); - bool has_label_file = image_path.Basename().find("unlabeled") == std::string::npos; - - std::streamsize image_size = image_reader->tellg(); - - image_reader->seekg(0, std::ios::beg); - auto images_buf = std::make_unique(image_size); - auto labels_buf = std::make_unique(0); - - if (images_buf == nullptr) { - std::string err_msg = "Failed to allocate memory for STL10 buffer."; - MS_LOG(ERROR) << err_msg.c_str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - uint64_t num_images = static_cast(image_size / kSTLImageSize); - (void)image_reader->read(images_buf.get(), image_size); - if (image_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to read image: " + image_names_[index] + - ", size:" + std::to_string(kSTLImageSize * num_images)); - } - - if (has_label_file) { - RETURN_UNEXPECTED_IF_NULL(label_reader); - std::streamsize label_size = label_reader->tellg(); - if (static_cast(label_size) != num_images) { - RETURN_STATUS_UNEXPECTED("Invalid file, error in " + label_names_[index] + - ": the number of labels is not equal to the number of images in " + image_names_[index] + - "! Please check the file integrity!"); - } - - label_reader->seekg(0, std::ios::beg); - labels_buf = std::make_unique(label_size); - if (labels_buf == nullptr) { - std::string err_msg = "Failed to allocate memory for STL10 buffer."; - MS_LOG(ERROR) << err_msg.c_str(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - (void)label_reader->read(labels_buf.get(), label_size); - if (label_reader->fail()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to read label:" + label_names_[index] + - ", size: " + std::to_string(num_images)); - } - } - - for (int64_t j = 0; j < num_images; ++j) { - int32_t label = (has_label_file ? labels_buf[j] - 1 : -1); - - std::shared_ptr image_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({kSTLImageRows, kSTLImageCols, kSTLImageChannel}), - data_schema_->Column(0).Type(), &image_tensor)); - - auto iter = image_tensor->begin(); - uint64_t total_pix = kSTLImageRows * kSTLImageCols; - // stl10: Column major order. - for (uint64_t count = 0, pix = 0; count < total_pix; count++) { - if (count % kSTLImageRows == 0) { - pix = count / kSTLImageRows; - } - - for (int ch = 0; ch < kSTLImageChannel; ch++) { - *iter = images_buf[j * kSTLImageSize + ch * total_pix + pix]; - iter++; - } - pix += kSTLImageRows; - } - - (void)stl10_image_label_pairs_.emplace_back(std::make_pair(image_tensor, label)); - image_path_.push_back(image_names_[index]); - label_path_.push_back(has_label_file ? label_names_[index] : "no label"); - } - - return Status::OK(); -} - -Status STL10Op::PrepareData() { - RETURN_IF_NOT_OK(this->WalkAllFiles()); - RETURN_IF_NOT_OK(this->ParseSTLData()); // Parse stl10 data and get num rows, blocking. - - return Status::OK(); -} - -Status STL10Op::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - // the logic of counting the number of samples is copied from ParseSTLData(). - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = std::make_shared(usage, num_workers, dir, op_connect_size, std::move(schema), std::move(sampler)); - - RETURN_IF_NOT_OK(op->WalkAllFiles()); - - bool use_train = false; - bool use_test = false; - bool use_unlabeled = false; - - if (usage == "train") { - use_train = true; - } else if (usage == "test") { - use_test = true; - } else if (usage == "unlabeled") { - use_unlabeled = true; - } else if (usage == "train+unlabeled") { - use_train = true; - use_unlabeled = true; - } else if (usage == "all") { - use_train = true; - use_test = true; - use_unlabeled = true; - } else { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, usage should be \"train\", \"test\", \"unlabeled\", " - "\"train+unlabeled\", \"all\", got " + - usage); - } - - *count = 0; - uint64_t num_stl10_records = 0; - uint64_t total_image_size = 0; - - if (use_train) { - uint32_t index = (usage == "all" ? 1 : 0); - Path train_image_path(op->image_names_[index]); - CHECK_FAIL_RETURN_UNEXPECTED(train_image_path.Exists() && !train_image_path.IsDirectory(), - "Invalid file, failed to open stl10 file: " + train_image_path.ToString()); - - std::ifstream train_image_file(train_image_path.ToString(), std::ios::binary | std::ios::ate); - CHECK_FAIL_RETURN_UNEXPECTED(train_image_file.is_open(), - "Invalid file, failed to open stl10 file: " + train_image_path.ToString()); - total_image_size += static_cast(train_image_file.tellg()); - - train_image_file.close(); - } - - if (use_test) { - uint32_t index = 0; - Path test_image_path(op->image_names_[index]); - CHECK_FAIL_RETURN_UNEXPECTED(test_image_path.Exists() && !test_image_path.IsDirectory(), - "Invalid file, failed to open stl10 file: " + test_image_path.ToString()); - - std::ifstream test_image_file(test_image_path.ToString(), std::ios::binary | std::ios::ate); - CHECK_FAIL_RETURN_UNEXPECTED(test_image_file.is_open(), - "Invalid file, failed to open stl10 file: " + test_image_path.ToString()); - total_image_size += static_cast(test_image_file.tellg()); - - test_image_file.close(); - } - - if (use_unlabeled) { - uint32_t index = (usage == "unlabeled" ? 0 : (usage == "train+unlabeled" ? 1 : 2)); - Path unlabeled_image_path(op->image_names_[index]); - CHECK_FAIL_RETURN_UNEXPECTED(unlabeled_image_path.Exists() && !unlabeled_image_path.IsDirectory(), - "Invalid file, failed to open stl10 file: " + unlabeled_image_path.ToString()); - - std::ifstream unlabeled_image_file(unlabeled_image_path.ToString(), std::ios::binary | std::ios::ate); - CHECK_FAIL_RETURN_UNEXPECTED(unlabeled_image_file.is_open(), - "Invalid file, failed to open stl10 file: " + unlabeled_image_path.ToString()); - total_image_size += static_cast(unlabeled_image_file.tellg()); - - unlabeled_image_file.close(); - } - - num_stl10_records = static_cast(total_image_size / kSTLImageSize); - - *count = *count + num_stl10_records; - - return Status::OK(); -} - -Status STL10Op::ComputeColMap() { - // set the column Name map (base class field). - if (column_name_id_map_.empty()) { - for (uint32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column Name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h deleted file mode 100644 index 8a8561368..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_STL10_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_STL10_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class STL10Op : public MappableLeafOp { - public: - // Constructor - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'unlabeled', "train+unlabeled" or - // "all" - // @param int32_t num_workers - number of workers reading images in parallel. - // @param std::string folder_path - dir directory of stl10. - // @param int32_t queue_size - connector queue size. - // @param std::unique_ptr data_schema - the schema of the stl10 dataset. - // @param td::unique_ptr sampler - sampler tells STL10Op what to read. - STL10Op(const std::string &usage, int32_t num_workers, const std::string &folder_path, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - // Destructor - ~STL10Op() = default; - - // Method derived from RandomAccess Op, enable Sampler to get all ids for each class - // @param (std::map> * cls_ids - key label, val all ids for this class - // @return Status The status code returned - Status GetClassIds(std::map> *cls_ids) const override; - - // A print method typically used for debugging. - // @param out - The output stream to write output to. - // @param show_all - A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - // Function to count the number of samples in the STL10 dataset. - // @param dir path to the STL10 directory. - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test', 'unlabeled', "train+unlabeled" or - // "all" - // @param count output arg that will hold the minimum of the actual dataset size and numSamples. - // @return Status The status code returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - // Op name getter. - // @return Name of the current Op. - std::string Name() const override { return "STL10Op"; } - - private: - // Read the needed files in the directory, save the file in image_names_ and label_names_. - // @return Status The status code returned. - Status WalkAllFiles(); - - // Read the specified number of images and labels from the file stream. - // @param std::ifstream *image_reader - image file stream. - // @param std::ifstream *label_reader - label file stream. - // @param int64_t index - number of image to read. - // @return Status The status code returned. - Status ReadImageAndLabel(std::ifstream *image_reader, std::ifstream *label_reader, size_t index); - - // Parse all stl10 dataset files - // @return Status The status code returned - Status ParseSTLData(); - - // Read all stl10 data files in the directory - // @return Status The status code returned. - Status PrepareData() override; - - // Private function for computing the assignment of the column name map. - // @return - Status. - Status ComputeColMap() override; - - // Load a tensor row according to a pair. - // @param uint64_t index - index need to load. - // @param TensorRow trow - image & label read into this tensor row. - // @return Status The status code returned. - Status LoadTensorRow(row_id_type index, TensorRow *trow) override; - - std::string folder_path_; // directory of image folder. - const std::string usage_; // can only be either "train" or "test" or "unlabeled" or "train+unlabeled" or "all". - std::unique_ptr data_schema_; - - std::vector, int32_t>> stl10_image_label_pairs_; - std::vector image_names_; - std::vector label_names_; - std::vector image_path_; - std::vector label_path_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_STL10_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.cc deleted file mode 100644 index 1755c0df6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.cc +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h" - -#include -#include -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr char kCategoriesMeta[] = "ClassName.txt"; - -SUN397Op::SUN397Op(const std::string &file_dir, bool decode, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(file_dir), - decode_(decode), - buf_cnt_(0), - categorie2id_({}), - image_path_label_pairs_({}), - data_schema_(std::move(data_schema)) {} - -Status SUN397Op::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - auto file_path = image_path_label_pairs_[row_id].first; - auto label_num = image_path_label_pairs_[row_id].second; - - std::shared_ptr image; - std::shared_ptr label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(label_num, &label)); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(file_path, &image)); - - if (decode_) { - Status rc = Decode(image, &image); - if (rc.IsError()) { - std::string err = "Invalid image, " + file_path + " decode failed, the image is broken or permission denied."; - RETURN_STATUS_UNEXPECTED(err); - } - } - (*trow) = TensorRow(row_id, {std::move(image), std::move(label)}); - trow->setPath({file_path, std::string("")}); - return Status::OK(); -} - -void SUN397Op::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nSUN397 directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -// Derived from RandomAccessOp. -Status SUN397Op::GetClassIds(std::map> *cls_ids) const { - if (cls_ids == nullptr || !cls_ids->empty() || image_path_label_pairs_.empty()) { - if (image_path_label_pairs_.empty()) { - RETURN_STATUS_UNEXPECTED("No image found in dataset. Check if image was read successfully."); - } else { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] Map for containing image-index pair is nullptr or has been set in other place," - "it must be empty before using GetClassIds."); - } - } - for (size_t i = 0; i < image_path_label_pairs_.size(); ++i) { - (*cls_ids)[image_path_label_pairs_[i].second].push_back(i); - } - for (auto &pair : (*cls_ids)) { - pair.second.shrink_to_fit(); - } - return Status::OK(); -} - -Status SUN397Op::GetFileContent(const std::string &info_file, std::string *ans) { - RETURN_UNEXPECTED_IF_NULL(ans); - auto info_file_realpath = FileUtils::GetRealPath(info_file.c_str()); - if (!info_file_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + info_file + " does not exist."); - } - std::ifstream reader; - reader.open(info_file_realpath.value(), std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED( - !reader.fail(), "Invalid file, failed to open " + info_file + ": SUN397 file is damaged or permission denied."); - reader.seekg(0, std::ios::end); - std::size_t size = reader.tellg(); - reader.seekg(0, std::ios::beg); - - CHECK_FAIL_RETURN_UNEXPECTED(size > 0, "Invalid file, the file size of " + info_file + " is unexpected, got size 0."); - std::string buffer(size, ' '); - reader.read(&buffer[0], size); - reader.close(); - - // remove \n character in the buffer. - std::regex pattern("([\\s\\n]+)"); - std::string fmt = " "; - std::string s = std::regex_replace(buffer, pattern, fmt); - - // remove the head and tail whiteblanks of the s. - s.erase(0, s.find_first_not_of(" ")); - s.erase(s.find_last_not_of(" ") + 1); - // append one whiteblanks to the end of s. - s += " "; - *ans = s; - return Status::OK(); -} - -Status SUN397Op::LoadCategories(const std::string &category_meta_name) { - categorie2id_.clear(); - std::string s; - RETURN_IF_NOT_OK(GetFileContent(category_meta_name, &s)); - auto get_splited_str = [&s, &category_meta_name](std::size_t pos) { - std::string item = s.substr(0, pos); - // If pos+1 is equal to the string length, the function returns an empty string. - s = s.substr(pos + 1); - return item; - }; - - std::string category; - uint32_t label = 0; - std::size_t pos = 0; - while ((pos = s.find(" ")) != std::string::npos) { - CHECK_FAIL_RETURN_UNEXPECTED(pos + 1 <= s.size(), "Invalid data, Reading SUN397 category file failed: " + - category_meta_name + ", space characters not found."); - category = get_splited_str(pos); - CHECK_FAIL_RETURN_UNEXPECTED(!category.empty(), "Invalid data, Reading SUN397 category file failed: " + - category_meta_name + ", space characters not found."); - categorie2id_.insert({category, label}); - label++; - } - return Status::OK(); -} - -Status SUN397Op::PrepareData() { - auto real_folder_path = FileUtils::GetRealPath(folder_path_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_folder_path.has_value(), "Invalid file path, " + folder_path_ + " does not exist."); - - RETURN_IF_NOT_OK(LoadCategories((Path(real_folder_path.value()) / Path(kCategoriesMeta)).ToString())); - image_path_label_pairs_.clear(); - for (auto c2i : categorie2id_) { - std::string folder_name = c2i.first; - uint32_t label = c2i.second; - - Path folder(folder_path_ + folder_name); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (!folder.Exists() || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid path, " + folder_name + " does not exist or permission denied."); - } - std::set imgs; // use this for ordering - auto dirname_offset = folder.ToString().size(); - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (file.Extension() == ".jpg") { - auto file_str = file.ToString(); - if (file_str.substr(dirname_offset + 1).find("sun_") == 0) { - (void)imgs.insert(file_str); - } - } else { - MS_LOG(WARNING) << "SUN397Dataset unsupported file found: " << file.ToString() - << ", extension: " << file.Extension() << "."; - } - } - for (const std::string &img : imgs) { - image_path_label_pairs_.push_back({img, label}); - } - } - num_rows_ = image_path_label_pairs_.size(); - CHECK_FAIL_RETURN_UNEXPECTED( - num_rows_ > 0, - "Invalid data, no valid data matching the dataset API SUN397Dataset. Please check dataset API or file path: " + - folder_path_ + "."); - return Status::OK(); -} - -Status SUN397Op::CountTotalRows(const std::string &dir, bool decode, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto sampler = std::make_shared(start_index, num_samples); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - auto op = - std::make_shared(dir, decode, num_workers, op_connect_size, std::move(schema), std::move(sampler)); - RETURN_IF_NOT_OK(op->PrepareData()); - - *count = op->image_path_label_pairs_.size(); - return Status::OK(); -} - -Status SUN397Op::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h deleted file mode 100644 index ea73ec57d..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SUN397_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SUN397_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -/// \brief Forward declares. -template -class Queue; - -using SUN397LabelPair = std::pair, uint32_t>; - -class SUN397Op : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] file_dir Dir directory of SUN397Dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] num_workers Num of workers reading images in parallel. - /// \param[in] queue_size Connector queue size. - /// \param[in] data_schema Schema of data. - /// \param[in] sampler Sampler tells SUN397Op what to read. - SUN397Op(const std::string &file_dir, bool decode, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler); - - /// \brief Destructor. - ~SUN397Op() = default; - - /// \brief Method derived from RandomAccess Op, enable Sampler to get all ids for each class. - /// \param[in] cls_ids Key label, val all ids for this class. - /// \return The status code returned. - Status GetClassIds(std::map> *cls_ids) const override; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \param[in] dir Path to the PhotoTour directory. - /// \param[in] decode Decode jpg format images. - /// \param[out] count Output arg that will hold the minimum of the actual dataset - /// size and numSamples. - /// \return The status code returned. - static Status CountTotalRows(const std::string &dir, bool decode, int64_t *count); - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "SUN397Op"; } - - private: - /// \brief Load a tensor row according to a pair. - /// \param[in] row_id Id for this tensor row. - /// \param[out] row Image & label read into this tensor row. - /// \return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *row); - - /// \brief The content in the given file path. - /// \param[in] info_file Info file name. - /// \param[out] ans Store the content of the info file. - /// \return Status The status code returned - Status GetFileContent(const std::string &info_file, std::string *ans); - - /// \brief Load the meta information of categories. - /// \param[in] category_meta_name Category file name. - /// \return Status The status code returned. - Status LoadCategories(const std::string &category_meta_name); - - /// \brief Initialize SUN397Op related var, calls the function to walk all files. - /// \return Status The status code returned. - Status PrepareData() override; - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - int64_t buf_cnt_; - std::unique_ptr data_schema_; - - std::string folder_path_; // directory of image folder - const bool decode_; - std::map categorie2id_; - std::vector> image_path_label_pairs_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_SUN397_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.cc deleted file mode 100644 index ff614a269..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.cc +++ /dev/null @@ -1,313 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -TedliumOp::TedliumOp(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, int32_t num_parallel_workers, - std::unique_ptr data_schema, std::shared_ptr sampler, int32_t queue_size) - : MappableLeafOp(num_parallel_workers, queue_size, std::move(sampler)), - dataset_dir_(dataset_dir), - release_(release), - usage_(usage), - extensions_(extensions), - data_schema_(std::move(data_schema)), - audio_files_({}), - usage_list_({}) {} - -void TedliumOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nNumber of rows: " << num_rows_ << "\nTedliumOp directory: " << dataset_dir_; - } -} - -Status TedliumOp::PrepareData() { - auto real_path = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!real_path.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file, get real path failed, path=" + dataset_dir_); - } - Path root_folder(real_path.value()); - - if (release_ == "release1" || release_ == "release2") { - if (usage_ == "train" || usage_ == "test" || usage_ == "dev") { - usage_list_.push_back(usage_); - } else if (usage_ == "all") { - usage_list_ = {"train", "test", "dev"}; - } else { - RETURN_STATUS_UNEXPECTED( - "Invalid parameter, usage should be \"train\", \"test\", \"dev\" or \"all\" when " - "specify \"release1\" or \"release2\" , got " + - usage_); - } - for (int32_t i = 0; i < usage_list_.size(); ++i) { - Path stm_folder = root_folder / usage_list_[i] / "stm"; - RETURN_IF_NOT_OK(ReadStmFolderRows(stm_folder, usage_list_[i])); - } - } else if (release_ == "release3") { - if (usage_ == "all") { - Path stm_folder = root_folder / "data" / "stm"; - RETURN_IF_NOT_OK(ReadStmFolderRows(stm_folder, "data")); - } else { - RETURN_STATUS_UNEXPECTED("Invalid parameter, usage should be \"all\" when specify \"release3\" , got " + usage_); - } - } - std::sort(audio_files_.begin(), audio_files_.end()); - num_rows_ = audio_files_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API TedliumDataset. Please check file path or dataset API."); - } - return Status::OK(); -} - -Status TedliumOp::ReadStmFolderRows(const Path &stm_folder, const std::string &release_usage) { - Path dir(stm_folder); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&dir); - if (!dir.Exists() || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open folder: " + dir.ToString()); - } - MS_LOG(DEBUG) << "Tedlium " + release_ + " stm folder Path found: " << dir << "."; - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (file.Extension() == ".stm") { - std::ifstream handle(file.ToString(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file.ToString()); - } - std::string line; - int32_t numline = 0; - while (getline(handle, line)) { - std::string filename = line.substr(0, line.find(" ")); - std::stringstream ss; - ss << numline; - audio_files_.push_back({ss.str(), filename, release_usage}); - ++numline; - } - handle.close(); - } - } - return Status::OK(); -} - -Status TedliumOp::ReadStm(const Path &file_stm_path, int32_t row_line, std::string *talk_id, std::string *speaker_id, - std::string *start_time, std::string *end_time, std::string *identifier, - std::string *transcript) { - std::ifstream handle(file_stm_path.ToString().c_str(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, get real path failed, path=" + file_stm_path.ToString()); - } - std::string line; - int32_t i = 0; - while (i <= row_line && getline(handle, line)) { - ++i; - } - handle.close(); - std::vector temp; - i = 0; - const int32_t data_stm_number = 7; - // There are seven pieces of data in each row, which need to be read out and stored - // with a space as a separator. - // Talk_id, _, speaker_id, start_time, end_time, identifier, transcript. - // "_" is the data we don't need. - while (i < data_stm_number - 1) { - std::string s = line.substr(0, line.find(" ")); - temp.push_back(s); - line.erase(0, line.find(" ") + 1); // to delete space, so use s.find(" ") + 1. - ++i; - } - temp.push_back(line); - if (temp.size() != data_stm_number) { - RETURN_STATUS_UNEXPECTED("Invalid data, stm data was broken."); - } - - const int32_t talk_id_num = 0, speaker_id_num = 2, start_time_num = 3, end_time_num = 4, identifier_num = 5, - transcript_num = 6; - *talk_id = temp[talk_id_num]; - // temp[1] is "_", which is the data we don't need. - *speaker_id = temp[speaker_id_num]; - *start_time = temp[start_time_num]; - *end_time = temp[end_time_num]; - *identifier = temp[identifier_num]; - *transcript = temp[transcript_num]; - - return Status::OK(); -} - -Status TedliumOp::ReadSph(const Path &file_sph_path, double start_time, double end_time, int32_t *sample_rate, - std::vector *result) { - std::ifstream handle(file_sph_path.ToString().c_str(), std::ios::in | std::ios::binary); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open file: " + file_sph_path.ToString()); - } - - char head[1024]; - handle.read(head, sizeof(head)); - CHECK_FAIL_RETURN_UNEXPECTED(!handle.fail(), - "Invalid data, failed to read head part from sph file: " + file_sph_path.ToString() + - ", re-download dataset(make sure the data is true)."); - std::vector vec; - for (int32_t i = 0, j = 0; i < strlen(head); ++i) { - if (head[i] == '\n' || head[i] == ' ') { - while (head[i + 1] == ' ') { - i++; - } - std::string strTemp(head + j, i - j); - vec.push_back(strTemp); - j = i + 1; - } - } - const int32_t dataToBytes = 2; - for (int32_t i = 0; i < vec.size(); ++i) { - if (vec[i] == "sample_rate") { - *sample_rate = atoi(vec[i + dataToBytes].c_str()); - } - } - - int32_t start = static_cast(start_time * (*sample_rate)); - int32_t end = static_cast(end_time * (*sample_rate)); - const int32_t size = (end - start); - std::vector temp(size * dataToBytes); - handle.seekg(start, std::ios::beg); - int32_t j = 0; - char c; - while (j < size * dataToBytes) { - handle.read(&c, 1); - CHECK_FAIL_RETURN_UNEXPECTED(!handle.fail(), - "Invalid data, failed to read data part from sph file: " + file_sph_path.ToString() + - ", re-download dataset(make sure the data is true)."); - temp.push_back(c); - ++j; - } - - const float kMaxVal = 32767.0; - for (int32_t i = 0; i < size; ++i) { - char bh = temp[2 * i]; - char bl = temp[2 * i + 1]; - // SPH audio files is big-endian, so we should convert the two bytes of data into int16_t based - // on the high 8 bits and the low 8 bits. - int16_t s = static_cast(((bh & 0x00FF) << 8) | (bl & 0x00FF)); - // Data normalization: Convert the data from the interval [-32768,32767] to the interval [-1,1]. - double t = s / kMaxVal; - (*result).push_back(t); - } - handle.close(); - - return Status::OK(); -} - -Status TedliumOp::LoadTensorRow(row_id_type row_id, TensorRow *row) { - int32_t row_line = atoi(audio_files_[row_id][0].c_str()); - std::string file_name = audio_files_[row_id][1]; - std::string file_usage_or3_none_ = audio_files_[row_id][2]; - Path dir_path(dataset_dir_); - Path file_stm_path = dir_path / file_usage_or3_none_ / "stm" / (file_name + ".stm"); - Path file_sph_path = dir_path / file_usage_or3_none_ / "sph" / (file_name + extensions_); - std::string talk_id, speaker_id, start_time, end_time, identifier, transcript; - std::vector result; - int32_t sample_rate; - RETURN_IF_NOT_OK( - ReadStm(file_stm_path, row_line, &talk_id, &speaker_id, &start_time, &end_time, &identifier, &transcript)); - RETURN_IF_NOT_OK(ReadSph(file_sph_path, atof(start_time.c_str()), atof(end_time.c_str()), &sample_rate, &result)); - - std::shared_ptr sample_rate_tensor, talk_id_tensor, speaker_id_tensor, identifier_tensor, transcript_tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &sample_rate_tensor)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(talk_id, &talk_id_tensor)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(speaker_id, &speaker_id_tensor)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(identifier, &identifier_tensor)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(transcript, &transcript_tensor)); - - std::shared_ptr audio_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(result, &audio_tensor)); - RETURN_IF_NOT_OK(audio_tensor->ExpandDim(0)); - (*row) = TensorRow(row_id, {audio_tensor, sample_rate_tensor, transcript_tensor, talk_id_tensor, speaker_id_tensor, - identifier_tensor}); - row->setPath({file_sph_path.ToString(), file_sph_path.ToString(), file_stm_path.ToString(), file_stm_path.ToString(), - file_stm_path.ToString(), file_stm_path.ToString()}); - - return Status::OK(); -} - -Status TedliumOp::CountTotalRows(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, int64_t *count) { - // the logic of counting the number of samples is copied from PrepareData() - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - const int64_t num_samples = 0; - const int64_t start_index = 0; - auto new_sampler = std::make_shared(start_index, num_samples); - - // build a new unique schema object - auto new_schema = std::make_unique(); - RETURN_IF_NOT_OK( - new_schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - TensorShape sample_rate_scalar = TensorShape::CreateScalar(); - TensorShape trans_scalar = TensorShape::CreateScalar(); - TensorShape talk_id_scalar = TensorShape::CreateScalar(); - TensorShape speaker_id_scalar = TensorShape::CreateScalar(); - TensorShape identi_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &sample_rate_scalar))); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("transcript", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &trans_scalar))); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("talk_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &talk_id_scalar))); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("speaker_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &speaker_id_scalar))); - RETURN_IF_NOT_OK(new_schema->AddColumn( - ColDescriptor("identifier", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &identi_scalar))); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connect_size = cfg->op_connector_size(); - std::shared_ptr op = - std::make_shared(dataset_dir, release, usage, extensions, num_workers, std::move(new_schema), - std::move(new_sampler), op_connect_size); - RETURN_IF_NOT_OK(op->PrepareData()); - *count = static_cast(op->audio_files_.size()); - return Status::OK(); -} - -Status TedliumOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h deleted file mode 100644 index 86c013742..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEDLIUM_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEDLIUM_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" - -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/path.h" - -namespace mindspore { -namespace dataset { -class TedliumOp : public MappableLeafOp { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Directory of tedlium dataset. - /// \param[in] release Release of tedlium dataset, can be 'release1', 'release2' or 'release3'. - /// \param[in] usage Usage of this dataset, if release is release3, can be '', else 'train', 'dev', 'test' or 'all'. - /// \param[in] extensions Extensions of the sph file, only '.sph' is valid. - /// \param[in] num_parallel_workers Number of workers in parallel. - /// \param[in] data_schema Schema of dataset. - /// \param[in] sampler Sampler tells TedliumOp what to read. - /// \param[in] queue_size Connector queue size. - TedliumOp(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, int32_t num_parallel_workers, std::unique_ptr data_schema, - std::shared_ptr sampler, int32_t queue_size); - - /// \brief Destructor. - ~TedliumOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out Out stream. - /// \param[in] show_all Whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - std::string Name() const override { return "TedliumOp"; } - - /// \brief Initialize TedliumOp related var, calls the function to walk all files. - /// \return Status The status code returned. - Status PrepareData() override; - - /// \brief Function to count the number of samples in the TEDLIUM dataset. - /// \param[in] dataset_dir Directory of tedlium dataset. - /// \param[in] release Release of tedlium dataset. - /// \param[in] usage Usage of this dataset, if release is release3, can be '', else 'train', 'dev', 'test' or 'all'. - /// \param[in] extensions Extensions of the sph file, only '.sph' is valid. - /// \param[in] count Output arg that will hold the actual dataset size. - /// \return Status The status code returned. - static Status CountTotalRows(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, int64_t *count); - - private: - /// \brief Read stm file. - /// \param[in] file_stm_path The path of stm file. - /// \param[in] row_line Which line of the file we need to read. - /// \param[out] talk_id Talk identifier of the row_line in the file. - /// \param[out] speaker_id Speaker identifier of the row_line in the file. - /// \param[out] start_time Start time of the row_line in the file. - /// \param[out] end_time End time of the row_line in the file. - /// \param[out] identifier Identifier of the row_line in the file. - /// \param[out] transcript Transcript of the row_line in the file. - /// \return Status The status code returned. - Status ReadStm(const Path &file_stm_path, int32_t row_line, std::string *talk_id, std::string *speaker_id, - std::string *start_time, std::string *end_time, std::string *identifier, std::string *transcript); - - /// \brief Read sph file. - /// \param[in] file_sph_path The path of sph file. - /// \param[in] start_time The start_time of row we need to use. - /// \param[in] end_time The end_time of row we need to use. - /// \param[out] sample_rate Sample rate of the row. - /// \param[out] result Waveform result vector of the row. - /// \return Status The status code returned. - Status ReadSph(const Path &file_sph_path, double start_time, double end_time, int32_t *sample_rate, - std::vector *result); - - /// \brief Read stm files according current release`s usage. - /// \param[in] stm_folder The folder of stm files. - /// \param[in] release_usage For release1 or release2, use usage_, for release3, "data". - /// \return Status The status code returned. - Status ReadStmFolderRows(const Path &stm_folder, const std::string &release_usage); - - /// \brief Load a tensor row according to a pair. - /// \param[in] row_id Id of row need to load. - /// \param[in] row Audio & label read into this tensor row. - /// \return Status The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \brief Private function for computing the assignment of the column name map. - /// \return Status The status code returned. - Status ComputeColMap() override; - - const std::string release_; - const std::string dataset_dir_; - const std::string usage_; - const std::string extensions_; - std::unique_ptr data_schema_; - - std::vector > audio_files_; - std::vector usage_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEDLIUM_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.cc deleted file mode 100644 index 0a7c92d8c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.cc +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -TextFileOp::TextFileOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr schema, std::vector text_files_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : NonMappableLeafOp(num_workers, worker_connector_size, total_rows, op_connector_size, shuffle_files, num_devices, - device_id), - text_files_list_(std::move(text_files_list)), - data_schema_(std::move(schema)) {} - -// A print method typically used for debugging -void TextFileOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\n" - << DatasetName(true) << " list:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status TextFileOp::Init() { - RETURN_IF_NOT_OK(filename_index_->insert(text_files_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(text_files_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - return Status::OK(); -} - -Status TextFileOp::LoadTensor(const std::string &line, TensorRow *out_row) const { - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(line, &tensor)); - (*out_row)[0] = std::move(tensor); - return Status::OK(); -} - -Status TextFileOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + file + " does not exist."); - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open text:" + file + - ", the file is damaged or permission denied."); - } - - int64_t rows_total = 0; - std::string line; - - while (getline(handle, line)) { - if (line.empty()) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - TensorRow tRow(1, nullptr); - tRow.setPath({file}); - auto s = LoadTensor(line, &tRow); - if (s != Status::OK()) { - handle.close(); - return s; - } - s = jagged_rows_connector_->Add(worker_id, std::move(tRow)); - if (s != Status::OK()) { - handle.close(); - return s; - } - - rows_total++; - } - handle.close(); - - return Status::OK(); -} - -Status TextFileOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - (void)file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - (void)file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -int64_t TextFileOp::CountTotalRows(const std::string &file) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file, " << file << " does not exist."; - return 0; - } - - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - MS_LOG(ERROR) << "Invalid file, failed to open text file:" << file << ", the file is damaged or permission denied."; - return 0; - } - - std::string line; - int64_t count = 0; - while (getline(handle, line)) { - if (!line.empty()) { - count++; - } - } - handle.close(); - - return count; -} - -Status TextFileOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count = CountTotalRows(it.value()); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (int i = 0; i < text_files_list_.size(); ++i) { - ss << " " << text_files_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED("Invalid data, " + DatasetName(true) + - "Dataset API can't read the data file (interface mismatch or no data found). Check " + - DatasetName() + ": " + file_list); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -Status TextFileOp::CountAllFileRows(const std::vector &files, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - int32_t num_workers = GlobalContext::config_manager()->num_parallel_workers(); - int32_t connector_que_size = GlobalContext::config_manager()->op_connector_size(); - int32_t worker_connector_size = GlobalContext::config_manager()->worker_connector_size(); - const int32_t shard_id = 0; - const int32_t num_shards = 1; - const int64_t num_samples = 0; - bool shuffle_files = false; - // Do internal Schema generation. - auto schema = std::make_unique(); - - // Create and initialize - std::shared_ptr op = - std::make_shared(num_workers, num_samples, worker_connector_size, std::move(schema), files, - connector_que_size, shuffle_files, num_shards, shard_id); - RETURN_IF_NOT_OK(op->Init()); - *count = 0; - for (auto file : files) { - *count += op->CountTotalRows(file); - } - return Status::OK(); -} - -Status TextFileOp::ComputeColMap() { - // Set the column name mapping (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h deleted file mode 100644 index b6b20cef6..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEXT_FILE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEXT_FILE_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/auto_index.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { -using StringIndex = AutoIndexObj; - -class TextFileOp : public NonMappableLeafOp { - public: - // Constructor of TextFileOp - // @note The builder class should be used to call this constructor. - // @param num_workers - number of worker threads reading data from tf_file files. - // @param total_num_rows - number of rows to read - // @param dataset_files_list - list of filepaths for the dataset files. - // @param data_schema - the data schema object. - // @param op_connector_size - size of each queue in the connector that the child operator pulls from. - // @param columns_to_load - the names of the columns to load data from. - // @param shuffle_files - whether or not to shuffle the files before reading data. - // @param equal_rows_per_shard - whether or not to get equal rows for each process. - TextFileOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, std::unique_ptr, - std::vector text_files_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - // Default destructor - ~TextFileOp() = default; - - // A print method typically used for debugging - // @param out - The output stream to write output to - // @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // Instantiates the internal queues and connectors - // @return Status - the error code returned - Status Init() override; - - // Get total rows in files. - // @param files - all text files. - // @param count - number of rows. - // @return Status - the error coed returned. - static Status CountAllFileRows(const std::vector &files, int64_t *count); - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "TextFileOp"; } - - // DatasetName name getter - // \return DatasetName of the current Op - virtual std::string DatasetName(bool upper = false) const { return upper ? "TextFile" : "text file"; } - - // File names getter - // @return Vector of the input file names - std::vector FileNames() { return text_files_list_; } - - protected: - // Parses a single row and puts the data into a tensor table. - // @param line - the content of the row. - // @param tensor_table - the tensor table to put the parsed data in. - // @param row - the id of the row filled in the tensor table. - // @return Status - the error code returned. - Status LoadTensor(const std::string &line, TensorRow *out_row) const; - - // Reads a text file and loads the data into multiple TensorRows. - // @param file - the file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - Status CalculateNumRowsPerShard() override; - - // Fill the IOBlockQueue. - // @para i_keys - keys of file to fill to the IOBlockQueue - // @return Status - the error code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - // Count number of rows in each file. - // @param file - txt file name. - // @return int64_t - the total number of rows in file. - virtual int64_t CountTotalRows(const std::string &file); - - std::vector text_files_list_; - std::unique_ptr data_schema_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TEXT_FILE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.cc deleted file mode 100644 index 40833eb2f..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.cc +++ /dev/null @@ -1,1348 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "proto/example.pb.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "utils/file_utils.h" -#include "utils/system/crc32c.h" - -namespace mindspore { -namespace dataset { -TFReaderOp::TFReaderOp(int32_t num_workers, int32_t worker_connector_size, int64_t total_num_rows, - std::vector dataset_files_list, std::unique_ptr data_schema, - int32_t op_connector_size, std::vector columns_to_load, bool shuffle_files, - int32_t num_devices, int32_t device_id, bool equal_rows_per_shard, - const CompressionType &compression_type, bool decode) - : NonMappableLeafOp(num_workers, worker_connector_size, total_num_rows, op_connector_size, shuffle_files, - num_devices, device_id, compression_type), - dataset_files_list_(std::move(dataset_files_list)), - columns_to_load_(std::move(columns_to_load)), - data_schema_(std::move(data_schema)), - equal_rows_per_shard_(equal_rows_per_shard), - decode_(decode) {} - -// A print method typically used for debugging -void TFReaderOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nTotal rows: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") - << "\nDataset files list: Size: " << dataset_files_list_.size() << "\n"; - for (const auto &i : dataset_files_list_) { - out << " " << i; - } - if (!columns_to_load_.empty()) { - out << "\nColumns to load:\n"; - for (const auto &j : columns_to_load_) { - out << " " << j; - } - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status TFReaderOp::Init() { - if (data_schema_->Empty()) { - RETURN_IF_NOT_OK(CreateSchema(dataset_files_list_[0], columns_to_load_)); - } - - if (total_rows_ == 0) { - total_rows_ = data_schema_->NumRows(); - } - if (total_rows_ < 0) { - RETURN_STATUS_UNEXPECTED( - "[Internal ERROR] num_samples or num_rows for TFRecordDataset must be greater than 0, but got: " + - std::to_string(total_rows_)); - } else if (compression_type_ != CompressionType::NONE && total_rows_ == 0) { - MS_LOG(WARNING) << "Since compression_type is set, but neither num_samples nor numRows (from schema file) " - << "is provided, performance might be degraded."; - } - - // Build the index with our files such that each file corresponds to a key id. - RETURN_IF_NOT_OK(filename_index_->insert(dataset_files_list_)); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - - // temporary: make size large enough to hold all files + EOE to avoid hangs - int32_t safe_queue_size = static_cast(std::ceil(dataset_files_list_.size() / num_workers_)) + 1; - io_block_queues_.Init(num_workers_, safe_queue_size); - - return Status::OK(); -} - -Status TFReaderOp::RegisterAndLaunchThreads() { - RETURN_UNEXPECTED_IF_NULL(tree_); - worker_in_queues_.Init(num_workers_, worker_connector_size_); - worker_out_queues_.Init(num_workers_, worker_connector_size_); - - // Registers QueueList and individual Queues for interrupt services - RETURN_IF_NOT_OK(worker_in_queues_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(worker_out_queues_.Register(tree_->AllTasks())); - RETURN_IF_NOT_OK(wait_for_workers_post_.Register(tree_->AllTasks())); - - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, std::bind(&TFReaderOp::WorkerEntry, this, std::placeholders::_1), - &worker_tasks_, Name() + "::WorkerEntry", id())); - // if decode is true, launch some workers to parse the protobuf - if (decode_) { - RETURN_IF_NOT_OK(tree_->LaunchWorkers(num_workers_, - std::bind(&TFReaderOp::ParsingWorkerEntry, this, std::placeholders::_1), - Name() + "::ParsingWorkerEntry", id())); - } - RETURN_IF_NOT_OK(tree_->LaunchWorkers(1, std::bind(&TFReaderOp::Collector, this), Name() + "::Collector", id())); - - return Status::OK(); -} - -Status TFReaderOp::operator()() { - RETURN_IF_NOT_OK(PrepareData()); - while (!finished_reading_dataset_) { - int32_t workers_done = 0; - int64_t rows_read = 0; - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = true; - } - TensorRow fetched_row; - while (workers_done < num_workers_) { - RETURN_IF_NOT_OK(jagged_rows_connector_->Pop(0, &fetched_row)); - if (fetched_row.eoe()) { - workers_done++; - } else if ((compression_type_ == CompressionType::NONE || compression_type_ == CompressionType::GZIP_WITH_COUNT || - compression_type_ == CompressionType::ZLIB_WITH_COUNT) && - (total_rows_ == 0 || rows_read < total_rows_)) { - if (decode_) { - // get record bytes from jagged_rows_connector and send them to workers for parsing - const auto parse_worker_id = NextWorkerID(); - RETURN_IF_NOT_OK(worker_in_queues_[parse_worker_id]->EmplaceBack(std::move(fetched_row))); - } else { - // get record bytes from jagged_rows_connector and send them to out_connector - RETURN_IF_NOT_OK(out_connector_->Add(std::move(fetched_row))); - } - rows_read++; - } else if ((compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::ZLIB) && - (rows_read < total_rows_ * num_devices_)) { - // for compressed version, total_rows_ is total rows that will be read per shard - if (decode_) { - // get record bytes from jagged_rows_connector and send them to workers for parsing - const auto parse_worker_id = NextWorkerID(); - RETURN_IF_NOT_OK(worker_in_queues_[parse_worker_id]->EmplaceBack(std::move(fetched_row))); - } else { - // get record bytes from jagged_rows_connector and send them to out_connector - RETURN_IF_NOT_OK(out_connector_->Add(std::move(fetched_row))); - } - rows_read++; - } else { - // IOBlockQueue thread needs to: - // -stop pushing stuff to IOBlockQueue - // -call PostEndOfEpoch (will send EOE) - // -wait for reset - // - // Worker threads need to: - // -stop reading the file they are currently reading and throw it away - // -keep pulling, but dont read other files (eventually skips all IOBlocks and will get EOE) - // - // Master thread needs to: - // -tell IOBlockQueue thread to stop pushing - // -tell worker threads to stop reading the file they are currently reading - // -keep pulling until EOE - - // don't think we need a lock for now - { - std::unique_lock lock(load_jagged_connector_mutex_); - load_jagged_connector_ = false; - } - { - std::unique_lock lock(load_io_block_queue_mutex_); - load_io_block_queue_ = false; - } - } - } - - if (decode_) { - // finish reading this epoch, send an EOE flag to next parsing worker - const auto parse_worker_id = NextWorkerID(); - RETURN_IF_NOT_OK(worker_in_queues_[parse_worker_id]->EmplaceBack(TensorRow(TensorRow::kFlagEOE))); - } else { - // finish reading this epoch, send an EOE flag to out_connector - RETURN_IF_NOT_OK(out_connector_->SendEOE()); - } - - RETURN_IF_NOT_OK(ResetAndUpdateRepeat()); - } - - if (decode_) { - // finish reading all the data, send an EOF flag to next parsing worker - auto parse_worker_id = NextWorkerID(); - RETURN_IF_NOT_OK(worker_in_queues_[parse_worker_id]->EmplaceBack(TensorRow::kFlagEOF)); - // tell all the parsing workers to quit - for (auto i = 0; i < num_workers_; ++i) { - RETURN_IF_NOT_OK(worker_in_queues_[i]->EmplaceBack(TensorRow::kFlagQuit)); - } - } else { - // finish reading all the data, send an EOF flag to out_connector - RETURN_IF_NOT_OK(out_connector_->SendEOF()); - } - - RETURN_IF_NOT_OK(PostEndOfData()); - - return Status::OK(); -} - -Status TFReaderOp::CalculateNumRowsPerShard() { - if (!equal_rows_per_shard_) { - return Status::OK(); - } - - if (compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::ZLIB) { - num_rows_per_shard_ = total_rows_; - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - std::vector file(1, it.value()); - int64_t num = CountTotalRowsSectioned(file, 0, 1, compression_type_); - filename_numrows_[it.value()] = num; - num_rows_ += num; - } - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - } - if (num_rows_per_shard_ == 0) { - std::stringstream ss; - for (auto &i : dataset_files_list_) { - ss << " " << i; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, TFRecordDataset API can't read the data file (interface mismatch or no data under the file). " - "Check file path." + - file_list); - } - return Status::OK(); -} - -Status TFReaderOp::ParsingWorkerEntry(int32_t worker_id) { - // must be called first if called by worker spawned by taskgroup - TaskManager::FindMe()->Post(); - - TensorRow next_row; - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&next_row)); - while (!next_row.quit()) { - if (!next_row.empty()) { - TensorRow parsed_row; - RETURN_IF_NOT_OK(ParseExample(next_row, &parsed_row)); - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(parsed_row)); - } else if (next_row.eoe() || next_row.eof()) { - RETURN_IF_NOT_OK(worker_out_queues_[worker_id]->EmplaceBack(next_row)); - } else { - RETURN_STATUS_UNEXPECTED("TFReaderOp: parsing worker got an unexpected empty tensor row."); - } - RETURN_IF_NOT_OK(worker_in_queues_[worker_id]->PopFront(&next_row)); - } - return Status::OK(); -} - -Status TFReaderOp::ParseExample(const TensorRow &raw_bytes, TensorRow *parsed_row) { - auto filename = raw_bytes.getPath()[0]; - auto itr = raw_bytes[0]->begin(); - dataengine::Example tf_record_example; - CHECK_FAIL_RETURN_UNEXPECTED(tf_record_example.ParseFromString(static_cast(*itr)), - "TFReaderOp: failed to parse example in tfrecord file: " + filename + - ". Perhaps the version of protobuf is not compatible. The example bytes is " + - static_cast(*itr)); - - auto num_columns = data_schema_->NumColumns(); - TensorRow parsed_example(num_columns, nullptr); - std::vector file_path(num_columns, filename); - parsed_example.setPath(file_path); - RETURN_IF_NOT_OK(LoadExample(&tf_record_example, &parsed_example)); - - *parsed_row = std::move(parsed_example); - return Status::OK(); -} - -// Reads a tf_record_file file and loads the data into multiple TensorRows. -Status TFReaderOp::LoadFile(const std::string &filename, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << filename << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + filename + " does not exist."); - } - std::string realpath_value = realpath.value(); - - if (compression_type_ == CompressionType::NONE) { - RETURN_IF_NOT_OK(HelperLoadNonCompFile(filename, start_offset, end_offset, worker_id, realpath_value)); - } -#if !defined(_WIN32) && !defined(_WIN64) - if (compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::GZIP_WITH_COUNT) { - RETURN_IF_NOT_OK(HelperLoadCompGZIPFile(filename, start_offset, end_offset, worker_id, realpath_value)); - } else if (compression_type_ == CompressionType::ZLIB || compression_type_ == CompressionType::ZLIB_WITH_COUNT) { - RETURN_IF_NOT_OK(HelperLoadCompZLIBFile(filename, start_offset, end_offset, worker_id, realpath_value)); - } -#endif - - return Status::OK(); -} - -Status TFReaderOp::SendRecordBytesRow(const std::string &filename, const std::string &serialized_example, - int32_t worker_id) { - std::vector filenames(1, filename); - TensorRow record_bytes_row(1, nullptr); - record_bytes_row.setPath(filenames); - std::shared_ptr record_bytes_tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(serialized_example, &record_bytes_tensor)); - record_bytes_row[0] = std::move(record_bytes_tensor); - RETURN_IF_NOT_OK(jagged_rows_connector_->Add(worker_id, std::move(record_bytes_row))); - return Status::OK(); -} - -Status TFReaderOp::HelperLoadNonCompFile(const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id, const std::string &realpath_value) { - std::ifstream reader; - reader.open(realpath_value, std::ios::in); - if (!reader) { - RETURN_STATUS_UNEXPECTED("Invalid file, " + filename + " open failed: permission denied!"); - } - - int64_t rows_total = 0; - - while (reader.peek() != EOF) { - if (!GetLoadJaggedConnector()) { - break; - } - RETURN_IF_INTERRUPTED(); - - // read length - std::streamsize record_length = 0; - (void)reader.read(reinterpret_cast(&record_length), kTFRecordRecLenSize); - - // ignore crc header - (void)reader.ignore(kTFRecordHeadFootSize); - - // read serialized Example - std::string serialized_example; - serialized_example.resize(static_cast(record_length)); - (void)reader.read(&serialized_example[0], record_length); - - if (start_offset == kInvalidOffset || (rows_total >= start_offset && rows_total < end_offset)) { - auto s = SendRecordBytesRow(filename, serialized_example, worker_id); - if (s != Status::OK()) { - reader.close(); - return s; - } - } - - // ignore crc footer - (void)reader.ignore(static_cast(kTFRecordHeadFootSize)); - rows_total++; - } - reader.close(); - return Status::OK(); -} - -#if !defined(_WIN32) && !defined(_WIN64) -Status TFReaderOp::HelperLoadCompGZIPFile(const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id, const std::string &realpath_value) { - gzFile file = gzopen(realpath_value.c_str(), "rb"); - if (file == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid file, " + filename + " open failed: permission denied!"); - } - - int64_t rows_read = 0; - int64_t rows_total = 0; - - while (gzeof(file) != 1) { - if (compression_type_ == CompressionType::GZIP && rows_read >= end_offset) { - break; - } - - if (!GetLoadJaggedConnector()) { - break; - } - RETURN_IF_INTERRUPTED(); - - // read length - int64_t record_length = 0; - (void)gzread(file, reinterpret_cast(&record_length), kTFRecordRecLenSize); - if (record_length == 0) { - continue; - } - - if (rows_total == 0) { - // do the delayed checking; read crc from file - uint32_t masked_crc = 0; - (void)gzread(file, reinterpret_cast(&masked_crc), sizeof(uint32_t)); - - // generate crc from data - uint32_t generated_crc = - system::Crc32c::GetMaskCrc32cValue(reinterpret_cast(&record_length), kTFRecordRecLenSize); - - // invalid tfrecord file - if (masked_crc != generated_crc) { - (void)gzclose(file); - RETURN_STATUS_UNEXPECTED("Invalid TFRecord file: " + filename); - } - } else { - // ignore crc header - (void)gzseek(file, kTFRecordHeadFootSize, SEEK_CUR); - } - - // read serialized Example - std::string serialized_example; - serialized_example.resize(static_cast(record_length)); - (void)gzread(file, &serialized_example[0], static_cast(record_length)); - - if (start_offset == kInvalidOffset || (rows_total >= start_offset && rows_total < end_offset)) { - auto s = SendRecordBytesRow(filename, serialized_example, worker_id); - if (s != Status::OK()) { - (void)gzclose(file); - return s; - } - rows_read++; - } - // ignore crc footer - (void)gzseek(file, kTFRecordHeadFootSize, SEEK_CUR); - rows_total++; - } - - (void)gzclose(file); - if (compression_type_ == CompressionType::GZIP && rows_read < end_offset) { - std::string errMsg = "This tfrecord file: " + filename + - ", does not meet minimum rows per shard requirement: " + std::to_string(total_rows_) + - " and " + std::to_string(static_cast(total_rows_ / num_devices_)) + - " number of rows per file, but got " + std::to_string(rows_read) + - " number of rows in this file."; - RETURN_STATUS_UNEXPECTED(errMsg); - } - - return Status::OK(); -} - -Status TFReaderOp::HelperLoadCompZLIBFile(const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id, const std::string &realpath_value) { - // ZLIB stream setup (based on zlib.h tutorial) - ZLIBStreamInf zlib_stream; - std::ifstream reader(realpath_value, std::ios::in | std::ios::binary); - if (!reader) { - RETURN_STATUS_UNEXPECTED("Invalid file, " + filename + " open failed: permission denied!"); - } - - zlib_stream.inflate_status = inflateInit(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_OK) { - reader.close(); - RETURN_STATUS_UNEXPECTED("Failed to initialize inflate stream for ZLIB for file " + filename + "!"); - } - - int64_t rows_read = 0; - int64_t rows_total = 0; - - // decompress until inflate stream ends or end of file - do { - if (compression_type_ == CompressionType::ZLIB && rows_read >= end_offset) { - break; - } - - if (!GetLoadJaggedConnector()) { - break; - } - RETURN_IF_INTERRUPTED(); - - (void)reader.read(zlib_stream.input_stream, kZLIBChunkSize); - zlib_stream.strm.avail_in = static_cast(reader.gcount()); - if (zlib_stream.strm.avail_in == 0) { - break; - } - zlib_stream.strm.next_in = reinterpret_cast(zlib_stream.input_stream); - - // run inflate() on input buffer until current output buffer is not full yet but still need more from input buffer, - // or rows_read have exceeded the required number of rows to be read (end_offset) - do { - if (compression_type_ == CompressionType::ZLIB && rows_read >= end_offset) { - break; - } - - // inflate the stream - auto s = HelperInflateZLIB(&zlib_stream, filename); - if (s != Status::OK()) { - reader.close(); - return s; - } - if (zlib_stream.left_to_read != 0) { - break; - } - - // Process inflated data depending on read flag - s = HelperProcessZLIBData(&zlib_stream, &rows_read, &rows_total, filename, start_offset, end_offset, worker_id); - if (s != Status::OK()) { - reader.close(); - return s; - } - zlib_stream.read_flag = (zlib_stream.read_flag + 1) % - (static_cast(ZLIBReadFlag::Footer) + 1); // resets flag to reading record length - } while (zlib_stream.strm.avail_out == 0); - } while (zlib_stream.inflate_status != Z_STREAM_END); - - (void)inflateEnd(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_STREAM_END && rows_read < end_offset) { - reader.close(); - RETURN_STATUS_UNEXPECTED("Decompression of ZLIB file failed for file " + filename + "!"); - } - - if (compression_type_ == CompressionType::ZLIB && rows_read < end_offset) { - reader.close(); - std::string errMsg = "This tfrecord file: " + filename + - ", does not meet minimum rows per shard requirement: " + std::to_string(total_rows_) + - " and " + std::to_string(static_cast(total_rows_ / num_devices_)) + - " number of rows per file, but got " + std::to_string(rows_read) + - " number of rows in this file."; - RETURN_STATUS_UNEXPECTED(errMsg); - } - reader.close(); - return Status::OK(); -} - -int64_t TFReaderOp::HelperBinDataToInt(const unsigned char *str_record_size, size_t str_size) { - int n = 1; - int new_value_width = 2; - if (*reinterpret_cast(&n) == 1) { // Little-endian system - std::stringstream ss; - ss << std::hex << std::setfill('0'); - std::string hex_str = "0x"; - for (int pos = static_cast(str_size) - 1; pos >= 0; pos--) { - ss << std::setw(new_value_width) << static_cast(str_record_size[static_cast(pos)]); - } - (void)hex_str.append(ss.str()); - auto result = static_cast(std::stoul(hex_str, nullptr, 16)); - return result; - } else { // Big-endian system - std::stringstream ss; - ss << std::hex << std::setfill('0'); - std::string hex_str = "0x"; - for (size_t pos = 0; pos < str_size; pos++) { - ss << std::setw(new_value_width) << static_cast(str_record_size[pos]); - } - (void)hex_str.append(ss.str()); - auto result = static_cast(std::stoul(hex_str, nullptr, 16)); - return result; - } -} - -Status TFReaderOp::HelperInflateZLIB(ZLIBStreamInf *zlib_stream, const std::string &filename) const { - if (zlib_stream->left_to_read != 0) { - zlib_stream->strm.avail_out = - static_cast(zlib_stream->left_to_read); // need to read the rest before process - } else { - switch (zlib_stream->read_flag) { - case ZLIBReadFlag::RecordLength: // record length - zlib_stream->strm.avail_out = kTFRecordRecLenSize; - zlib_stream->strm.next_out = zlib_stream->record_size; - break; - case ZLIBReadFlag::Header: // record header/footer - case ZLIBReadFlag::Footer: - zlib_stream->strm.avail_out = kTFRecordHeadFootSize; - zlib_stream->strm.next_out = zlib_stream->garbage; - break; - default: // record example - zlib_stream->strm.avail_out = static_cast(zlib_stream->record_length); - zlib_stream->content = std::make_unique(static_cast(zlib_stream->record_length)); - zlib_stream->strm.next_out = zlib_stream->content.get(); - } - } - - // Inflate stream - zlib_stream->inflate_status = inflate(&zlib_stream->strm, Z_NO_FLUSH); - auto inflate_status = zlib_stream->inflate_status; - // inflate returns Z_BUF_ERROR if no progress is possible. - // It is not fatal and inflate can be called again to continue compressing. - if (inflate_status == Z_OK || inflate_status == Z_STREAM_END || inflate_status == Z_BUF_ERROR) { - zlib_stream->left_to_read = static_cast(zlib_stream->strm.avail_out); // after reading - return Status::OK(); - } else if (inflate_status == Z_STREAM_ERROR) { - (void)inflateEnd(&zlib_stream->strm); - RETURN_STATUS_UNEXPECTED("State not clobbered when inflating file " + filename + "!"); - } else if (inflate_status == Z_NEED_DICT || inflate_status == Z_DATA_ERROR) { - (void)inflateEnd(&zlib_stream->strm); - RETURN_STATUS_UNEXPECTED("Invalid or incomplete inflate data when inflating file " + filename + "!"); - } else if (inflate_status == Z_MEM_ERROR) { - (void)inflateEnd(&zlib_stream->strm); - RETURN_STATUS_UNEXPECTED("Out of memory when inflating file " + filename + "!"); - } else { - (void)inflateEnd(&zlib_stream->strm); - RETURN_STATUS_UNEXPECTED("Got error code " + std::to_string(inflate_status) + " when inflating file " + filename + - "! Please refer to the zilb documentation for more details."); - } -} - -Status TFReaderOp::HelperProcessZLIBData(ZLIBStreamInf *zlib_stream, int64_t *rows_read, int64_t *rows_total, - const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id) { - if (zlib_stream->read_flag == static_cast(ZLIBReadFlag::RecordLength)) { // read record length - zlib_stream->record_length = HelperBinDataToInt(zlib_stream->record_size, kTFRecordRecLenSize); - } else if (zlib_stream->read_flag == static_cast(ZLIBReadFlag::Header) && - *rows_total == 0) { // read header when needed (for tfrecord validation) - auto masked_crc = static_cast(HelperBinDataToInt(zlib_stream->garbage, kTFRecordHeadFootSize)); - uint32_t generated_crc = - system::Crc32c::GetMaskCrc32cValue(reinterpret_cast(&zlib_stream->record_length), kTFRecordRecLenSize); - - // invalid tfrecord file - if (masked_crc != generated_crc) { - RETURN_STATUS_UNEXPECTED("Invalid TFRecord file: " + filename); - } - } else if (zlib_stream->read_flag == static_cast(ZLIBReadFlag::Content)) { // read serialized example - std::string serialized_example(reinterpret_cast(zlib_stream->content.get()), zlib_stream->record_length); - - if (start_offset == kInvalidOffset || (*rows_total >= start_offset && *rows_total < end_offset)) { - RETURN_IF_NOT_OK(SendRecordBytesRow(filename, serialized_example, worker_id)); - (*rows_read)++; - } - } else if (zlib_stream->read_flag == static_cast(ZLIBReadFlag::Footer)) { - (*rows_total)++; - } - - return Status::OK(); -} -#endif - -// Parses a single row and puts the data into a tensor table. -Status TFReaderOp::LoadExample(const dataengine::Example *tf_record_file, TensorRow *out_row) { - auto num_columns = static_cast(data_schema_->NumColumns()); - for (int32_t col = 0; col < num_columns; ++col) { - const ColDescriptor current_col = data_schema_->Column(col); - const dataengine::Features &example_features = tf_record_file->features(); - const google::protobuf::Map &feature_map = example_features.feature(); - auto iter_column = feature_map.find(current_col.Name()); - if (iter_column == feature_map.end()) { - RETURN_STATUS_UNEXPECTED("Invalid columns_list, column name: " + current_col.Name() + - " does not exist in tfrecord file, check tfrecord files."); - } - const dataengine::Feature &column_values_list = iter_column->second; - RETURN_IF_NOT_OK(LoadFeature(out_row, column_values_list, current_col, col)); - } - - return Status::OK(); -} - -// Parses a single cell and puts the data into a tensor table. -Status TFReaderOp::LoadFeature(TensorRow *tensor_row, const dataengine::Feature &column_values_list, - const ColDescriptor ¤t_col, int32_t col) { - const dataengine::Feature::KindCase column_list_type = column_values_list.kind_case(); - std::unique_ptr float_array; // For staging data from protobuf deserialization - const unsigned char *data_ptr = nullptr; // Generic pointer used for populating the Tensor - - // This variable will point into the above staging variables. - // Also used for creating shape attributes. - int32_t num_elements = 0; - - // we build a tensor first a read directly into it if we need to cast - std::shared_ptr ts; - - // Depending on the type of data from the tf_record_file, we want to extract 2 things: - // 1) A pointer to the data as a const unsigned char * - // 2) The number of elements of the data - // After those are determined, we can then build the tensor to represent this data. - switch (column_list_type) { - case dataengine::Feature::KindCase::kBytesList: { - RETURN_IF_NOT_OK(LoadBytesList(current_col, column_values_list, &num_elements, &ts)); - - break; - } - case dataengine::Feature::KindCase::kFloatList: { - RETURN_IF_NOT_OK(LoadFloatList(current_col, column_values_list, &num_elements, &float_array)); - - data_ptr = reinterpret_cast(float_array.get()); - - // only floatList needs to create the tensor here, other two lists read directly - // into the tensor - TensorShape current_shape = TensorShape::CreateUnknownRankShape(); - RETURN_IF_NOT_OK(current_col.MaterializeTensorShape(num_elements, ¤t_shape)); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(current_shape, current_col.Type(), data_ptr, &ts)); - break; - } - case dataengine::Feature::KindCase::kInt64List: { - RETURN_IF_NOT_OK(LoadIntListSwitch(current_col, column_values_list, &num_elements, &ts)); - break; - } - case dataengine::Feature::KindCase::KIND_NOT_SET: { - std::string err_msg = - "Unrecognized datatype, column type in tfrecord file must be uint8, int64 or float32, check tfrecord file."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - default: { - std::string err_msg = - "Unrecognized datatype, column type in tfrecord file must be uint8, int64 or float32, check tfrecord file."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - (*tensor_row)[col] = std::move(ts); - - return Status::OK(); -} - -Status TFReaderOp::LoadBytesList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor) { - // kBytesList can map to the following DE types ONLY! - // DE_UINT8, DE_INT8 - // Must be single byte type for each element! - if (current_col.Type() != DataType::DE_UINT8 && current_col.Type() != DataType::DE_INT8 && - current_col.Type() != DataType::DE_STRING) { - std::string err_msg = "Invalid column type, the column type of " + current_col.Name() + - " should be int8, uint8 or string, but got " + current_col.Type().ToString(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - const dataengine::BytesList &bytes_list = column_values_list.bytes_list(); - - *num_elements = bytes_list.value_size(); - - if (current_col.Type() == DataType::DE_STRING) { - TensorShape shape = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(current_col.MaterializeTensorShape(*num_elements, &shape)); - RETURN_IF_NOT_OK(Tensor::CreateFromByteList(bytes_list, shape, tensor)); - return Status::OK(); - } - - uint64_t max_size = 0; - for (uint32_t i = 0; i < bytes_list.value_size(); ++i) { -#if defined(__APPLE__) - max_size = fmax(max_size, bytes_list.value(i).size()); -#else - max_size = std::max(max_size, bytes_list.value(i).size()); -#endif - } - - int64_t pad_size = max_size; - - // if user provides a shape in the form of [-1, d1, 2d, ... , dn], we need to pad to d1 * d2 * ... * dn - if (current_col.HasShape()) { - TensorShape cur_shape = current_col.Shape(); - if (cur_shape.Size() >= 2 && cur_shape[0] == TensorShape::kDimUnknown) { - int64_t new_pad_size = 1; - for (int i = 1; i < cur_shape.Size(); ++i) { - if (cur_shape[i] == TensorShape::kDimUnknown) { - std::string err_msg = - "Invalid data dimension, only one dimension shape supported is -1, but the 0th and the" + - std::to_string(i) + "th dimension shape of " + current_col.Name() + " are both -1."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - new_pad_size *= cur_shape[i]; - } - pad_size = new_pad_size; - } else { - if (cur_shape.known() && cur_shape.NumOfElements() != max_size) { - std::string err_msg = "Data dimensions of '" + current_col.Name() + - "' do not match, the expected total elements of shape " + cur_shape.ToString() + - " should be " + std::to_string(max_size) + ", but got " + - std::to_string(cur_shape.NumOfElements()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - } - - // know how many elements there are and the total bytes, create tensor here: - TensorShape current_shape = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(current_col.MaterializeTensorShape((*num_elements) * pad_size, ¤t_shape)); - RETURN_IF_NOT_OK(Tensor::CreateFromByteList(bytes_list, current_shape, current_col.Type(), pad_size, tensor)); - - return Status::OK(); -} - -Status TFReaderOp::LoadFloatList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::unique_ptr *float_array) { - // KFloatList can only map to DE types: - // DE_FLOAT32 - if (current_col.Type() != DataType::DE_FLOAT32) { - std::string err_msg = "Invalid column type, the column type of " + current_col.Name() + - " should be string, but got " + current_col.Type().ToString(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - const dataengine::FloatList &float_list = column_values_list.float_list(); - - // Identify how many values we have and then create a local array of these - // to deserialize into - *num_elements = float_list.value_size(); - *float_array = std::make_unique(*num_elements); - for (int i = 0; i < float_list.value_size(); ++i) { - (*float_array)[i] = float_list.value(i); - } - - return Status::OK(); -} - -// Determines which template type to use and calls LoadIntList -Status TFReaderOp::LoadIntListSwitch(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor) { - if (current_col.Type() == DataType::DE_UINT64) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_INT64) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_UINT32) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_INT32) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_UINT16) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_INT16) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_UINT8) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else if (current_col.Type() == DataType::DE_INT8) { - RETURN_IF_NOT_OK(LoadIntList(current_col, column_values_list, num_elements, tensor)); - } else { - std::string err_msg = "Invalid column type, the column type of " + current_col.Name() + - " should be uint64, int64, uint32, int32, uint16, int16, uint8 or int8, but got " + - current_col.Type().ToString(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} - -// Reads values from a bytes list and casts the value to type T, must be an integral type -// compatible with int64_t -template -Status TFReaderOp::LoadIntList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor) { - if (!(current_col.Type().IsInt())) { - std::string err_msg = "Invalid column type, the column type of " + current_col.Name() + " should be int, but got " + - current_col.Type().ToString(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - const dataengine::Int64List &int64_list = column_values_list.int64_list(); - - // Identify how many values we have and then create a local array of these - // to deserialize into - *num_elements = int64_list.value_size(); - - // know how many elements there are, create tensor here: - TensorShape current_shape = TensorShape::CreateUnknownRankShape(); - RETURN_IF_NOT_OK(current_col.MaterializeTensorShape(*num_elements, ¤t_shape)); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(current_shape, current_col.Type(), tensor)); - - int64_t i = 0; - auto it = (*tensor)->begin(); - for (; it != (*tensor)->end(); i++, ++it) { - T element = static_cast(int64_list.value(i)); - *it = element; - } - - return Status::OK(); -} - -Status TFReaderOp::CreateSchema(const std::string &tf_record_file, std::vector columns_to_load) { - auto realpath = FileUtils::GetRealPath(tf_record_file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << tf_record_file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + tf_record_file + " does not exist."); - } - - std::string serialized_example; - RETURN_IF_NOT_OK(HelperGetExampleSchema(&serialized_example, realpath.value(), tf_record_file)); - - dataengine::Example example; - if (!example.ParseFromString(serialized_example)) { - RETURN_STATUS_UNEXPECTED("Failed to parse tfrecord file: " + realpath.value() + - ", fields that failed to parse: " + serialized_example); - } - - const dataengine::Features &example_features = example.features(); - const google::protobuf::Map &feature_map = example_features.feature(); - - if (columns_to_load.empty()) { - (void)std::transform(feature_map.begin(), feature_map.end(), std::back_inserter(columns_to_load), - [](const auto &it) -> std::string { return it.first; }); - std::sort(columns_to_load.begin(), columns_to_load.end()); - } - - for (const auto &curr_col_name : columns_to_load) { - auto it = feature_map.find(curr_col_name); - if (it == feature_map.end()) { - RETURN_STATUS_UNEXPECTED("Invalid columns_list, tfrecord file failed to find column name: " + curr_col_name); - } - std::string column_name = it->first; - - std::string column_type; - - const dataengine::Feature &feature = it->second; - const dataengine::Feature::KindCase kind_case = feature.kind_case(); - switch (kind_case) { - case dataengine::Feature::KindCase::kBytesList: - column_type = "uint8"; - break; - - case dataengine::Feature::KindCase::kFloatList: - column_type = "float32"; - break; - - case dataengine::Feature::KindCase::kInt64List: - column_type = "int64"; - break; - - case dataengine::Feature::KindCase::KIND_NOT_SET: - RETURN_STATUS_UNEXPECTED("Unrecognized column type, the column type of " + column_name + - " should be uint8, int64 or float32, but got unrecognized column type."); - - default: - RETURN_STATUS_UNEXPECTED("Unsupported column type, the column type of " + column_name + - " should be uint8, int64 or float32, but got unsupported column type."); - } - - RETURN_IF_NOT_OK( - data_schema_->AddColumn(ColDescriptor(column_name, DataType(column_type), TensorImpl::kFlexible, 1))); - } - - return Status::OK(); -} - -Status TFReaderOp::HelperGetExampleSchema(std::string *const serialized_example, const std::string &realpath_value, - const std::string &filename) const { - if (compression_type_ == CompressionType::NONE) { - std::ifstream reader; - reader.open(realpath_value, std::ios::in); - - // read length - int64_t record_length = 0; - (void)reader.read(reinterpret_cast(&record_length), static_cast(kTFRecordRecLenSize)); - - // ignore crc header - (void)reader.ignore(static_cast(kTFRecordHeadFootSize)); - - // read serialized Example - (*serialized_example).resize(static_cast(record_length)); - (void)reader.read(&(*serialized_example)[0], static_cast(record_length)); - reader.close(); - } -#if !defined(_WIN32) && !defined(_WIN64) - if (compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::GZIP_WITH_COUNT) { - gzFile file = gzopen(realpath_value.c_str(), "rb"); - - // read length - int64_t record_length = 0; - (void)gzread(file, reinterpret_cast(&record_length), kTFRecordRecLenSize); - - // ignore crc header - (void)gzseek(file, kTFRecordHeadFootSize, SEEK_CUR); - - // read serialized Example - (*serialized_example).resize(static_cast(record_length)); - (void)gzread(file, &(*serialized_example)[0], static_cast(record_length)); - (void)gzclose(file); - } else if (compression_type_ == CompressionType::ZLIB || compression_type_ == CompressionType::ZLIB_WITH_COUNT) { - // ZLIB stream setup (based on zlib.h tutorial) - ZLIBStreamInf zlib_stream; - - std::ifstream reader(realpath_value.c_str(), std::ios::in | std::ios::binary); - zlib_stream.inflate_status = inflateInit(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_OK) { - reader.close(); - RETURN_STATUS_UNEXPECTED("Failed to initialize inflate stream for ZLIB for file " + filename + "!"); - } - - // decompress until first row is read - do { - (void)reader.read(zlib_stream.input_stream, kZLIBChunkSize); - zlib_stream.strm.avail_in = static_cast(reader.gcount()); - zlib_stream.strm.next_in = reinterpret_cast(zlib_stream.input_stream); - - // run inflate() on input until output buffer not full - do { - auto s = HelperInflateZLIB(&zlib_stream, filename); - if (s != Status::OK()) { - reader.close(); - return s; - } - if (zlib_stream.left_to_read != 0) { - break; - } - - // Process inflated data depending on read flag - if (zlib_stream.read_flag == static_cast(ZLIBReadFlag::RecordLength)) { // read record length - zlib_stream.record_length = HelperBinDataToInt(zlib_stream.record_size, kTFRecordRecLenSize); - } else if (zlib_stream.read_flag == static_cast(ZLIBReadFlag::Content)) { // read serialized example - (*serialized_example).resize(static_cast(zlib_stream.record_length)); - (void)(*serialized_example) - .assign(reinterpret_cast(zlib_stream.content.get()), - static_cast(zlib_stream.record_length)); - } - zlib_stream.read_flag++; - } while (zlib_stream.strm.avail_out == 0 && zlib_stream.read_flag != static_cast(ZLIBReadFlag::Footer)); - } while (zlib_stream.inflate_status != Z_STREAM_END && - zlib_stream.read_flag != static_cast(ZLIBReadFlag::Footer)); - - (void)inflateEnd(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_STREAM_END && zlib_stream.read_flag < static_cast(ZLIBReadFlag::Footer)) { - reader.close(); - RETURN_STATUS_UNEXPECTED("Decompression of ZLIB file failed for file " + filename + "!"); - } - - reader.close(); - } -#endif - - return Status::OK(); -} - -Status TFReaderOp::CountTotalRows(int64_t *out_total_rows, const std::vector &filenames, int64_t threads, - bool estimate, CompressionType compression_type) { - RETURN_UNEXPECTED_IF_NULL(out_total_rows); - try { - if (threads > filenames.size()) { - threads = filenames.size(); - } - - std::vector> async_results; - - if (threads <= 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid threads number, the threads number of TFReader should be greater than zero, but got " + - std::to_string(threads) + "."); - } - int64_t chunk_size = filenames.size() / threads; - int64_t remainder = filenames.size() % threads; - - int64_t begin = 0; - int64_t end = begin; - for (int i = 0; i < threads; i++) { - end += chunk_size; - if (remainder > 0) { - end++; - remainder--; - } - - if (estimate) { - // Parse a single file for each chunk with estimate mode on - async_results.push_back( - std::async(std::launch::async, &CountTotalRowsSectioned, filenames, begin, begin + 1, compression_type)); - } else { - // Parse the whole chunk with estimate mode off - async_results.push_back( - std::async(std::launch::async, &CountTotalRowsSectioned, filenames, begin, end, compression_type)); - } - - begin = end; - } - - int64_t total_rows = 0; - for (auto &async_result : async_results) { - total_rows += async_result.get(); - } - - if (estimate) { - // Each thread only scans 1 file - // Estimated total rows = Average rows * total number of files - total_rows = total_rows / threads * filenames.size(); - } - - *out_total_rows = total_rows; - } catch (const std::exception &e) { - std::string err_msg = "Unexpected error occurred: "; - err_msg += std::string(e.what()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} - -int64_t TFReaderOp::CountTotalRowsSectioned(const std::vector &filenames, int64_t begin, int64_t end, - CompressionType compression_type) { - int64_t rows_read = 0; - for (size_t i = begin; i < end; i++) { - auto realpath = FileUtils::GetRealPath(filenames[i].c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << filenames[i] << " does not exist."; - continue; - } - - if (compression_type == CompressionType::NONE) { - HelperCountNonCompRows(realpath.value(), filenames[i], &rows_read); - } -#if !defined(_WIN32) && !defined(_WIN64) - if (compression_type == CompressionType::GZIP_WITH_COUNT) { - HelperCountGZIPRows(realpath.value(), filenames[i], &rows_read); - } else if (compression_type == CompressionType::ZLIB_WITH_COUNT) { - HelperCountZLIBRows(realpath.value(), filenames[i], &rows_read); - } -#endif - } - - return rows_read; -} - -void TFReaderOp::HelperCountNonCompRows(const std::string &realpath_value, const std::string &filename, - int64_t *rows_read) { - std::ifstream reader; - reader.open(realpath_value, std::ios::in); - if (!reader) { - MS_LOG(DEBUG) << "TFReader operator failed to open file " << filename << "."; - } - - while (reader.peek() != EOF) { - // read length - int64_t record_length = 0; - (void)reader.read(reinterpret_cast(&record_length), static_cast(kTFRecordRecLenSize)); - - // ignore crc header - (void)reader.ignore(static_cast(kTFRecordHeadFootSize)); - - // ignore TFRecord file contents - (void)reader.ignore(static_cast(record_length)); - - // ignore crc footer - (void)reader.ignore(static_cast(kTFRecordHeadFootSize)); - (*rows_read)++; - } - reader.close(); -} - -#if !defined(_WIN32) && !defined(_WIN64) -void TFReaderOp::HelperCountGZIPRows(const std::string &realpath_value, const std::string &filename, - int64_t *rows_read) { - gzFile file = gzopen(realpath_value.c_str(), "rb"); - - if (file == nullptr) { - MS_LOG(DEBUG) << "TFReader operator failed to open file " << filename << " with GZIP."; - } - - while (gzeof(file) != 1) { - // read length - int64_t record_length = 0; - (void)gzread(file, reinterpret_cast(&record_length), kTFRecordRecLenSize); - if (record_length == 0) { - continue; - } - - // ignore crc header - (void)gzseek(file, kTFRecordHeadFootSize, SEEK_CUR); - - // ignore TFRecord file contents - (void)gzseek(file, record_length, SEEK_CUR); - - // ignore crc footer - (void)gzseek(file, kTFRecordHeadFootSize, SEEK_CUR); - (*rows_read)++; - } - (void)gzclose(file); -} - -void TFReaderOp::HelperCountZLIBRows(const std::string &realpath_value, const std::string &filename, - int64_t *rows_read) { - // ZLIB stream setup (based on zlib.h tutorial) - ZLIBStreamInf zlib_stream; - - std::ifstream reader(realpath_value.c_str(), std::ios::in | std::ios::binary); - - if (!reader) { - MS_LOG(DEBUG) << "TFReader operator failed to open file " << filename << " with ZLIB."; - } - - zlib_stream.inflate_status = inflateInit(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_OK) { - reader.close(); - MS_LOG(DEBUG) << "Failed to initialize inflate stream for ZLIB when counting rows for file " << filename << "!"; - } - - // decompress until first row is read - do { - (void)reader.read(zlib_stream.input_stream, kZLIBChunkSize); - zlib_stream.strm.avail_in = static_cast(reader.gcount()); - zlib_stream.strm.next_in = reinterpret_cast(zlib_stream.input_stream); - - // run inflate() on input until output buffer not full - do { - if (zlib_stream.left_to_read != 0) { - zlib_stream.strm.avail_out = zlib_stream.left_to_read; // need to read the rest before process - } else { - switch (zlib_stream.read_flag) { - case ZLIBReadFlag::RecordLength: // record length - zlib_stream.strm.avail_out = kTFRecordRecLenSize; - zlib_stream.strm.next_out = zlib_stream.record_size; - break; - default: // record header, example, and footer since we just want to count rows - zlib_stream.strm.avail_out = zlib_stream.record_length + kTFRecordHeadFootSize + kTFRecordHeadFootSize; - zlib_stream.content = std::make_unique(zlib_stream.record_length + kTFRecordHeadFootSize + - kTFRecordHeadFootSize); - zlib_stream.strm.next_out = zlib_stream.content.get(); - } - } - - // Inflate stream - zlib_stream.inflate_status = inflate(&zlib_stream.strm, Z_NO_FLUSH); - if (zlib_stream.inflate_status == Z_OK || zlib_stream.inflate_status == Z_STREAM_END) { - zlib_stream.left_to_read = zlib_stream.strm.avail_out; // after reading - } else { - MS_LOG(DEBUG) << "An error is found during inflation when counting rows for file: " << filename << "!"; - } - - if (zlib_stream.left_to_read != 0) { - break; - } - - // Process inflated data depending on read flag - if (zlib_stream.read_flag == static_cast(ZLIBReadFlag::RecordLength)) { // read record length - zlib_stream.record_length = HelperBinDataToInt(zlib_stream.record_size, kTFRecordRecLenSize); - } else if (zlib_stream.read_flag == static_cast(ZLIBReadFlag::Footer)) { - (*rows_read)++; - } - zlib_stream.read_flag = zlib_stream.read_flag == static_cast(ZLIBReadFlag::Footer) - ? static_cast(ZLIBReadFlag::RecordLength) - : static_cast(ZLIBReadFlag::Footer); // resets flag to reading record length - } while (zlib_stream.strm.avail_out == 0 && zlib_stream.inflate_status == Z_OK); - } while (zlib_stream.inflate_status != Z_STREAM_END && zlib_stream.inflate_status == Z_OK); - - (void)inflateEnd(&zlib_stream.strm); - if (zlib_stream.inflate_status != Z_STREAM_END) { - MS_LOG(DEBUG) << "Decompression of ZLIB file failed when counting rows for file " << filename << "!"; - } - - reader.close(); -} - -#endif - -Status TFReaderOp::ComputeColMap() { - // Construct the column name map for this operator (base class field) - if (column_name_id_map_.empty()) { - if (decode_) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - // if decode is false, the output will only have one column containing the record bytes - column_name_id_map_["proto"] = 0; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status TFReaderOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int32_t key_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool end_of_epoch = false; - if (shuffle_files_) { - do { - // Iterate over all the keys and add one key to each block. - for (auto i_key : i_keys) { - { - if (!GetLoadIoBlockQueue()) { - end_of_epoch = true; - break; - } - } - RETURN_IF_NOT_OK(HelperIOBlockFiller(&queue_index, &key_index, &pre_count, &start_offset, &end_offset, i_key, - (*filename_index_)[i_key])); - } - } while ((compression_type_ == CompressionType::NONE || compression_type_ == CompressionType::GZIP_WITH_COUNT || - compression_type_ == CompressionType::ZLIB_WITH_COUNT) && - equal_rows_per_shard_ && pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_ && - !end_of_epoch); - } else { - do { - // Iterate over all the keys and add one key to each block. - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - end_of_epoch = true; - break; - } - } - RETURN_IF_NOT_OK( - HelperIOBlockFiller(&queue_index, &key_index, &pre_count, &start_offset, &end_offset, it.key(), it.value())); - } - } while ((compression_type_ == CompressionType::NONE || compression_type_ == CompressionType::GZIP_WITH_COUNT || - compression_type_ == CompressionType::ZLIB_WITH_COUNT) && - equal_rows_per_shard_ && pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_ && - !end_of_epoch); - } - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -Status TFReaderOp::HelperIOBlockFiller(int32_t *queue_index, int32_t *key_index, int64_t *pre_count, - int64_t *start_offset, int64_t *end_offset, int64_t key, - const std::string &file_name) { - if (compression_type_ == CompressionType::GZIP || compression_type_ == CompressionType::ZLIB) { - int num_files_to_read = - static_cast(dataset_files_list_.size() - dataset_files_list_.size() % static_cast(num_devices_)); - if (*key_index % num_devices_ == device_id_ && *key_index < num_files_to_read) { - *end_offset = static_cast(total_rows_ / - static_cast(dataset_files_list_.size() / static_cast(num_devices_))); - auto ioBlock = std::make_unique(key, 0, *end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(*queue_index, std::move(ioBlock))); - *queue_index = (*queue_index + 1) % num_workers_; - } - (*key_index)++; - } else if (!equal_rows_per_shard_) { - if ((*key_index)++ % num_devices_ == device_id_) { - auto ioBlock = std::make_unique(key, kInvalidOffset, kInvalidOffset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(*queue_index, std::move(ioBlock))); - *queue_index = (*queue_index + 1) % num_workers_; - } - } else { - if (NeedPushFileToBlockQueue(file_name, start_offset, end_offset, *pre_count)) { - auto ioBlock = std::make_unique(key, *start_offset, *end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(*queue_index, std::move(ioBlock))); - *queue_index = (*queue_index + 1) % num_workers_; - } - - *pre_count += filename_numrows_[file_name]; - } - return Status::OK(); -} - -Status TFReaderOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - RETURN_IF_NOT_OK(NonMappableLeafOp::GetNextRowPullMode(row)); - if (decode_) { - if (!row->empty()) { - // data got from jagged_rows_connector is raw bytes so we need to parse it before return - TensorRow res; - RETURN_IF_NOT_OK(ParseExample(*row, &res)); - *row = std::move(res); - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h deleted file mode 100644 index 257561338..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h +++ /dev/null @@ -1,372 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TF_READER_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TF_READER_OP_H_ - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/util/auto_index.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace dataengine { -class Example; -class Feature; -class BytesList; -} // namespace dataengine - -namespace mindspore { -namespace dataset { -const std::streamsize kTFRecordRecLenSize = sizeof(int64_t); -const std::streamsize kTFRecordHeadFootSize = sizeof(int32_t); // header has same size with footer -const std::streamsize kZLIBChunkSize = 16384; - -template -class Queue; - -template -class Connector; - -class JaggedConnector; -class FilenameBlock; - -using StringIndex = AutoIndexObj; - -class TFReaderOp : public NonMappableLeafOp { - public: - /// \brief Constructor. - /// \param num_workers The number of worker threads for reading data. - /// \param worker_connector_size The size of each worker queue. - /// \param total_num_rows The Number of rows to read. - /// \param dataset_files_list The list of paths of dataset files to read. - /// \param data_schema The data schema descributing the feature names, dtypes and shapes. - /// \param op_connector_size The size of connector queue for the child node to read from. - /// \param columns_to_load The feature names to load from the files. - /// \param shuffle_files Whether to shuffle the files before reading. - /// \param num_devices The number of shards that the dataset will be divided into. - /// \param device_id Which part of dataset to read among all the shards. - /// \param equal_rows_per_shard Whether to read equal number of rows for each shard. - /// \param compression_type The compression type of the dataset files. - /// \param decode Whether to decode the protobuf, or leave it for ParseExampleOp to parse. - TFReaderOp(int32_t num_workers, int32_t worker_connector_size, int64_t total_num_rows, - std::vector dataset_files_list, std::unique_ptr data_schema, - int32_t op_connector_size, std::vector columns_to_load, bool shuffle_files, - int32_t num_devices, int32_t device_id, bool equal_rows_per_shard, const CompressionType &compression_type, - bool decode); - - /// Default destructor - ~TFReaderOp() override = default; - - /// A print method typically used for debugging - /// @param out - The output stream to write output to - /// @param show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// Instantiates the internal queues and connectors. - /// @return Status - the error code returned. - Status Init() override; - - /// \brief Implement the operator() method of this class for workers to run with. - /// \return Status code. - Status operator()() override; - - /// Reads all the provided TFRecord files and counts the total number of rows. filenames will - /// first be sectioned into equal parts, then sections are read in parallel. If threads is - /// greater than the number of files, threads will be clamped to the number of files. - /// When compression_type is provided, this should only be called when num_samples == 0. - /// @param out_total_rows - output parameter which contains the total number of rows - /// @param filenames - a list of TFRecord filenames. - /// @param threads - number of threads to use to read the TFRecord files. - /// @param estimate - estimate mode, under this mode each threads will sample a single file from each chunk - /// @param compression_type - compression type of the TFRecord files - /// @return Status - the error code returned. - static Status CountTotalRows(int64_t *out_total_rows, const std::vector &filenames, int64_t threads = 1, - bool estimate = false, CompressionType compression_type = CompressionType::NONE); - - /// Op name getter - /// @return Name of the current Op - std::string Name() const override { return "TFReaderOp"; } - - /// File names getter - /// @return Vector of the input file names - std::vector FileNames() { return dataset_files_list_; } - - /// \brief Get the next row in pull mode. - /// \param[in] row Fetched TensorRow. - /// \return Status code. - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - Status FillIOBlockQueue(const std::vector &i_keys) override; - - private: - /// \brief Register queues and launch workers. - /// \return Status code. - Status RegisterAndLaunchThreads() override; - - /// \brief The method for parsing workers to run with. - /// \param[in] worker_id Which worker to run. - /// \return Status code. - Status ParsingWorkerEntry(int32_t worker_id); - - /// \brief Parse the raw record bytes. - /// \param[in] raw_bytes The raw record bytes tensor row. - /// \param[out] parsed_row The parsed record tensor row. - /// \return Status code. - Status ParseExample(const TensorRow &raw_bytes, TensorRow *parsed_row); - - // Reads a TFRecord file and loads the data into multiple TensorRows. - // @param filename - the TFRecord file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status LoadFile(const std::string &filename, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - /// \brief Create a TensorRow with the given example string and send it to parsing workers. - /// \param[in] filename The file from which the example string originated. - /// \param[in] serialized_example The example string. - /// \param[in] worker_id Which worker to send to. - /// \return Status code. - Status SendRecordBytesRow(const std::string &filename, const std::string &serialized_example, int32_t worker_id); - - // Helper function to read a non-compressed TFRecord file and loads the data into multiple TensorRows. - // @param filename - the TFRecord file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @param realpath_value - the real path for filename. - // @return Status - the error code returned. - Status HelperLoadNonCompFile(const std::string &filename, int64_t start_offset, int64_t end_offset, int32_t worker_id, - const std::string &realpath_value); - -#if !defined(_WIN32) && !defined(_WIN64) - // ZLIBStream struct to initial ZLIB stream - typedef struct ZLIBStreamInflate { - z_stream strm; - char input_stream[kZLIBChunkSize]; // instream - unsigned char record_size[kTFRecordRecLenSize]; // in order to get record_length - unsigned char garbage[kTFRecordHeadFootSize]; // header and footer are ignored - std::unique_ptr content; // for serialized example - int64_t record_length; // the content's length - int read_flag; // flag to keep track what is currently being read - int64_t left_to_read; // number of bytes left to read for particular read_flag - int inflate_status; // in order to keep track of inflate status - - ZLIBStreamInflate() - : record_length(0), - read_flag(static_cast(ZLIBReadFlag::RecordLength)), - left_to_read(0), - inflate_status(static_cast(Z_OK)) { - // allocate inflate state - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - } - } ZLIBStreamInf; - - // Helper function to read a compressed GZIP TFRecord file and loads the data into multiple TensorRows. - // @param filename - the TFRecord file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @param realpath_value - the real path for filename. - // @return Status - the error code returned. - Status HelperLoadCompGZIPFile(const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id, const std::string &realpath_value); - - // Helper function to read a compressed ZLIB TFRecord file and loads the data into multiple TensorRows. - // @param filename - the TFRecord file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @param realpath_value - the real path for filename. - // @return Status - the error code returned. - Status HelperLoadCompZLIBFile(const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id, const std::string &realpath_value); - - // Helper function to convert binary data to int64_t. - // @param str_record_size - the binary data stored in string. - // @param str_size - length of binary data stored in string. - // @return int64_t - the integer. - static int64_t HelperBinDataToInt(const unsigned char *str_record_size, size_t str_size); - - // Helper function to inflate ZLIB stream - // @param zlib_stream - ZLIB stream. - // @param filename - TFRecord file name (for throwing error purposes) - // @return Status - the error code returned. - Status HelperInflateZLIB(ZLIBStreamInf *zlib_stream, const std::string &filename) const; - - // Helper function to process ZLIB data depending on the flag. - // @param zlib_stream - ZLIB stream. - // @param rows_read - number of rows that have been read (content only). - // @param rows_total - number of total rows that have been read. - // @param filename - the TFRecord file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status HelperProcessZLIBData(ZLIBStreamInf *zlib_stream, int64_t *rows_read, int64_t *rows_total, - const std::string &filename, int64_t start_offset, int64_t end_offset, - int32_t worker_id); - - // Helper function to count rows for GZIP compressed TFRecord file. - // @param realpath_value - the path for the file. - // @param filename - the TFRecord file to read. - // @param rows_read - number of rows that have been read (content only). - // @return void - static void HelperCountGZIPRows(const std::string &realpath_value, const std::string &filename, int64_t *rows_read); - - // Helper function to count rows for ZLIB compressed TFRecord file. - // @param realpath_value - the path for the file. - // @param filename - the TFRecord file to read. - // @param rows_read - number of rows that have been read (content only). - // @return void - static void HelperCountZLIBRows(const std::string &realpath_value, const std::string &filename, int64_t *rows_read); -#endif - - // Helper function to count rows for uncompressed TFRecord file. - // @param realpath_value - the path for the file. - // @param filename - the TFRecord file to read. - // @param rows_read - number of rows that have been read (content only). - // @return void - static void HelperCountNonCompRows(const std::string &realpath_value, const std::string &filename, - int64_t *rows_read); - - // Helper function to get serialized example for Schema. - // @param serialized_example - container to store the serialized_example. - // @param realpath_value - the path for the file. - // @param filename - TFRecord file name (for throwing error purposes) - // @return Status - the error code returned. - Status HelperGetExampleSchema(std::string *const serialized_example, const std::string &realpath_value, - const std::string &filename) const; - - // Parses a single row and puts the data into a tensor table. - // @param tf_record_file - the row to be parsed. - // @param tensor_table - the tensor table to put the parsed data in. - // @param row - the id of the row filled in the tensor table. - // @return Status - the error code returned. - Status LoadExample(const dataengine::Example *tf_record_file, TensorRow *out_row); - - // Parses a single cell and puts the data into a tensor table. - // @param tensor_table - the tensor table to put the parsed data in. - // @param column_values_list - the cell to parse. - // @param current_col - the column descriptor containing the expected shape and type of the data. - // @return Status - the error code returned. - Status LoadFeature(TensorRow *tensor_row, const dataengine::Feature &column_values_list, - const ColDescriptor ¤t_col, int32_t col); - - /// Reads values from a bytes list - /// @param current_col - the column descriptor containing the expected shape and type of the data. - /// @param column_values_list - the cell that contains the bytes list to read from. - /// @param elementStr - the string we read the value into. - /// @return Status - the error code returned. - static Status LoadBytesList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor); - - /// Reads values from a float list - /// @param current_col - the column descriptor containing the expected shape and type of the data. - /// @param column_values_list - the cell that contains the float list to read from. - /// @Param numElements - number of values in the float list. - /// @param float_array - the array we read the values into. - /// @return Status - the error code returned. - Status LoadFloatList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::unique_ptr *float_array); - - /// Reads values from a bytes list and casts the value to type T, must be an integral - /// type compatible with int64_t - /// @param current_col - the column descriptor containing the expected shape and type of the data. - /// @param column_values_list - the cell that contains the int list to read from. - /// @Param num_elements - number of values in the int list. - /// @param tensor - the tensor we read the values into. - /// @return Status - the error code returned. - template - Status LoadIntList(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor); - - /// Determines which template type to use and calls LoadIntList - /// @param current_col - the column descriptor containing the expected shape and type of the data. - /// @param column_values_list - the cell that contains the int list to read from. - /// @Param numElements - number of values in the int list. - /// @param tensor - the tensor we read the values into. - /// @return Status - the error code returned. - Status LoadIntListSwitch(const ColDescriptor ¤t_col, const dataengine::Feature &column_values_list, - int32_t *num_elements, std::shared_ptr *tensor); - - /// Reads one row of data from a tf file and creates a schema based on that row - /// @return Status - the error code returned. - Status CreateSchema(const std::string &tf_record_file, std::vector columns_to_load); - - /// Meant to be called async. Will read files in the range [begin, end) and return the total rows - /// When compression_type is provided, this should only be called when num_samples == 0. - /// @param filenames - a list of tf data filenames. - /// @param begin - index of first file to read. - /// @param end - one greater than the index of the last file to read. - /// @param compression_type - compression type of the TFRecord files - /// @return int63_t - the total number of rows of files read. - static int64_t CountTotalRowsSectioned(const std::vector &filenames, const int64_t begin, - const int64_t end, CompressionType compression_type = CompressionType::NONE); - - enum ZLIBReadFlag { RecordLength = 0, Header = 1, Content = 2, Footer = 3 }; - - // Helper to fill IO block queue - // @param queue_index - the queue index. - // @param key_index - current key_index. - // @param pre_count - to keep track total number of rows read so far. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param key - key to be put in the io block. - // @param file_name - the TFRecord file name based on the current key iteration. - // @return Status - the error code returned. - Status HelperIOBlockFiller(int32_t *queue_index, int32_t *key_index, int64_t *pre_count, int64_t *start_offset, - int64_t *end_offset, int64_t key, const std::string &file_name); - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - Status CalculateNumRowsPerShard() override; - - /// Private function for computing the assignment of the column name map. - /// @return - Status - Status ComputeColMap() override; - - std::vector dataset_files_list_; - std::vector columns_to_load_; - std::unique_ptr data_schema_; - bool equal_rows_per_shard_; - bool decode_; // whether to parse the proto -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_TF_READER_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.cc deleted file mode 100644 index 1cc9dfb18..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.cc +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -UDPOSOp::UDPOSOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr schema, const std::vector &udpos_files_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, total_rows, worker_connector_size, std::move(schema), udpos_files_list, op_connector_size, - shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void UDPOSOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nUDPOS files list:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status UDPOSOp::LoadTensor(const std::vector &column, TensorRow *out_row, size_t index) { - std::shared_ptr tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(column, &tensor)); - (*out_row)[index] = std::move(tensor); - return Status::OK(); -} - -// Function to split string based on a character delimiter. -std::vector UDPOSOp::Split(const std::string &s, char delim) { - std::vector res; - std::stringstream ss(s); - std::string item; - - while (getline(ss, item, delim)) { - res.push_back(item); - } - return res; -} - -// Removes excess space before and after the string. -std::string UDPOSOp::Strip(const std::string &str) { - size_t strlen = str.size(); - size_t i, j; - i = 0; - while (i < strlen && str[i] == ' ') { - i++; - } - j = strlen - 1; - while (j >= i && str[j] == ' ') { - j--; - } - j++; - if (i == 0 && j == strlen) { - return str; - } else { - return str.substr(i, j - i); - } -} - -Status UDPOSOp::Load(const std::vector &word, const std::vector &universal, - const std::vector &stanford, const std::string &file, int32_t worker_id) { - size_t row_line = 3; - size_t word_line = 0, universal_line = 1, stanford_line = 2; - TensorRow tRow(row_line, nullptr); - // Add file path info. - std::vector file_path(row_line, file); - tRow.setPath(file_path); - RETURN_IF_NOT_OK(LoadTensor(word, &tRow, word_line)); - RETURN_IF_NOT_OK(LoadTensor(universal, &tRow, universal_line)); - RETURN_IF_NOT_OK(LoadTensor(stanford, &tRow, stanford_line)); - RETURN_IF_NOT_OK(jagged_rows_connector_->Add(worker_id, std::move(tRow))); - return Status::OK(); -} - -Status UDPOSOp::LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - auto realpath = FileUtils::GetRealPath(file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " + DatasetName() + " dataset dir: " << file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + DatasetName() + " dataset dir: " + file + " does not exist."); - } - std::ifstream handle(realpath.value(), std::ios::in); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open " + DatasetName() + ": " + file); - } - int64_t rows_total = 0; - std::string line; - std::vector word_column; - std::vector universal_column; - std::vector stanford_column; - while (getline(handle, line)) { - if (line.empty() && rows_total < start_offset) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - if (word_column.size() != 0) { - auto s = Load(word_column, universal_column, stanford_column, file, worker_id); - if (s != Status::OK()) { - handle.close(); - return s; - } - } - std::vector().swap(word_column); - std::vector().swap(universal_column); - std::vector().swap(stanford_column); - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - line = Strip(line); - if (line.empty() && rows_total >= start_offset) { - if (word_column.size() != 0) { - auto s = Load(word_column, universal_column, stanford_column, file, worker_id); - if (s != Status::OK()) { - handle.close(); - return s; - } - } - std::vector().swap(word_column); - std::vector().swap(universal_column); - std::vector().swap(stanford_column); - continue; - } else if (!line.empty() && rows_total >= start_offset) { - std::vector column = Split(line, '\t'); - size_t right_column_size = 3; - if (column.size() < right_column_size) { - handle.close(); - MS_LOG(ERROR) - << "Invalid file content, each line should contain three columns, representing word, universal and stanford."; - RETURN_STATUS_UNEXPECTED( - "Invalid file content, each line should contain three columns, representing word, universal and stanford."); - } - size_t word_line = 0, universal_line = 1, stanford_line = 2; - word_column.push_back(column[word_line]); - universal_column.push_back(column[universal_line]); - stanford_column.push_back(column[stanford_line]); - } else { - handle.close(); - RETURN_STATUS_UNEXPECTED("[Internal ERROR], rows_total is less than start_offset."); - } - rows_total++; - } - handle.close(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h deleted file mode 100644 index 4dbdc3506..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_UDPOS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_UDPOS_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -class UDPOSOp : public TextFileOp { - public: - /// \Constructor of UDPOSOp. - UDPOSOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, std::unique_ptr, - const std::vector &udpos_files_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - /// \Default destructor. - ~UDPOSOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "UDPOSOp"; } - - // DatasetName name getter - /// \param[in] upper Needs to be capitalized or not - // \return DatasetName of the current Op - std::string DatasetName(bool upper = false) const { return upper ? "UDPOS" : "udpos"; } - - private: - /// \brief Parses a single row and puts the data into multiple TensorRows. - /// \param[in] column The content of the column. - /// \param[in] out_row The tensor table to put the parsed data in. - /// \param[in] index Serial number of column. - /// \return Status The error code returned. - Status LoadTensor(const std::vector &column, TensorRow *out_row, size_t index); - - /// \brief Removes excess space before and after the string. - /// \param[in] str The input string. - /// \return A string. - std::string Strip(const std::string &str); - - /// \brief Split string based on a character delimiter. - /// \param[in] s The input string. - /// \param[in] delim Symbols for separating string. - /// \return A string vector. - std::vector Split(const std::string &s, char delim); - - /// \brief Specify that the corresponding data is translated into Tensor. - /// \param[in] word A list of words in a sentence. - /// \param[in] universal General part of speech. - /// \param[in] stanford Stanford part of speech. - /// \param[in] file The file to read. - /// \param[in] worker_id The id of the worker that is executing this function. - /// \return Status The error code returned. - Status Load(const std::vector &word, const std::vector &universal, - const std::vector &stanford, const std::string &file, int32_t worker_id); - - /// \brief Reads a text file and loads the data into multiple TensorRows. - /// \param file The file to read. - /// \param start_offset The start offset of file. - /// \param end_offset The end offset of file. - /// \param worker_id The id of the worker that is executing this function. - /// \return Status The error code returned. - Status LoadFile(const std::string &file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_UDPOS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.cc deleted file mode 100644 index a5362c4a2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.cc +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr int64_t kUSPSImageHeight = 16; -constexpr int64_t kUSPSImageWidth = 16; -constexpr int64_t kUSPSImageChannel = 1; -constexpr int64_t kUSPSImageSize = kUSPSImageHeight * kUSPSImageWidth * kUSPSImageChannel; -constexpr int32_t kNumColumns = 2; - -USPSOp::USPSOp(const std::string &dataset_dir, const std::string &usage, std::unique_ptr data_schema, - int32_t num_workers, int32_t worker_connector_size, int64_t num_samples, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id) - : NonMappableLeafOp(num_workers, worker_connector_size, num_samples, op_connector_size, shuffle_files, num_devices, - device_id), - usage_(usage), - dataset_dir_(dataset_dir), - data_schema_(std::move(data_schema)) {} - -void USPSOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nUSPS directory: " << dataset_dir_ - << "\nUSPS usage: " << usage_ << "\n\n"; - out << "\nData schema:\n"; - out << *data_schema_ << "\n\n"; - } -} - -Status USPSOp::Init() { - RETURN_IF_NOT_OK(this->GetFiles()); - RETURN_IF_NOT_OK(filename_index_->insert(data_files_list_)); - - int32_t safe_queue_size = static_cast(std::ceil(data_files_list_.size() / num_workers_) + 1); - io_block_queues_.Init(num_workers_, safe_queue_size); - - jagged_rows_connector_ = std::make_unique(num_workers_, 1, worker_connector_size_); - return Status::OK(); -} - -Status USPSOp::CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - *count = 0; - - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t num_workers = cfg->num_parallel_workers(); - int32_t op_connector_size = cfg->op_connector_size(); - int32_t worker_connector_size = cfg->worker_connector_size(); - - const int64_t num_samples = 0; - const int32_t num_devices = 1; - const int32_t device_id = 0; - bool shuffle = false; - - auto op = std::make_shared(dir, usage, std::move(schema), num_workers, worker_connector_size, num_samples, - op_connector_size, shuffle, num_devices, device_id); - RETURN_IF_NOT_OK(op->Init()); - // the logic of counting the number of samples - for (auto data_file : op->FileNames()) { - *count += op->CountRows(data_file); - } - return Status::OK(); -} - -int64_t USPSOp::CountRows(const std::string &data_file) const { - auto data_file_realpath = FileUtils::GetRealPath(data_file.c_str()); - if (!data_file_realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << data_file << " does not exist."; - return 0; - } - std::ifstream data_file_reader; - data_file_reader.open(data_file_realpath.value(), std::ios::in); - if (!data_file_reader.is_open()) { - MS_LOG(ERROR) << "Invalid file, failed to open " << data_file << ": the file is permission denied."; - return 0; - } - - std::string line; - int64_t count = 0; - while (std::getline(data_file_reader, line)) { - if (!line.empty()) { - count++; - } - } - data_file_reader.close(); - return count; -} - -Status USPSOp::GetFiles() { - auto real_dataset_dir = FileUtils::GetRealPath(dataset_dir_.c_str()); - CHECK_FAIL_RETURN_UNEXPECTED(real_dataset_dir.has_value(), - "Invalid file path, USPS dataset dir: " + dataset_dir_ + " does not exist."); - Path root_dir(real_dataset_dir.value()); - - const Path train_file_name("usps"); - const Path test_file_name("usps.t"); - - bool use_train = false; - bool use_test = false; - - if (usage_ == "train") { - use_train = true; - } else if (usage_ == "test") { - use_test = true; - } else if (usage_ == "all") { - use_train = true; - use_test = true; - } - - if (use_train) { - Path train_path = root_dir / train_file_name; - CHECK_FAIL_RETURN_UNEXPECTED( - train_path.Exists() && !train_path.IsDirectory(), - "Invalid file, USPS dataset train file: " + train_path.ToString() + " does not exist or is a directory."); - data_files_list_.emplace_back(train_path.ToString()); - MS_LOG(INFO) << "USPS operator found train data file " << train_path.ToString() << "."; - } - - if (use_test) { - Path test_path = root_dir / test_file_name; - CHECK_FAIL_RETURN_UNEXPECTED( - test_path.Exists() && !test_path.IsDirectory(), - "Invalid file, USPS dataset test file: " + test_path.ToString() + " does not exist or is a directory."); - data_files_list_.emplace_back(test_path.ToString()); - MS_LOG(INFO) << "USPS operator found test data file " << test_path.ToString() << "."; - } - return Status::OK(); -} - -Status USPSOp::LoadFile(const std::string &data_file, int64_t start_offset, int64_t end_offset, int32_t worker_id) { - std::ifstream data_file_reader(data_file); - if (!data_file_reader.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open USPS dataset file: " + data_file + - ", the file is permission denied."); - } - - int64_t rows_total = 0; - std::string line; - - while (getline(data_file_reader, line)) { - if (line.empty()) { - continue; - } - // If read to the end offset of this file, break. - if (rows_total >= end_offset) { - break; - } - // Skip line before start offset. - if (rows_total < start_offset) { - rows_total++; - continue; - } - - TensorRow tRow(kNumColumns, nullptr); - tRow.setPath({data_file, data_file}); - Status rc = LoadTensor(&line, &tRow); - if (rc.IsError()) { - data_file_reader.close(); - return rc; - } - rc = jagged_rows_connector_->Add(worker_id, std::move(tRow)); - if (rc.IsError()) { - data_file_reader.close(); - return rc; - } - - rows_total++; - } - - data_file_reader.close(); - return Status::OK(); -} - -Status USPSOp::LoadTensor(std::string *line, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(line); - RETURN_UNEXPECTED_IF_NULL(trow); - - auto images_buffer = std::make_unique(kUSPSImageSize); - auto labels_buffer = std::make_unique(1); - if (images_buffer == nullptr || labels_buffer == nullptr) { - MS_LOG(ERROR) << "[Internal ERROR] Failed to allocate memory for USPS buffer."; - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Failed to allocate memory for USPS buffer."); - } - - RETURN_IF_NOT_OK(this->ParseLine(line, images_buffer, labels_buffer)); - - // create tensor - std::shared_ptr image, label; - TensorShape image_tensor_shape = TensorShape({kUSPSImageHeight, kUSPSImageWidth, kUSPSImageChannel}); - auto pixels = &images_buffer[0]; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(image_tensor_shape, data_schema_->Column(0).Type(), - reinterpret_cast(pixels), &image)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(labels_buffer[0], &label)); - - (*trow) = {std::move(image), std::move(label)}; - return Status::OK(); -} - -Status USPSOp::ParseLine(std::string *line, const std::unique_ptr &images_buffer, - const std::unique_ptr &labels_buffer) const { - auto label = &labels_buffer[0]; - auto pixels = &images_buffer[0]; - - size_t pos = 0; - int32_t split_num = 0; - while ((pos = line->find(" ")) != std::string::npos) { - split_num += 1; - std::string item = line->substr(0, pos); - - if (split_num == 1) { - // the class label is 1~10 but we need 0~9 - *label = static_cast(std::stoi(item)) - 1; - } else { - size_t split_pos = item.find(":"); - - CHECK_FAIL_RETURN_UNEXPECTED(split_pos != std::string::npos, - "Invalid data, split character ':' is missing in USPS data file."); - // check pixel index - CHECK_FAIL_RETURN_UNEXPECTED(std::stoi(item.substr(0, split_pos)) == (split_num - 1), - "Invalid data, the character before ':' should be " + std::to_string(split_num - 1) + - ", but got " + item.substr(0, split_pos) + "."); - - std::string pixel_str = item.substr(split_pos + 1, item.length() - split_pos); - // transform the real pixel value from [-1, 1] to the integers within [0, 255] - pixels[split_num - 2] = static_cast((std::stof(pixel_str) + 1.0) / 2.0 * 255.0); - } - line->erase(0, pos + 1); - } - - CHECK_FAIL_RETURN_UNEXPECTED(split_num == (kUSPSImageSize + 1), - "Invalid data, the number of split characters ':' in USPS data file is corrupted, " - "should be " + - std::to_string(kUSPSImageSize + 1) + ", but got " + std::to_string(split_num) + "."); - return Status::OK(); -} - -Status USPSOp::CalculateNumRowsPerShard() { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - int64_t count = CountRows(it.value()); - filename_numrows_[it.value()] = count; - num_rows_ += count; - } - if (num_rows_ == 0) { - std::stringstream ss; - for (size_t i = 0; i < data_files_list_.size(); ++i) { - ss << " " << data_files_list_[i]; - } - std::string file_list = ss.str(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, 'USPSDataset' API can't read the data file (interface mismatch or no data found). " - "Check file: " + - file_list); - } - - num_rows_per_shard_ = static_cast(std::ceil(num_rows_ * 1.0 / num_devices_)); - MS_LOG(DEBUG) << "Number rows per shard is " << num_rows_per_shard_; - return Status::OK(); -} - -Status USPSOp::FillIOBlockQueue(const std::vector &i_keys) { - int32_t queue_index = 0; - int64_t pre_count = 0; - int64_t start_offset = 0; - int64_t end_offset = 0; - bool finish = false; - while (!finish) { - std::vector> file_index; - if (!i_keys.empty()) { - for (auto it = i_keys.begin(); it != i_keys.end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair((*filename_index_)[*it], *it)); - } - } else { - for (auto it = filename_index_->begin(); it != filename_index_->end(); ++it) { - { - if (!GetLoadIoBlockQueue()) { - break; - } - } - file_index.emplace_back(std::pair(it.value(), it.key())); - } - } - for (auto file_info : file_index) { - if (NeedPushFileToBlockQueue(file_info.first, &start_offset, &end_offset, pre_count)) { - auto ioBlock = std::make_unique(file_info.second, start_offset, end_offset, IOBlock::kFlagNone); - RETURN_IF_NOT_OK(PushIoBlockQueue(queue_index, std::move(ioBlock))); - queue_index = (queue_index + 1) % num_workers_; - } - - pre_count += filename_numrows_[file_info.first]; - } - - if (pre_count < (static_cast(device_id_) + 1) * num_rows_per_shard_) { - finish = false; - } else { - finish = true; - } - } - - RETURN_IF_NOT_OK(PostEndOfEpoch(queue_index)); - return Status::OK(); -} - -Status USPSOp::ComputeColMap() { - // set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h deleted file mode 100644 index b3d5b34a5..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_USPS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_USPS_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" - -namespace mindspore { -namespace dataset { -class USPSOp : public NonMappableLeafOp { - public: - // Constructor. - // @param const std::string &dataset_dir - dir directory of USPS data file. - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all'. - // @param std::unique_ptr data_schema - the schema of the USPS dataset. - // @param num_workers - number of worker threads reading data from tf_file files. - // @param worker_connector_size - size of each internal queue. - // @param num_samples - number of samples to read. - // @param op_connector_size - size of each queue in the connector that the child operator pulls from. - // @param shuffle_files - whether to shuffle the files before reading data. - // @param num_devices - number of devices. - // @param device_id - device id. - USPSOp(const std::string &dataset_dir, const std::string &usage, std::unique_ptr data_schema, - int32_t num_workers, int32_t worker_connector_size, int64_t num_samples, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id); - - // Destructor. - ~USPSOp() = default; - - // Op name getter. - // @return std::string - Name of the current Op. - std::string Name() const override { return "USPSOp"; } - - // A print method typically used for debugging. - // @param std::ostream &out - out stream. - // @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - // Instantiates the internal queues and connectors - // @return Status - the error code returned. - Status Init() override; - - // Function to count the number of samples in the USPS dataset. - // @param const std::string &dir - path to the USPS directory. - // @param const std::string &usage - Usage of this dataset, can be 'train', 'test' or 'all'. - // @param int64_t *count - output arg that will hold the minimum of the actual dataset size and numSamples. - // @return Status - the error coed returned. - static Status CountTotalRows(const std::string &dir, const std::string &usage, int64_t *count); - - // File names getter. - // @return Vector of the input file names. - std::vector FileNames() { return data_files_list_; } - - private: - // Function to count the number of samples in one data file. - // @param const std::string &data_file - path to the data file. - // @return int64_t - the count result. - int64_t CountRows(const std::string &data_file) const; - - // Reads a data file and loads the data into multiple TensorRows. - // @param data_file - the data file to read. - // @param start_offset - the start offset of file. - // @param end_offset - the end offset of file. - // @param worker_id - the id of the worker that is executing this function. - // @return Status - the error code returned. - Status LoadFile(const std::string &data_file, int64_t start_offset, int64_t end_offset, int32_t worker_id) override; - - // Parses a single row and puts the data into a tensor table. - // @param line - the content of the row. - // @param trow - image & label read into this tensor row. - // @return Status - the error code returned. - Status LoadTensor(std::string *line, TensorRow *trow); - - // Calculate number of rows in each shard. - // @return Status - the error code returned. - Status CalculateNumRowsPerShard() override; - - // Fill the IOBlockQueue. - // @param i_keys - keys of file to fill to the IOBlockQueue. - // @return Status - the error code returned. - Status FillIOBlockQueue(const std::vector &i_keys) override; - - // Get all files in the dataset_dir_. - // @return Status - The status code returned. - Status GetFiles(); - - // Parse a line to image and label. - // @param line - the content of the row. - // @param images_buffer - image destination. - // @param labels_buffer - label destination. - // @return Status - the status code returned. - Status ParseLine(std::string *line, const std::unique_ptr &images_buffer, - const std::unique_ptr &labels_buffer) const; - - // Private function for computing the assignment of the column name map. - // @return Status - the error code returned. - Status ComputeColMap() override; - - const std::string usage_; // can be "all", "train" or "test". - std::string dataset_dir_; // directory of data files. - std::unique_ptr data_schema_; - - std::vector data_files_list_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_USPS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.cc deleted file mode 100644 index b2586acb9..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.cc +++ /dev/null @@ -1,380 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const char kColumnImage[] = "image"; -const char kColumnTarget[] = "target"; -const char kColumnBbox[] = "bbox"; -const char kColumnLabel[] = "label"; -const char kColumnDifficult[] = "difficult"; -const char kColumnTruncate[] = "truncate"; -const char kJPEGImagesFolder[] = "/JPEGImages/"; -const char kSegmentationClassFolder[] = "/SegmentationClass/"; -const char kAnnotationsFolder[] = "/Annotations/"; -const char kImageSetsSegmentation[] = "/ImageSets/Segmentation/"; -const char kImageSetsMain[] = "/ImageSets/Main/"; -const char kImageExtension[] = ".jpg"; -const char kSegmentationExtension[] = ".png"; -const char kAnnotationExtension[] = ".xml"; -const char kImageSetsExtension[] = ".txt"; - -#ifdef ENABLE_PYTHON -VOCOp::VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, - const std::map &class_index, int32_t num_workers, int32_t queue_size, bool decode, - std::unique_ptr data_schema, std::shared_ptr sampler, bool extra_metadata, - py::function decrypt) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - decode_(decode), - row_cnt_(0), - task_type_(task_type), - usage_(task_mode), - folder_path_(folder_path), - class_index_(class_index), - data_schema_(std::move(data_schema)), - extra_metadata_(extra_metadata), - decrypt_(std::move(decrypt)) {} -#else -VOCOp::VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, - const std::map &class_index, int32_t num_workers, int32_t queue_size, bool decode, - std::unique_ptr data_schema, std::shared_ptr sampler, bool extra_metadata) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - decode_(decode), - row_cnt_(0), - task_type_(task_type), - usage_(task_mode), - folder_path_(folder_path), - class_index_(class_index), - data_schema_(std::move(data_schema)), - extra_metadata_(extra_metadata) {} -#endif - -void VOCOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nNumber of rows: " << num_rows_ << "\nVOC Directory: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status VOCOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string image_id = image_ids_[row_id]; - std::vector path_list; - const std::string kImageFile = - folder_path_ + std::string(kJPEGImagesFolder) + image_id + std::string(kImageExtension); - if (task_type_ == TaskType::Segmentation) { - std::shared_ptr image, target; - const std::string kTargetFile = - folder_path_ + std::string(kSegmentationClassFolder) + image_id + std::string(kSegmentationExtension); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile, data_schema_->Column(0), &image)); - RETURN_IF_NOT_OK(ReadImageToTensor(kTargetFile, data_schema_->Column(1), &target)); - (*trow) = TensorRow(row_id, {std::move(image), std::move(target)}); - path_list = {kImageFile, kTargetFile}; - } else if (task_type_ == TaskType::Detection) { - std::shared_ptr image; - TensorRow annotation; - const std::string kAnnotationFile = - folder_path_ + std::string(kAnnotationsFolder) + image_id + std::string(kAnnotationExtension); - RETURN_IF_NOT_OK(ReadImageToTensor(kImageFile, data_schema_->Column(0), &image)); - RETURN_IF_NOT_OK(ReadAnnotationToTensor(kAnnotationFile, &annotation)); - trow->setId(row_id); - trow->push_back(std::move(image)); - trow->insert(trow->end(), annotation.begin(), annotation.end()); - path_list = {kImageFile, kAnnotationFile, kAnnotationFile, kAnnotationFile, kAnnotationFile}; - } - if (extra_metadata_) { - // Now VOCDataset add a new column named "_meta-filename". - std::shared_ptr filename; - RETURN_IF_NOT_OK(Tensor::CreateScalar(image_id, &filename)); - trow->push_back(std::move(filename)); - path_list.push_back(kImageFile); - } - trow->setPath(path_list); - return Status::OK(); -} - -Status VOCOp::ParseImageIds() { - if (!image_ids_.empty()) { - return Status::OK(); - } - std::string image_sets_file; - if (task_type_ == TaskType::Segmentation) { - image_sets_file = folder_path_ + std::string(kImageSetsSegmentation) + usage_ + std::string(kImageSetsExtension); - } else if (task_type_ == TaskType::Detection) { - image_sets_file = folder_path_ + std::string(kImageSetsMain) + usage_ + std::string(kImageSetsExtension); - } - - auto realpath = FileUtils::GetRealPath(image_sets_file.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << image_sets_file << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + image_sets_file + " does not exist."); - } - - std::ifstream in_file; - in_file.open(realpath.value(), std::ios::in); - if (in_file.fail()) { - RETURN_STATUS_UNEXPECTED("Invalid ImageSets file, failed to open ImageSets file: " + image_sets_file + - ", the file is damaged or permission denied."); - } - std::string id; - while (getline(in_file, id)) { - if (id.size() > 0 && id[id.size() - 1] == '\r') { - image_ids_.push_back(id.substr(0, id.size() - 1)); - } else { - image_ids_.push_back(id); - } - } - in_file.close(); - image_ids_.shrink_to_fit(); - num_rows_ = image_ids_.size(); - return Status::OK(); -} - -Status VOCOp::ParseAnnotationIds() { - std::vector new_image_ids; - for (auto id : image_ids_) { - const std::string annotation_name = - folder_path_ + std::string(kAnnotationsFolder) + id + std::string(kAnnotationExtension); - RETURN_IF_NOT_OK(ParseAnnotationBbox(annotation_name)); - if (annotation_map_.find(annotation_name) != annotation_map_.end()) { - new_image_ids.push_back(id); - } - } - - if (image_ids_.size() != new_image_ids.size()) { - image_ids_.clear(); - image_ids_.insert(image_ids_.end(), new_image_ids.begin(), new_image_ids.end()); - } - uint32_t count = 0; - for (auto &label : label_index_) { - label.second = count++; - } - - num_rows_ = image_ids_.size(); - if (num_rows_ == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, VOCDataset API can't read the data file (interface mismatch or no data found). " - "Check file in directory:" + - folder_path_); - } - return Status::OK(); -} - -void VOCOp::ParseNodeValue(XMLElement *bbox_node, const char *name, float *value) { - *value = 0.0; - if (bbox_node != nullptr) { - XMLElement *node = bbox_node->FirstChildElement(name); - if (node != nullptr) { - *value = node->FloatText(); - } - } -} - -Status VOCOp::CheckIfBboxValid(const float &xmin, const float &ymin, const float &xmax, const float &ymax, - const std::string &path) { - if (!(xmin > 0 && ymin > 0 && xmax > xmin && ymax > ymin)) { - std::string invalid_bbox = "{" + std::to_string(static_cast(xmin)) + ", " + - std::to_string(static_cast(ymin)) + ", " + std::to_string(static_cast(xmax)) + - ", " + std::to_string(static_cast(ymax)) + "}"; - RETURN_STATUS_UNEXPECTED("Invalid bndbox, the coordinate of bndbox in " + path + - " should be greater than 0, but got " + invalid_bbox); - } - return Status::OK(); -} - -Status VOCOp::ParseAnnotationBbox(const std::string &path) { - if (!Path(path).Exists()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + path + " does not exist."); - } - Annotation annotation; - XMLDocument doc; - XMLError e = doc.LoadFile(common::SafeCStr(path)); - if (e != XMLError::XML_SUCCESS) { - RETURN_STATUS_UNEXPECTED("Invalid xml, failed to load " + path + ": the xml file is damaged or incorrect format."); - } - XMLElement *root = doc.RootElement(); - if (root == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid xml, failed to load root element of " + path + - ": the format of xml file is incorrect."); - } - XMLElement *object = root->FirstChildElement("object"); - if (object == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid xml, the node of object is missing in " + path + "."); - } - while (object != nullptr) { - std::string label_name; - float xmin = 0.0, ymin = 0.0, xmax = 0.0, ymax = 0.0, truncated = 0.0, difficult = 0.0; - XMLElement *name_node = object->FirstChildElement("name"); - if (name_node != nullptr && name_node->GetText() != nullptr) { - label_name = name_node->GetText(); - } - ParseNodeValue(object, "difficult", &difficult); - ParseNodeValue(object, "truncated", &truncated); - - XMLElement *bbox_node = object->FirstChildElement("bndbox"); - if (bbox_node != nullptr) { - ParseNodeValue(bbox_node, "xmin", &xmin); - ParseNodeValue(bbox_node, "xmax", &xmax); - ParseNodeValue(bbox_node, "ymin", &ymin); - ParseNodeValue(bbox_node, "ymax", &ymax); - RETURN_IF_NOT_OK(CheckIfBboxValid(xmin, ymin, xmax, ymax, path)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid xml, the node of bndbox is missing in " + path); - } - - if (label_name != "" && (class_index_.empty() || class_index_.find(label_name) != class_index_.end()) && xmin > 0 && - ymin > 0 && xmax > xmin && ymax > ymin) { - std::vector bbox_list = {xmin, ymin, xmax - xmin, ymax - ymin, difficult, truncated}; - annotation.emplace_back(std::make_pair(label_name, bbox_list)); - label_index_[label_name] = 0; - } - object = object->NextSiblingElement("object"); - } - if (annotation.size() > 0) { - annotation_map_[path] = annotation; - } - return Status::OK(); -} - -Status VOCOp::PrepareData() { - RETURN_IF_NOT_OK(this->ParseImageIds()); - if (task_type_ == TaskType::Detection) { - RETURN_IF_NOT_OK(this->ParseAnnotationIds()); - } - return Status::OK(); -} - -Status VOCOp::ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor) { -#ifdef ENABLE_PYTHON - RETURN_IF_NOT_OK(MappableLeafOp::ImageDecrypt(path, tensor, decrypt_)); -#else - RETURN_IF_NOT_OK(Tensor::CreateFromFile(path, tensor)); -#endif - if (decode_ == true) { - Status rc = Decode(*tensor, tensor); - if (rc.IsError()) { - RETURN_STATUS_UNEXPECTED("Invalid image, failed to decode " + path + - ": the image is damaged or permission denied."); - } - } - return Status::OK(); -} - -// When task is Detection, user can get bbox data with four columns: -// column ["bbox"] with datatype=float32 -// column ["label"] with datatype=uint32 -// column ["difficult"] with datatype=uint32 -// column ["truncate"] with datatype=uint32 -Status VOCOp::ReadAnnotationToTensor(const std::string &path, TensorRow *row) { - Annotation annotation = annotation_map_[path]; - std::shared_ptr bbox, label, difficult, truncate; - std::vector bbox_data; - std::vector label_data, difficult_data, truncate_data; - dsize_t bbox_num = 0; - for (auto item : annotation) { - if (label_index_.find(item.first) != label_index_.end()) { - if (class_index_.find(item.first) != class_index_.end()) { - label_data.push_back(static_cast(class_index_[item.first])); - } else { - label_data.push_back(static_cast(label_index_[item.first])); - } - CHECK_FAIL_RETURN_UNEXPECTED( - item.second.size() == 6, - "[Internal ERROR] annotation only support 6 parameters, but got " + std::to_string(item.second.size())); - - std::vector tmp_bbox = {(item.second)[0], (item.second)[1], (item.second)[2], (item.second)[3]}; - bbox_data.insert(bbox_data.end(), tmp_bbox.begin(), tmp_bbox.end()); - difficult_data.push_back(static_cast((item.second)[4])); - truncate_data.push_back(static_cast((item.second)[5])); - bbox_num++; - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(bbox_data, TensorShape({bbox_num, 4}), &bbox)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(label_data, TensorShape({bbox_num, 1}), &label)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(difficult_data, TensorShape({bbox_num, 1}), &difficult)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(truncate_data, TensorShape({bbox_num, 1}), &truncate)); - (*row) = TensorRow({std::move(bbox), std::move(label), std::move(difficult), std::move(truncate)}); - return Status::OK(); -} - -Status VOCOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - switch (task_type_) { - case TaskType::Detection: - RETURN_IF_NOT_OK(ParseImageIds()); - RETURN_IF_NOT_OK(ParseAnnotationIds()); - break; - case TaskType::Segmentation: - RETURN_IF_NOT_OK(ParseImageIds()); - break; - } - *count = static_cast(image_ids_.size()); - return Status::OK(); -} - -Status VOCOp::ComputeColMap() { - // Set the column name map (base class field) - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status VOCOp::GetClassIndexing(std::vector>> *output_class_indexing) { - RETURN_UNEXPECTED_IF_NULL(output_class_indexing); - if ((*output_class_indexing).empty()) { - if (task_type_ != TaskType::Detection) { - MS_LOG(ERROR) << "Invalid task, only 'Detection' task support GetClassIndexing."; - RETURN_STATUS_UNEXPECTED("Invalid task, only 'Detection' task support GetClassIndexing."); - } - RETURN_IF_NOT_OK(ParseImageIds()); - RETURN_IF_NOT_OK(ParseAnnotationIds()); - for (const auto &label : label_index_) { - if (!class_index_.empty()) { - (*output_class_indexing) - .emplace_back(std::make_pair(label.first, std::vector(1, class_index_[label.first]))); - } else { - (*output_class_indexing).emplace_back(std::make_pair(label.first, std::vector(1, label.second))); - } - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h deleted file mode 100644 index 5345c161e..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_VOC_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_VOC_OP_H_ - -#include -#include -#include -#include -#include - -#include "./tinyxml2.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -using tinyxml2::XMLDocument; -using tinyxml2::XMLElement; -using tinyxml2::XMLError; -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -using Annotation = std::vector>>; - -class VOCOp : public MappableLeafOp { - public: - enum class TaskType { Segmentation = 0, Detection = 1 }; - -#ifdef ENABLE_PYTHON - // Constructor - // @param TaskType task_type - task type of VOC - // @param std::string task_mode - task mode of VOC - // @param std::string folder_path - dir directory of VOC - // @param std::map class_index - input class-to-index of annotation - // @param int32_t num_workers - number of workers reading images in parallel - // @param int32_t queue_size - connector queue size - // @param bool decode - whether to decode images - // @param std::unique_ptr data_schema - the schema of the VOC dataset - // @param std::shared_ptr sampler - sampler tells VOCOp what to read - // @param extra_metadata - flag to add extra meta-data to row - // @param py::function decrypt - Image decryption function, which accepts the path of the encrypted image file - // and returns the decrypted bytes data. Default: None, no decryption. - VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, - const std::map &class_index, int32_t num_workers, int32_t queue_size, bool decode, - std::unique_ptr data_schema, std::shared_ptr sampler, bool extra_metadata, - py::function decrypt = py::none()); -#else - // Constructor - // @param TaskType task_type - task type of VOC - // @param std::string task_mode - task mode of VOC - // @param std::string folder_path - dir directory of VOC - // @param std::map class_index - input class-to-index of annotation - // @param int32_t num_workers - number of workers reading images in parallel - // @param int32_t queue_size - connector queue size - // @param bool decode - whether to decode images - // @param std::unique_ptr data_schema - the schema of the VOC dataset - // @param std::shared_ptr sampler - sampler tells VOCOp what to read - // @param extra_metadata - flag to add extra meta-data to row - VOCOp(const TaskType &task_type, const std::string &task_mode, const std::string &folder_path, - const std::map &class_index, int32_t num_workers, int32_t queue_size, bool decode, - std::unique_ptr data_schema, std::shared_ptr sampler, bool extra_metadata); -#endif - - // Destructor - ~VOCOp() = default; - - // A print method typically used for debugging - // @param out - // @param show_all - void Print(std::ostream &out, bool show_all) const override; - - // @param int64_t *count - output rows number of VOCDataset - Status CountTotalRows(int64_t *count); - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return "VOCOp"; } - - // /// \brief Gets the class indexing - // /// \return Status - The status code return - Status GetClassIndexing(std::vector>> *output_class_indexing) override; - - private: - // Load a tensor row according to image id - // @param row_id_type row_id - id for this tensor row - // @param std::string image_id - image id - // @param TensorRow row - image & target read into this tensor row - // @return Status The status code returned - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - // @param const std::string &path - path to the image file - // @param const ColDescriptor &col - contains tensor implementation and datatype - // @param std::shared_ptr tensor - return - // @return Status The status code returned - Status ReadImageToTensor(const std::string &path, const ColDescriptor &col, std::shared_ptr *tensor); - - // @param const std::string &path - path to the image file - // @param TensorRow *row - return - // @return Status The status code returned - Status ReadAnnotationToTensor(const std::string &path, TensorRow *row); - - // Read image list from ImageSets - // @return Status The status code returned - Status ParseImageIds(); - - // Read annotation from Annotation folder - // @return Status The status code returned - Status ParseAnnotationIds(); - - // @param const std::string &path - path to annotation xml - // @return Status The status code returned - Status ParseAnnotationBbox(const std::string &path); - - // @param xmin - the left coordinate of bndbox - // @param ymin - the top coordinate of bndbox - // @param xmax - the right coordinate of bndbox - // @param ymax - the bottom coordinate of bndbox - // @param path - the file path of bndbox xml - // @return Status The status code returned - Status CheckIfBboxValid(const float &xmin, const float &ymin, const float &xmax, const float &ymax, - const std::string &path); - - // @param XMLElement *bbox_node - bbox node info found in json object - // @param const char *name - sub node name in object - // @param float *value - value of certain sub node - // @return Status The status code returned - void ParseNodeValue(XMLElement *bbox_node, const char *name, float *value); - - // Private function for computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; - - protected: - Status PrepareData() override; - - private: - bool decode_; - int64_t row_cnt_; - std::string folder_path_; - TaskType task_type_; - std::string usage_; - std::unique_ptr data_schema_; - bool extra_metadata_; - - std::vector image_ids_; - std::map class_index_; - std::map label_index_; - std::map annotation_map_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_VOC_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.cc deleted file mode 100644 index 872ac044b..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.cc +++ /dev/null @@ -1,304 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. -*/ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -constexpr char kSplitPath[] = "wider_face_split"; -constexpr char kTrainAnno[] = "wider_face_train_bbx_gt.txt"; -constexpr char kValAnno[] = "wider_face_val_bbx_gt.txt"; -constexpr char kTestAnno[] = "wider_face_test_filelist.txt"; -constexpr char kTrainBase[] = "WIDER_train"; -constexpr char kValBase[] = "WIDER_val"; -constexpr char kTestBase[] = "WIDER_test"; -constexpr char kImage[] = "images"; -constexpr char kExtension[] = ".jpg"; -constexpr int kDataLen = 10; // Length of each data row. -constexpr int kBboxLen = 4; // Length of bbox in annotation vector. -constexpr int kBlurIndex = 4; // Index of blur in annotation vector. -constexpr int kExpressionIndex = 5; // Index of expression in annotation vector. -constexpr int kIlluminationIndex = 6; // Index of illumination in annotation vector. -constexpr int kOcclusionIndex = 7; // Index of occlusion in annotation vector. -constexpr int kPoseIndex = 8; // Index of pose in annotation vector. -constexpr int kInvalidIndex = 9; // Index of invalid in annotation vector. - -WIDERFaceOp::WIDERFaceOp(const std::string &folder_path, const std::string &usage, int32_t num_workers, - int32_t queue_size, bool decode, std::unique_ptr data_schema, - std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - folder_path_(folder_path), - decode_(decode), - usage_(usage), - data_schema_(std::move(data_schema)) {} - -Status WIDERFaceOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(folder_path_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, WIDERFace dataset dir: " << folder_path_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, WIDERFace dataset dir: " + folder_path_ + " does not exist."); - } - std::string train_folder = (Path(realpath.value()) / Path(kTrainBase) / Path(kImage)).ToString(); - std::string val_folder = (Path(realpath.value()) / Path(kValBase) / Path(kImage)).ToString(); - std::string test_folder = (Path(realpath.value()) / Path(kTestBase) / Path(kImage)).ToString(); - std::string train_anno_dir = (Path(realpath.value()) / Path(kSplitPath) / Path(kTrainAnno)).ToString(); - std::string val_anno_dir = (Path(realpath.value()) / Path(kSplitPath) / Path(kValAnno)).ToString(); - - if (usage_ == "train") { - RETURN_IF_NOT_OK(WalkFolders(train_folder)); - RETURN_IF_NOT_OK(GetTraValAnno(train_anno_dir, train_folder)); - } else if (usage_ == "valid") { - RETURN_IF_NOT_OK(WalkFolders(val_folder)); - RETURN_IF_NOT_OK(GetTraValAnno(val_anno_dir, val_folder)); - } else if (usage_ == "test") { - RETURN_IF_NOT_OK(WalkFolders(test_folder)); - } else { - RETURN_IF_NOT_OK(WalkFolders(train_folder)); - RETURN_IF_NOT_OK(WalkFolders(val_folder)); - RETURN_IF_NOT_OK(GetTraValAnno(train_anno_dir, train_folder)); - RETURN_IF_NOT_OK(GetTraValAnno(val_anno_dir, val_folder)); - } - all_img_names_.shrink_to_fit(); - num_rows_ = all_img_names_.size(); - return Status::OK(); -} - -void WIDERFaceOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nWIDERFace dataset dir: " << folder_path_ - << "\nDecode: " << (decode_ ? "yes" : "no") << "\n\n"; - } -} - -Status WIDERFaceOp::LoadTensorRow(row_id_type row_id, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::string img_name = all_img_names_[row_id]; - std::shared_ptr image; - std::vector path_list; - RETURN_IF_NOT_OK(ReadImageToTensor(img_name, &image)); - trow->setId(row_id); - trow->push_back(std::move(image)); - if (usage_ == "test") { - path_list = {img_name}; - } else if (usage_ == "all" || usage_ == "train" || usage_ == "valid") { - TensorRow annotation; - RETURN_IF_NOT_OK(ParseAnnotations(img_name, &annotation)); - std::string train_path = (Path(folder_path_) / Path(kSplitPath) / Path(kTrainAnno)).ToString(); - std::string val_path = (Path(folder_path_) / Path(kSplitPath) / Path(kValAnno)).ToString(); - trow->insert(trow->end(), annotation.begin(), annotation.end()); - if (img_name.find("train") != std::string::npos) { - path_list = {img_name, train_path, train_path, train_path, train_path, train_path, train_path, train_path}; - } else { - path_list = {img_name, val_path, val_path, val_path, val_path, val_path, val_path, val_path}; - } - } else { - RETURN_STATUS_UNEXPECTED("Invalid parameter, usage should be \"train\", \"test\", \"valid\" or \"all\", got " + - usage_); - } - trow->setPath(path_list); - return Status::OK(); -} - -Status WIDERFaceOp::ReadImageToTensor(const std::string &image_path, std::shared_ptr *tensor) { - RETURN_UNEXPECTED_IF_NULL(tensor); - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_path, tensor)); - if (decode_) { - Status rc = Decode(*tensor, tensor); - CHECK_FAIL_RETURN_UNEXPECTED( - rc.IsOk(), "Invalid file, failed to decode image, the image may be broken or permission denied: " + image_path); - } - return Status::OK(); -} - -// Get annotations of usage of train or valid. -Status WIDERFaceOp::GetTraValAnno(const std::string &list_path, const std::string &image_folder_path) { - std::string line; - bool file_name_line = true; - bool num_boxes_line = false; - bool box_annotation_line = false; - int32_t num_boxes = 0, box_counter = 0; - std::string image_path; - std::vector image_labels; - std::ifstream file_reader(list_path, std::ios::in); - while (getline(file_reader, line)) { - if (file_name_line) { - box_counter = 0; - image_labels.clear(); - image_path = (Path(image_folder_path) / Path(line)).ToString(); - file_name_line = false; - num_boxes_line = true; - } else if (num_boxes_line) { - try { - num_boxes = std::stoi(line); - } catch (const std::exception &e) { - file_reader.close(); - RETURN_STATUS_UNEXPECTED("Invalid data, failed to read the number of bbox: " + line); - } - num_boxes_line = false; - box_annotation_line = true; - } else if (box_annotation_line) { - box_counter += 1; - std::vector labels; - auto s = Split(line, &labels); - if (s != Status::OK()) { - file_reader.close(); - return s; - } - image_labels.insert(image_labels.end(), labels.begin(), labels.end()); - if (box_counter >= num_boxes) { - box_annotation_line = false; - file_name_line = true; - annotation_map_[image_path] = image_labels; - } - } - } - file_reader.close(); - return Status::OK(); -} - -// Parse annotations to tensors. -Status WIDERFaceOp::ParseAnnotations(const std::string &path, TensorRow *tensor) { - RETURN_UNEXPECTED_IF_NULL(tensor); - std::vector annotation = annotation_map_[path]; - CHECK_FAIL_RETURN_UNEXPECTED( - static_cast(annotation.size()) % kDataLen == 0, - "Invalid parameter, only annotation with size multiple of eight are accepted, but got: " + - std::to_string(annotation.size())); - std::vector bboxes_vec, blur_vec, expression_vec, illumination_vec, occlusion_vec, pose_vec, invalid_vec; - std::vector label_vec; - std::shared_ptr bbox, blur, expression, illumination, occlusion, pose, invalid; - int32_t bbox_num = static_cast(annotation.size()) / kDataLen; - for (int32_t index = 0; index < bbox_num; index++) { - label_vec.clear(); - for (int32_t inner_index = 0; inner_index < kDataLen; inner_index++) { - label_vec.emplace_back(annotation[index * kDataLen + inner_index]); - } - bboxes_vec.insert(bboxes_vec.end(), label_vec.begin(), label_vec.begin() + kBboxLen); - blur_vec.emplace_back(label_vec[kBlurIndex]); - expression_vec.emplace_back(label_vec[kExpressionIndex]); - illumination_vec.emplace_back(label_vec[kIlluminationIndex]); - occlusion_vec.emplace_back(label_vec[kOcclusionIndex]); - pose_vec.emplace_back(label_vec[kPoseIndex]); - invalid_vec.emplace_back(label_vec[kInvalidIndex]); - } - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(bboxes_vec, TensorShape({bbox_num, 4}), &bbox)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(blur_vec, &blur)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(expression_vec, &expression)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(illumination_vec, &illumination)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(occlusion_vec, &occlusion)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(pose_vec, &pose)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(invalid_vec, &invalid)); - (*tensor) = TensorRow({std::move(bbox), std::move(blur), std::move(expression), std::move(illumination), - std::move(occlusion), std::move(pose), std::move(invalid)}); - return Status::OK(); -} - -// Convert annotation line to int32_t vector. -Status WIDERFaceOp::Split(const std::string &line, std::vector *split_num) { - RETURN_UNEXPECTED_IF_NULL(split_num); - std::string str = line; - std::string::size_type pos; - std::vector split; - int size = str.size(); - for (int index = 0; index < size;) { - pos = str.find(" ", index); - if (pos != index) { - std::string s = str.substr(index, pos - index); - split.push_back(s); - } - index = pos + 1; - } - int i = 0; - try { - for (; i < split.size(); i++) { - split_num->emplace_back(stoi(split[i])); - } - } catch (const std::exception &e) { - MS_LOG(ERROR) << "Invalid data, failed to parse the annotation " << i << ": " << split[i]; - RETURN_STATUS_UNEXPECTED("Invalid data, failed to parse the annotation " + std::to_string(i) + ": " + split[i]); - } - return Status::OK(); -} - -// Get dataset size. -Status WIDERFaceOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - if (all_img_names_.size() == 0) { - RETURN_IF_NOT_OK(PrepareData()); - } - *count = static_cast(all_img_names_.size()); - return Status::OK(); -} - -Status WIDERFaceOp::WalkFolders(const std::string &wf_path) { - Path img_folder(wf_path); - CHECK_FAIL_RETURN_UNEXPECTED(img_folder.Exists() && img_folder.IsDirectory(), - "Invalid path, failed to open WIDERFace folder: " + wf_path); - std::shared_ptr img_folder_itr = Path::DirIterator::OpenDirectory(&img_folder); - - RETURN_UNEXPECTED_IF_NULL(img_folder_itr); - int32_t dirname_offset_ = img_folder.ToString().length() + 1; - - while (img_folder_itr->HasNext()) { - Path sub_dir = img_folder_itr->Next(); - if (sub_dir.IsDirectory()) { - folder_names_.insert(sub_dir.ToString().substr(dirname_offset_)); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!folder_names_.empty(), - "Invalid file, no subfolder found under path: " + img_folder.ToString()); - for (std::set::iterator it = folder_names_.begin(); it != folder_names_.end(); ++it) { - Path folder_dir(img_folder / (*it)); - auto folder_it = Path::DirIterator::OpenDirectory(&folder_dir); - CHECK_FAIL_RETURN_UNEXPECTED(folder_it != nullptr, "Invalid path, failed to open dir: " + folder_dir.ToString() + - ", not exists or permission denied."); - while (folder_it->HasNext()) { - Path file = folder_it->Next(); - if (file.Extension() == kExtension) { - all_img_names_.emplace_back(file.ToString()); - } - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!all_img_names_.empty(), - "Invalid file, no " + std::string(kExtension) + " file found under path: " + wf_path); - return Status::OK(); -} - -Status WIDERFaceOp::ComputeColMap() { - // Set the column name map (base class field). - if (column_name_id_map_.empty()) { - for (int32_t index = 0; index < data_schema_->NumColumns(); index++) { - column_name_id_map_[data_schema_->Column(index).Name()] = index; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h deleted file mode 100644 index 38b251ba4..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIDER_FACE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIDER_FACE_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" - -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class WIDERFaceOp : public MappableLeafOp { - public: - /// Constructor. - /// \param[in] string folder_path - directory of WIDERFace dataset. - /// \param[in] string usage - usage. - /// \param[in] uint32_t num_workers - number of workers reading images in parallel. - /// \param[in] uint32_t queue_size - connector queue size. - /// \param[in] bool decode - whether to decode images. - /// \param[in] unique_ptr schema - data schema of WIDERFace dataset. - /// \param[in] shared_ptr sampler - sampler tells WIDERFace what to read. - WIDERFaceOp(const std::string &folder_path, const std::string &usage, int32_t num_workers, int32_t queue_size, - bool decode, std::unique_ptr schema, std::shared_ptr sampler); - - /// Deconstructor. - ~WIDERFaceOp() override = default; - - /// A print method typically used for debugging. - /// \param[out] out - out stream. - /// \param[in] show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "WIDERFaceOp"; } - - /// Function to count the number of samples in the WIDERFace dataset. - /// \param[in] count - output arg that will hold the actual dataset size. - /// \return Status - The status code returned. - Status CountTotalRows(int64_t *count); - - private: - /// Load a tensor row. - /// \param[in] uint64_t row_id - row id. - /// \param[in] TensorRow row - read all features into this tensor row. - /// \return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *row) override; - - /// \param[in] string image_path - path of image data. - /// \param[in] Tensor &tensor - get image tensor. - /// \return Status - The status code returned. - Status ReadImageToTensor(const std::string &image_path, std::shared_ptr *tensor); - - /// Called first when function is called. Get file_name, img_path and attribute info from ".txt" files. - /// \return Status - The status code returned. - Status PrepareData(); - - /// \param[in] string wf_path - walk the selected folder to get image names. - /// \return Status - The status code returned. - Status WalkFolders(const std::string &wf_path); - - /// Get all valid or train or both file paths(names). - /// \param[in] string list_path - real path of annotation file. - /// \param[in] string image_folder_path - real path of image folder. - /// \return Status - the status code returned. - Status GetTraValAnno(const std::string &list_path, const std::string &image_folder_path); - - /// \param[in] string path - path to the WIDERFace directory. - /// \param[in] TensorRow tensor - get attributes. - /// \return Status-the status code returned. - Status ParseAnnotations(const std::string &path, TensorRow *tensor); - - /// Split attribute info with space. - /// \param[in] string line - read line from annotation files. - /// \param[out] vector split_num - vector of annotation values. - /// \return Status-the status code returned. - Status Split(const std::string &line, std::vector *split_num); - - /// Private function for computing the assignment of the column name map. - /// \return Status-the status code returned. - Status ComputeColMap() override; - - std::string folder_path_; // directory of WIDERFace folder. - std::string usage_; - bool decode_; - std::unique_ptr data_schema_; - - std::vector all_img_names_; - std::set folder_names_; - std::map class_index_; - std::map> annotation_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIDER_FACE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.cc deleted file mode 100644 index 9aa65a41e..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.cc +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h" - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/io_block.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -namespace mindspore { -namespace dataset { -WikiTextOp::WikiTextOp(int32_t num_workers, int64_t total_rows, int32_t worker_connector_size, - std::unique_ptr schema, const std::vector &file_list, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id) - : TextFileOp(num_workers, total_rows, worker_connector_size, std::move(schema), file_list, op_connector_size, - shuffle_files, num_devices, device_id) {} - -// A print method typically used for debugging. -void WikiTextOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nRow count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nWikiText files list:\n"; - for (size_t i = 0; i < text_files_list_.size(); ++i) { - out << " " << text_files_list_[i]; - } - out << "\nData Schema:\n"; - out << *data_schema_ << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h deleted file mode 100644 index 2bac031af..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIKI_TEXT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIKI_TEXT_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -class WikiTextOp : public TextFileOp { - public: - /// \brief Constructor. - /// \param[in] num_workers Number of workers reading images in parallel - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] file_list List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. This argument should be - /// specified only when num_devices is also specified. - WikiTextOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, std::unique_ptr, - const std::vector &file_list, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id); - - /// \brief Default destructor. - ~WikiTextOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[in] out he output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "WikiTextOp"; } - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "WikiText" : "wiki text"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_WIKI_TEXT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.cc deleted file mode 100644 index a370b8c2e..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.cc +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h" - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -YahooAnswersOp::YahooAnswersOp(const std::vector &dataset_files_list, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, int32_t op_connector_size, bool shuffle_files, - int32_t num_devices, int32_t device_id) - : CsvOp(dataset_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id) {} - -void YahooAnswersOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nYahooAnswers files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h deleted file mode 100644 index 26270301a..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YAHOO_ANSWERS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YAHOO_ANSWERS_OP_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class YahooAnswersOp : public CsvOp { - public: - /// \brief Constructor. - /// \param[in] dataset_files_list - List of file paths for the dataset files. - /// \param[in] field_delim - A char that indicates the delimiter to separate fields. - /// \param[in] column_default - List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name - List of column names of the dataset file. - /// \param[in] num_workers - Num of workers reading files in parallel. - /// \param[in] num_samples - The number of samples to be included in the dataset. - /// \param[in] worker_connector_size - Size of each internal queue. - /// \param[in] op_connector_size - Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files - Whether or not to shuffle the files before reading data. - /// \param[in] num_devices - Number of devices that the dataset should be divided into. - /// \param[in] device_id - The device ID within num_devices. - YahooAnswersOp(const std::vector &dataset_files_list, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, int32_t num_workers, int64_t num_samples, - int32_t worker_connector_size, int32_t op_connector_size, bool shuffle_files, int32_t num_devices, - int32_t device_id); - - /// \brief Destructor. - ~YahooAnswersOp() = default; - - /// \brief A print method typically used for debugging - /// \param[out] out The output stream to write output to - /// \param[in] show_all A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "YahooAnswers" : "yahoo answers"; } - - /// \brief Op name getter - /// \return Name of the current Op. - std::string Name() const override { return "YahooAnswersOp"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YAHOO_ANSWERS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.cc deleted file mode 100644 index 3f255cbe3..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h" - -#include - -#include "include/utils/common.h" - -namespace mindspore { -namespace dataset { -YelpReviewOp::YelpReviewOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, - int32_t op_connector_size, bool shuffle_files, int32_t num_devices, int32_t device_id, - char field_delim, const std::vector> &column_default, - const std::vector &column_name, - const std::vector &yelp_review_files_list) - : CsvOp(yelp_review_files_list, field_delim, column_default, column_name, num_workers, num_samples, - worker_connector_size, op_connector_size, shuffle_files, num_devices, device_id) {} - -void YelpReviewOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op. - out << "\n"; - } else { - // Call the super class for displaying any common detailed info. - ParallelOp::Print(out, show_all); - // Then show any custom derived-internal stuff. - out << "\nSample count: " << total_rows_ << "\nDevice id: " << device_id_ << "\nNumber of devices: " << num_devices_ - << "\nShuffle files: " << ((shuffle_files_) ? "yes" : "no") << "\nYelpReview files list:\n"; - for (int i = 0; i < csv_files_list_.size(); ++i) { - out << " " << csv_files_list_[i]; - } - out << "\n\n"; - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h deleted file mode 100644 index 42f6e976c..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YELP_REVIEW_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YELP_REVIEW_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector; - -/// \class YelpReviewOp -/// \brief A Op derived class to represent YelpReview Op. -class YelpReviewOp : public CsvOp { - public: - /// \brief Constructor of YelpReviewOp. - /// \param[in] num_workers Number of worker threads reading data from yelp_review files. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] worker_connector_size Size of each internal queue. - /// \param[in] op_connector_size Size of each queue in the connector that the child operator pulls from. - /// \param[in] shuffle_files Whether or not to shuffle the files before reading data. - /// \param[in] num_devices Number of devices that the dataset should be divided into. - /// \param[in] device_id The device ID within num_devices. - /// \param[in] field_delim A char that indicates the delimiter to separate fields. - /// \param[in] column_default List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). - /// \param[in] column_name List of column names of the dataset. - /// \param[in] yelp_review_files_list List of file paths for the dataset files. - YelpReviewOp(int32_t num_workers, int64_t num_samples, int32_t worker_connector_size, int32_t op_connector_size, - bool shuffle_files, int32_t num_devices, int32_t device_id, char field_delim, - const std::vector> &column_default, - const std::vector &column_name, const std::vector &yelp_review_files_list); - - /// \brief Destructor. - ~YelpReviewOp() = default; - - /// \brief A print method typically used for debugging. - /// \param[out] out The output stream to write output to. - /// \param[in] show_all A bool to control if you want to show all info or just a summary. - void Print(std::ostream &out, bool show_all) const override; - - /// \brief DatasetName name getter. - /// \param[in] upper A bool to control if you want to return uppercase or lowercase Op name. - /// \return DatasetName of the current Op. - std::string DatasetName(bool upper = false) const { return upper ? "YelpReview" : "yelp review"; } - - /// \brief Op name getter. - /// \return Name of the current Op. - std::string Name() const override { return "YelpReviewOp"; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YELP_REVIEW_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.cc deleted file mode 100644 index ef76f26a2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.cc +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/audio/kernels/audio_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -constexpr float kMaxShortVal = 32767.0; -constexpr char kExtension[] = ".wav"; -constexpr int kStrLen = 15; // the length of name. -#ifndef _WIN32 -constexpr char kSplitSymbol[] = "/"; -#else -constexpr char kSplitSymbol[] = "\\"; -#endif - -YesNoOp::YesNoOp(const std::string &file_dir, int32_t num_workers, int32_t queue_size, - std::unique_ptr data_schema, std::shared_ptr sampler) - : MappableLeafOp(num_workers, queue_size, std::move(sampler)), - dataset_dir_(file_dir), - data_schema_(std::move(data_schema)) {} - -Status YesNoOp::PrepareData() { - auto realpath = FileUtils::GetRealPath(dataset_dir_.c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << dataset_dir_ << " does not exist."; - RETURN_STATUS_UNEXPECTED("Invalid file path, " + dataset_dir_ + " does not exist."); - } - Path dir(realpath.value()); - if (dir.Exists() == false || dir.IsDirectory() == false) { - RETURN_STATUS_UNEXPECTED("Invalid directory, " + dataset_dir_ + " does not exist or is not a directory."); - } - std::shared_ptr dir_itr = Path::DirIterator::OpenDirectory(&dir); - RETURN_UNEXPECTED_IF_NULL(dir_itr); - while (dir_itr->HasNext()) { - Path file = dir_itr->Next(); - if (file.Extension() == kExtension) { - all_wave_files_.emplace_back(file.ToString()); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(!all_wave_files_.empty(), "Invalid file, no .wav files found under " + dataset_dir_); - num_rows_ = all_wave_files_.size(); - return Status::OK(); -} - -void YesNoOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - ParallelOp::Print(out, show_all); - out << "\n"; - } else { - ParallelOp::Print(out, show_all); - out << "\nNumber of rows: " << num_rows_ << "\nYesNo directory: " << dataset_dir_ << "\n\n"; - } -} - -Status YesNoOp::Split(const std::string &line, std::vector *split_num) { - RETURN_UNEXPECTED_IF_NULL(split_num); - std::string str = line; - int dot_pos = str.find_last_of(kSplitSymbol); - std::string sub_line = line.substr(dot_pos + 1, kStrLen); // (dot_pos + 1) because the index start from 0. - std::string::size_type pos; - std::vector split; - sub_line += "_"; // append to sub_line indicating the end of the string. - uint32_t size = sub_line.size(); - for (uint32_t index = 0; index < size;) { - pos = sub_line.find("_", index); - if (pos != index) { - std::string s = sub_line.substr(index, pos - index); - split.emplace_back(s); - } - index = pos + 1; - } - try { - for (int i = 0; i < split.size(); i++) { - split_num->emplace_back(stoi(split[i])); - } - } catch (const std::exception &e) { - MS_LOG(ERROR) << "[Internal ERROR] Converting char to int confront with an error in function stoi: " << e.what(); - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Converting char to int confront with an error in function stoi: " + - std::string(e.what())); - } - return Status::OK(); -} - -Status YesNoOp::LoadTensorRow(row_id_type index, TensorRow *trow) { - RETURN_UNEXPECTED_IF_NULL(trow); - std::shared_ptr waveform, sample_rate_scalar, label_scalar; - int32_t sample_rate; - std::string file_name = all_wave_files_[index]; - std::vector label; - std::vector waveform_vec; - RETURN_IF_NOT_OK(Split(file_name, &label)); - RETURN_IF_NOT_OK(ReadWaveFile(file_name, &waveform_vec, &sample_rate)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(waveform_vec, &waveform)); - RETURN_IF_NOT_OK(waveform->ExpandDim(0)); - RETURN_IF_NOT_OK(Tensor::CreateScalar(sample_rate, &sample_rate_scalar)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(label, &label_scalar)); - (*trow) = TensorRow(index, {waveform, sample_rate_scalar, label_scalar}); - trow->setPath({file_name, file_name, file_name}); - return Status::OK(); -} - -Status YesNoOp::CountTotalRows(int64_t *count) { - RETURN_UNEXPECTED_IF_NULL(count); - if (all_wave_files_.size() == 0) { - RETURN_IF_NOT_OK(PrepareData()); - } - *count = static_cast(all_wave_files_.size()); - return Status::OK(); -} - -Status YesNoOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - for (int32_t i = 0; i < data_schema_->NumColumns(); ++i) { - column_name_id_map_[data_schema_->Column(i).Name()] = i; - } - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h deleted file mode 100644 index 5ded1ee14..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YES_NO_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YES_NO_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -class YesNoOp : public MappableLeafOp { - public: - /// Constructor. - /// @param std::string file_dir - dir directory of YesNo. - /// @param int32_t num_workers - number of workers reading images in parallel. - /// @param int32_t queue_size - connector queue size. - /// @param std::unique_ptr data_schema - the schema of the YesNo dataset. - /// @param std::shared_ptr sampler - sampler tells YesNoOp what to read. - YesNoOp(const std::string &file_dir, int32_t num_workers, int32_t queue_size, std::unique_ptr data_schema, - std::shared_ptr sampler); - - /// Destructor. - ~YesNoOp() = default; - - /// A print method typically used for debugging. - /// @param std::ostream &out - out stream. - /// @param bool show_all - whether to show all information. - void Print(std::ostream &out, bool show_all) const override; - - /// Op name getter. - /// @return Name of the current Op. - std::string Name() const override { return "YesNoOp"; } - - /// @param int64_t *count - output rows number of YesNoDataset. - /// @return Status - The status code returned. - Status CountTotalRows(int64_t *count); - - private: - /// Load a tensor row according to wave id. - /// @param row_id_type row_id - id for this tensor row. - /// @param TensorRow trow - wave & target read into this tensor row. - /// @return Status - The status code returned. - Status LoadTensorRow(row_id_type row_id, TensorRow *trow) override; - - /// Get file infos by file name. - /// @param string line - file name. - /// @param vector split_num - vector of annotation. - /// @return Status - The status code returned. - Status Split(const std::string &line, std::vector *split_num); - - /// Initialize YesNoDataset related var, calls the function to walk all files. - /// @return Status - The status code returned. - Status PrepareData(); - - /// Private function for computing the assignment of the column name map. - /// @return Status - The status code returned. - Status ComputeColMap() override; - - std::vector all_wave_files_; - std::string dataset_dir_; - std::unique_ptr data_schema_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_SOURCE_YES_NO_OP_H diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/take_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/take_op.cc deleted file mode 100644 index 633f3bb88..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/take_op.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/take_op.h" - -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { -// Constructor of the TakeOp. -TakeOp::TakeOp(int32_t count) : PipelineOp(0), max_takes_(count), take_count_(0) {} - -// A print method typically used for debugging -void TakeOp::Print(std::ostream &out, bool show_all) const { - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << " [takes: " << max_takes_ << "]\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal stuff - out << "\nTake count: " << take_count_ << "\nMax takes: " << max_takes_ << "\n\n"; - } -} - -Status TakeOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] TakeOp is an inlined operator."); } - -Status TakeOp::CommonGetNextRow(TensorRow *row, bool is_pull_mode) { - bool eoe_received = false; - if (take_count_ < max_takes_) { - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - } else { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - } - if (row->eoe()) { - eoe_received = true; - } else { - take_count_++; - return Status::OK(); - } - } - if (take_count_ == max_takes_) { - // drain - while (!row->eoe()) { - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_[0]->GetNextRow(row)); - } else { - RETURN_IF_NOT_OK(child_[0]->GetNextRowPullMode(row)); - } - } - eoe_received = true; - } - if (eoe_received) { - UpdateRepeatAndEpochCounter(); - take_count_ = 0; - } - - return Status::OK(); -} - -Status TakeOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - RETURN_IF_NOT_OK(CommonGetNextRow(row, false)); - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -Status TakeOp::GetNextRowPullMode(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - RETURN_IF_NOT_OK(CommonGetNextRow(row, true)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/take_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/take_op.h deleted file mode 100644 index a5ee522b2..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/take_op.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_TAKE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_TAKE_OP_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" - -namespace mindspore { -namespace dataset { -class TakeOp : public PipelineOp { - public: - // Constructor of the TakeOp. - // @note The builder class should be used to call it - // @param count - The number of takes to do - explicit TakeOp(int32_t count); - - // Destructor - ~TakeOp() = default; - - // A print method typically used for debugging - // \param[in] out - The output stream to write output to - // \param[in] show_all - A bool to control if you want to show all info or just a summary - void Print(std::ostream &out, bool show_all) const override; - - // << Stream output operator overload - // @notes This allows you to write the debug print info using stream operators - // \param[in] out - reference to the output stream being overloaded - // \param[in] ro - reference to the TakeOp to display - // @return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const TakeOp &ro) { - ro.Print(out, false); - return out; - } - - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kTakeOp; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRow(TensorRow *row) override; - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - int32_t max_takes_; // The number of takes that the user requested - int32_t take_count_; // A counter for the current number of executed takes - - std::unique_ptr child_iterator_; // An iterator for fetching. - - /// \brief Common non-pull mode and pull mode function to get the next row - /// \param row[out] - Fetched TensorRow - /// \param is_pull_mode[in] - an indicator to identify if in pull mode or not - /// \return Status The status code returned - Status CommonGetNextRow(TensorRow *row, bool is_pull_mode); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_TAKE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.cc b/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.cc deleted file mode 100644 index 45b308faf..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.cc +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h" - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -// Construct ZipOp here, local variables initialized in operator due to tree construction restrictions -ZipOp::ZipOp() : PipelineOp(0) {} - -// destructor -ZipOp::~ZipOp() {} - -// fetches next zipped (merged) row -Status ZipOp::getNextZippedRow(TensorRow *const new_zip_row, int32_t *skip_child, bool is_pull_mode) const { - *new_zip_row = {}; - // iterate over all iterators and generate a row - for (size_t i = 0; i < child_.size(); ++i) { - TensorRow new_row; - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_[i]->GetNextRow(&new_row)); - } else { - RETURN_IF_NOT_OK(child_[i]->GetNextRowPullMode(&new_row)); - } - if (new_row.eoe() || new_row.eof()) { - *new_zip_row = new_row; - *skip_child = static_cast(i); - return Status::OK(); - } else { - MS_LOG(DEBUG) << "Zip operator got row from child " << i << ". Num cols: " << new_row.size() << "."; - // TODO(ly): set to last row time, but actually they should be added together - new_row.CopyTimerTo(new_zip_row); - new_zip_row->insert(new_zip_row->end(), new_row.begin(), new_row.end()); - } - } - return Status::OK(); -} - -// drain end of epoch messages from iterator for this epoch -Status ZipOp::drainPipeline(int32_t skip_child, bool is_pull_mode) const { - for (size_t con = 0; con < child_.size(); ++con) { - if (con == skip_child) { - continue; - } - MS_LOG(DEBUG) << "Zip operator draining child at " << con << "."; - TensorRow row; - while (!row.eoe()) { - if (!is_pull_mode) { - RETURN_IF_NOT_OK(child_[con]->GetNextRow(&row)); - } else { - RETURN_IF_NOT_OK(child_[con]->GetNextRowPullMode(&row)); - } - } - } - // at this point all connectors don't contain end of epoch messages. next iteration should be clean - return Status::OK(); -} - -// A function that prints info about the Operator -void ZipOp::Print(std::ostream &out, // In: The output stream to print to - bool show_all) const { // In: T/F if it should print everything - if (!show_all) { - // Call the super class for displaying any common 1-liner info - PipelineOp::Print(out, show_all); - // Then show any custom derived-internal 1-liner info for this op - out << "\n"; - } else { - // Call the super class for displaying any common detailed info - PipelineOp::Print(out, show_all); - } - // Then show any custom derived-internal stuff - out << "\nDatasets: " << child_.size() << "\n\n"; -} - -// overwrite function and handle eof -Status ZipOp::EofReceived(int32_t) { - MS_LOG(DEBUG) << "Zip operator EOF received, do nothing now."; - return Status::OK(); -} - -// overwrite function and handle eoe -Status ZipOp::EoeReceived(int32_t) { - state_ = OpState::kDeOpIdle; - return Status::OK(); -} - -Status ZipOp::ComputeColMap() { - if (column_name_id_map_.empty()) { - column_name_id_map_ = {}; - for (size_t i = 0; i < child_.size(); ++i) { - // Initializing col_name_id_map from the child. - const std::unordered_map col_name_id_map = child_[i]->column_name_id_map(); - int32_t colsCurrent = column_name_id_map_.size(); - // the update code below shouldn't do anything bad if the column name already exists. - for (const auto &pair : col_name_id_map) { - std::string name = pair.first; - int32_t old_id = pair.second; - // check if name already exists in column name descriptor - if (column_name_id_map_.count(name) == 1) { - RETURN_STATUS_UNEXPECTED("Invalid data, duplicate column " + name + " already exists when zipping datasets."); - } - column_name_id_map_[name] = old_id + colsCurrent; - } - } - MS_LOG(DEBUG) << "Setting column map:\n" << this->ColumnNameMapAsString(); - } else { - MS_LOG(WARNING) << "Column name map is already set!"; - } - return Status::OK(); -} - -Status ZipOp::operator()() { RETURN_STATUS_UNEXPECTED("[Internal ERROR] ZipOp is an inlined operator."); } - -Status ZipOp::GetNextRow(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t start_time = GetSyscnt(); - int32_t skip_child = -1; - RETURN_IF_NOT_OK(getNextZippedRow(row, &skip_child, false)); - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - MS_LOG(DEBUG) << "Zip operator is now draining child inputs."; - RETURN_IF_NOT_OK(drainPipeline(skip_child, false)); - } - RETURN_IF_NOT_OK( - CollectOpInfo(this->NameWithID(), "GetFromPreviousOp", start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -Status ZipOp::GetNextRowPullMode(TensorRow *row) { - RETURN_UNEXPECTED_IF_NULL(row); - int32_t skip_child = -1; - RETURN_IF_NOT_OK(getNextZippedRow(row, &skip_child, true)); - if (row->eoe()) { - UpdateRepeatAndEpochCounter(); - MS_LOG(DEBUG) << "Zip operator in pull mode is now draining child inputs."; - RETURN_IF_NOT_OK(drainPipeline(skip_child, true)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h b/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h deleted file mode 100644 index 01fe47f7e..000000000 --- a/mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_ZIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_ZIP_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/pipeline_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -class ZipOp : public PipelineOp { - public: - // Constructor for ZipOp - // @param op_connector_size - connector size - ZipOp(); - - // Destructor - ~ZipOp(); - - Status EofReceived(int32_t) override; - - Status EoeReceived(int32_t) override; - - // Print function for Zip - // \param[in] out - output stream to print to - // \param[in] show_all - if it should print everything - void Print(std::ostream &out, bool show_all) const override; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const ZipOp &zo) { - zo.Print(out, false); - return out; - } - - // Class functor operator () override. - // All dataset ops operate by launching a thread (see ExecutionTree). This class functor will - // provide the master loop that drives the logic for performing the work - // @return Status The status code returned - Status operator()() override; - - // Op name getter - // @return Name of the current Op - std::string Name() const override { return kZipOp; } - - /// \brief Gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRow(TensorRow *row) override; - - /// \brief In pull mode, gets the next row - /// \param row[out] - Fetched TensorRow - /// \return Status The status code returned - Status GetNextRowPullMode(TensorRow *const row) override; - - protected: - /// \brief Gets the implementation status for operator in pull mode - /// \return Implementation status - ImplementedPullMode PullModeImplementationStatus() const override { return ImplementedPullMode::Implemented; } - - private: - /// \brief Drain eoe signals from all children connectors. - /// \notes Handle special handle case where an empty row has been received from child iterator. - /// When this function is called and encounters eoe at child iterator, - /// we need to drain rows from other child iterators until we hit eoe from all other child iterators. - /// \param[in] skip_child - identifier for child to be skipped - /// \param[in] is_pull_mode - an indicator to identify if in pull mode or not - Status drainPipeline(int32_t skip_child, bool is_pull_mode) const; - - // Merges 1 row from each childIterator together - // \param[in] new_zip_row - input and output, will be a non-empty row if all rows from childConnectors are non-empty - // \param[in] skip_child - input and output, identifier for child to be skipped - // \param[in] is_pull_mode - an indicator to identify if in pull mode or not - // @details merge rows from iterator together. This is the main functionality for ZipOp - // this function takes one row and fills it with tensors from rows fetched - // from childIterators. - // @example: - // Zips multiple rows at a time, the output is store in newZipRow - // 1 a T - // \ | / - // 1, a, T - Status getNextZippedRow(TensorRow *const new_zip_row, int32_t *skip_child, bool is_pull_mode) const; - - // Computing the assignment of the column name map. - // @return - Status - Status ComputeColMap() override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_DATASETOPS_ZIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/engine/execution_tree.cc b/mindspore-lite/minddata/dataset/engine/execution_tree.cc deleted file mode 100644 index 33b2df4a4..000000000 --- a/mindspore-lite/minddata/dataset/engine/execution_tree.cc +++ /dev/null @@ -1,309 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#ifdef WITH_BACKEND -#include "utils/numa_interface.h" -#include "utils/ms_context.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -#ifdef WITH_BACKEND -ExecutionTree::ExecutionTree() : ExecutionTree(GlobalContext::config_manager()) {} - -ExecutionTree::ExecutionTree(std::shared_ptr cfg) - : rank_id_(cfg->rank_id()), - numa_enable_(cfg->numa_enable()), - handle_(nullptr), - id_count_(0), - tree_state_(kDeTStateInit) { - tg_ = std::make_unique(); - root_ = nullptr; - unique_id_ = Services::GetUniqueID(); -} -#else -ExecutionTree::ExecutionTree() : id_count_(0), tree_state_(kDeTStateInit) { - tg_ = std::make_unique(); - root_ = nullptr; - unique_id_ = Services::GetUniqueID(); -} -#endif - -// Destructor -ExecutionTree::~ExecutionTree() { -#ifdef WITH_BACKEND - if (numa_enable_) { - handle_ = nullptr; - } - DataQueueOp *op = dynamic_cast(root_.get()); - if (op != nullptr) { - op->StopWaiting(); - } -#endif - (void)tg_->ServiceStop(); -} - -// Associates a DatasetOp with this tree. This assigns a valid node id to the operator and -// provides it with a link to the tree. A node cannot form any relationships (parent/child) with -// other nodes unless they are associated with the same tree. -Status ExecutionTree::AssociateNode(const std::shared_ptr &op) { - RETURN_UNEXPECTED_IF_NULL(op); - // If we are already a part of the tree, no-op - if (op->tree_ == this) { - return Status::OK(); - } - if (tree_state_ != kDeTStateInit && tree_state_ != kDeTStateBuilding) { - std::string err_msg = - "Invalid tree state for adding a node. Current state: " + std::to_string(static_cast(tree_state_)) + - " Expected states: " + std::to_string(static_cast(kDeTStateInit)) + " or " + - std::to_string(static_cast(kDeTStateBuilding)); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Enter the building state if we were not already there - tree_state_ = kDeTStateBuilding; - - // Assign an id to the operator - op->SetId(id_count_); - id_count_++; - - // Assign our tree into the op so that each op has a link back to the tree - op->set_tree(this); - return Status::OK(); -} - -// Sets the root node of the tree -Status ExecutionTree::AssignRoot(const std::shared_ptr &op) { - RETURN_UNEXPECTED_IF_NULL(op); - // Tree must be in building state before we can assign root to it - if (tree_state_ != kDeTStateBuilding) { - std::string err_msg = - "Invalid tree state for assigning a root node. Current state: " + std::to_string(static_cast(tree_state_)) + - " Expected state: " + std::to_string(static_cast(kDeTStateBuilding)); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // If they didn't already call AssociateNode for this node before calling AssignRoot, - // then do so now. - if (op->operator_id_ == DatasetOp::kInvalidOperatorId) { - RETURN_IF_NOT_OK(this->AssociateNode(op)); - } - - // Then add it as the root. - root_ = op; - - return Status::OK(); -} - -// A print method typically used for debugging -void ExecutionTree::Print(std::ostream &out, const std::shared_ptr &op) const { - out << "Execution tree summary:\n" - << "-----------------------\n"; - this->PrintNode(out, op == nullptr ? root_ : op, "", true, false); - out << "\nExecution tree operator details:\n" - << "--------------------------------\n"; - this->PrintNode(out, op == nullptr ? root_ : op, "", true, true); -} - -// A helper functions for doing the recursive printing -void ExecutionTree::PrintNode(std::ostream &out, const std::shared_ptr &dataset_op, std::string indent, - bool last, bool detailed) const { - if (dataset_op == nullptr) { - return; - } - // Decide which printer to use based on detailed arg. - if (!detailed) { - out << indent << "+- " << *dataset_op; - indent += (last ? " " : "| "); - } else { - dataset_op->Print(out, detailed); - } - - // Descend to children - for (size_t i = 0; i < dataset_op->child_.size(); ++i) { - this->PrintNode(out, dataset_op->child_[i], indent, (i == (dataset_op->child_.size() - 1)), detailed); - } -} - -// Start the execution of the tree -Status ExecutionTree::Launch() { - uint64_t start_time = GetSyscnt(); - // opencv limit too many threads -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) && !defined(ENABLE_ANDROID) -#ifdef WITH_BACKEND - // Here we do numa bind for performance optimization, as our test result, - // if we do numa bind when get_dataset_size launch a tree, we'll get a - // better performance than only we do numa bind at the time _To_Device - // launch a tree. Our numa bind work is a process level bind, bind with - // both cpu and memory and we choose numa_node with a polling logic: - // numa_bind_id = rank_id_ % (numa_max_node() + 1) - // Now we only support GPU scenario and the single process scenario of Ascend, - // now we remove the target_link of numa with _c_dataengine, and user can use - // a config api to control whether to open numa feature. - if (numa_enable_ && rank_id_ >= 0) { - if (handle_ == nullptr) { - handle_ = GetNumaAdapterHandle(); - if (handle_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Numa package (libnuma.so) not found."); - } - } - RETURN_IF_NOT_OK(NumaBind(handle_.get(), rank_id_)); - MS_LOG(INFO) << "Numa bind memory and cpu successful."; - } -#endif -#endif - - // Tree must be built and prepared before it can be launched! - if (tree_state_ != kDeTStatePrepared) { - std::string err_msg = - "Invalid tree state for launching tree. Current state: " + std::to_string(static_cast(tree_state_)) + - " Expected state: " + std::to_string(static_cast(kDeTStatePrepared)); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::ostringstream ss; - ss << *this; - MS_LOG(INFO) << "Printing the tree before launch tasks:\n" << ss.str(); - - // By default, Python uses fork() to create child processes, which will - // inherit all the resources of the parent process, including the state - // of locks. So we should create all the child processes first, make sure - // the main process is in single-threaded and lock-free, and then create - // all the DatasetOp threads. - for (auto itr = this->begin(); itr != this->end(); ++itr) { - RETURN_IF_NOT_OK(itr->Launch()); - } - - for (auto itr = this->begin(); itr != this->end(); ++itr) { - // An inlined operator is one that has an output connector size of 0, and it does not - // require a thread to execute. Instead, the work of this operator is executed inlined - // from the tree node directly above it (or in the case of a root node, it runs from within - // the launching tree/user thread. Do not exec any thread for an inlined op. - // Set the state of the Operator as running. This only matters in Leaf ops, CacheOp and TakeOp - itr->state_ = DatasetOp::OpState::kDeOpRunning; - if (!itr->inlined()) { - Status create_status = tg_->CreateAsyncTask(itr->NameWithID(), std::ref(*itr), nullptr, itr->id()); - if (create_status.StatusCode() == StatusCode::kMDInterrupted) { - // If CreateAsyncTask is interrupted, the pipeline may be already in error, - // try to retrieve the error status here - RETURN_IF_NOT_OK(TaskManager::GetMasterThreadRc()); - } else { - RETURN_IF_NOT_OK(create_status); - } - // Set if this task group has data queue op - if (itr->Name() == kDeviceQueueOp) { - tg_->HasDataQueue(true); - } - } - } - - tree_state_ = kDeTStateExecuting; - RETURN_IF_NOT_OK(CollectPipelineInfo("Pipeline", "Launch", start_time)); - return Status::OK(); -} - -// A function that traverse the tree in postorder then save the results in nodes -void ExecutionTree::Iterator::PostOrderTraverse(const std::shared_ptr &node) { - if (node == nullptr) { - return; - } - for (int32_t i = 0; i < node->child_.size(); ++i) { - PostOrderTraverse(node->child_[i]); - } - nodes_.push_back(node); -} - -ExecutionTree::Iterator::Iterator(const std::shared_ptr &root) : ind_(0) { - // post-order traverse the tree, if root is null, it return - PostOrderTraverse(root); - (void)nodes_.emplace_back(nullptr); -} - -// Given the number of workers, launch the worker entry function for each worker. This is essentially a -// wrapper for the TaskGroup handling that is stored inside the execution tree. -Status ExecutionTree::LaunchWorkers(int32_t num_workers, std::function func, - std::vector *worker_tasks, std::string name, int32_t operator_id) { - int32_t num_cpu_threads = GlobalContext::Instance()->config_manager()->num_cpu_threads(); - // this performs check that num_workers is positive and not unreasonably large which could happen - // for example, un-initialized variable. uint16 max is 65536 which is large enough to cover everything - CHECK_FAIL_RETURN_UNEXPECTED(num_workers > 0 && num_workers < std::numeric_limits::max(), - name + "'s num_worker=" + std::to_string(num_workers) + ", is negative or too large."); - // Launch the workers - if (num_workers > num_cpu_threads) { - MS_LOG(WARNING) << name << " is launched with " << std::to_string(num_workers) << " worker threads which exceeds " - << std::to_string(num_cpu_threads) << ", the maximum number of threads on this CPU."; - } - worker_tasks->resize(num_workers); - for (size_t i = 0; i < num_workers; ++i) { - Task *task = nullptr; - RETURN_IF_NOT_OK(tg_->CreateAsyncTask(name, std::bind(func, i), &task, operator_id)); - CHECK_FAIL_RETURN_UNEXPECTED(task != nullptr, "Failed to create a new worker"); - (*worker_tasks)[i] = task; - } - return Status::OK(); -} - -// Given the number of workers, launches the worker entry function for each. Essentially a -// wrapper for the TaskGroup handling that is stored inside the execution tree. -Status ExecutionTree::LaunchWorkers(int32_t num_workers, std::function func, std::string name, - int32_t operator_id) { - std::vector tasks; - return LaunchWorkers(num_workers, func, &tasks, name, operator_id); -} - -// Walks the tree to perform modifications to the tree in post-order to get it ready for execution. -Status ExecutionTree::Prepare(bool is_pull_mode) { - if (root_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Please assign one operator as the root of this tree."); - } - - std::vector> fifo; - std::shared_ptr op = root_; - size_t index = 0; - - // Build a FIFO queue with the root at the beginning and continue adding its descendants to the queue. - fifo.push_back(op); - do { - op = fifo[index]; - fifo.insert(fifo.end(), op->child_.begin(), op->child_.end()); - ++index; - } while (index < fifo.size()); - - // By iterating from the end of the FIFO queue, we simulate the post-order walk. - for (auto rit = fifo.crbegin(); rit != fifo.crend(); ++rit) { - if (!is_pull_mode) { - RETURN_IF_NOT_OK((*rit)->PrepareOperator()); - } else { - RETURN_IF_NOT_OK((*rit)->PrepareOperatorPullBased()); - } - } - - // The tree is prepared. - tree_state_ = kDeTStatePrepared; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/execution_tree.h b/mindspore-lite/minddata/dataset/engine/execution_tree.h deleted file mode 100644 index 2d745d518..000000000 --- a/mindspore-lite/minddata/dataset/engine/execution_tree.h +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_EXECUTION_TREE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_EXECUTION_TREE_H_ - -#include -#include -#include -#include -#include -#ifndef ENABLE_ANDROID -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) -#include -#include -#endif -#endif -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -namespace mindspore { -namespace dataset { -// Forward declares -class TaskGroup; -class DatasetOp; -class Pass; -using OptPass = std::vector>; -class ExecutionTree { - public: - // State flags for the lifecycle of the tree - enum TreeState { - kDeTStateInit = 0, // The freshly initialized state after construction - kDeTStateBuilding, // The tree is being built, nodes are being added - kDeTStatePrepared, // The tree has been prepared and is ready to be launched - kDeTStateExecuting, // The tree has been launched and is executing - kDeTStateEpochEnd, // The tree has been received end of epoch signal, just for profiling - kDeTStateFinished // The tree has been drained, dataset iterator received EOF - }; - - class Iterator { - public: - // support iterator traits - using iterator_category = std::bidirectional_iterator_tag; - using value_type = DatasetOp; - using difference_type = ptrdiff_t; - using pointer = DatasetOp *; - using reference = DatasetOp &; - - // Constructor - // @param root The root node to start iterating from - explicit Iterator(const std::shared_ptr &root = nullptr); - - // Destructor - ~Iterator() {} - - Iterator &operator++() { - ++ind_; - return *this; - } // prefix ++ overload - - Iterator operator++(int) { - Iterator it = *this; - it.ind_ = ind_; - ind_++; - return it; - } // post-fix ++ overload - - Iterator &operator--() { - --ind_; - return *this; - } // prefix -- overload - - Iterator operator--(int) { - Iterator it = *this; - it.ind_ = ind_; - ind_--; - return it; - } // post-fix -- overload - - DatasetOp &operator*() { return *nodes_[ind_]; } // dereference operator - - std::shared_ptr operator->() { return nodes_[ind_]; } - - // getter function - // @return Shared pointer to the current operator - std::shared_ptr get() { return nodes_[ind_]; } - - bool operator==(const Iterator &rhs) { return nodes_[ind_] == rhs.nodes_[rhs.ind_]; } - - bool operator!=(const Iterator &rhs) { return nodes_[ind_] != rhs.nodes_[rhs.ind_]; } - - size_t NumNodes() const { return nodes_.size(); } - - private: - size_t ind_; // the cur node our Iterator points to - std::vector> nodes_; // store the nodes in post order - void PostOrderTraverse(const std::shared_ptr &); - }; - - // Constructor - ExecutionTree(); - - // Destructor - ~ExecutionTree(); - - /// \brief Associates a DatasetOp with this tree. This assigns a valid node id to the operator and - /// provides it with a link to the tree. A node cannot form any relationships (parent/child) with - /// other nodes unless they are associated with the same tree. - /// \param op - The operator to associate - /// \return Status The status code returned - Status AssociateNode(const std::shared_ptr &op); - - /// \brief Set the root node of the tree - /// \param op - The operator to assign as root - /// \return Status The status code returned - Status AssignRoot(const std::shared_ptr &op); - - /// \brief Start the execution of the tree - /// \return Status The status code returned - Status Launch(); - - /// /brief A print method typically used for debugging - /// \param out - The output stream to write output to - void Print(std::ostream &out, const std::shared_ptr &op = nullptr) const; - - /// \brief Return an iterator positioned at the start - /// \return Iterator - The iterator - ExecutionTree::Iterator begin(const std::shared_ptr &root = nullptr) const { - return Iterator(root == nullptr ? root_ : root); - } - - /// \brief Return an iterator positioned at the end - /// \return Iterator - The iterator - ExecutionTree::Iterator end() const { return Iterator(nullptr); } - - /// \brief << Stream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param out - reference to the output stream being overloaded - /// \param exe_tree - reference to the execution tree to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const ExecutionTree &exe_tree) { - exe_tree.Print(out); - return out; - } - - const bool IsPython() { - for (auto itr = this->begin(); itr != this->end(); ++itr) { - if (itr->IsPython()) { - return true; - } - } - return false; - } - - /// \brief Given the number of workers, launches the worker entry function for each. Essentially a - /// wrapper for the TaskGroup handling that is stored inside the execution tree. - /// \param num_workers - The number of workers to launch - /// \param func - The function entry point that workers will execute - /// \param[out] worker_tasks - output vector to hold generated tasks - /// \param name - The description of worker to launch - /// \param op_id - The id of corresponding operator, if not inherit from dataset op then it is -1. - /// \return Status The status code returned - Status LaunchWorkers(int32_t num_workers, std::function func, std::vector *worker_tasks, - std::string name, int32_t operator_id = -1); - - Status LaunchWorkers(int32_t num_workers, std::function func, std::string name, - int32_t operator_id = -1); - - /// \brief Getter method - /// \return shared_ptr to the root operator - std::shared_ptr root() const { return root_; } - - /// \brief The prepare phase walks the tree in post-order to perform modifications to get it ready for execution. - /// \param is_pull_mode - an indicator if it's in pull mode or not - /// \return Status The status code returned - Status Prepare(bool is_pull_mode = false); - - /// \brief Return the pointer to the TaskGroup - /// \return raw pointer to the TaskGroup - TaskGroup *const AllTasks() const { return tg_.get(); } - - /// \brief Return if the ExecutionTree is at end of epoch status - /// \return bool - true is ExecutionTree is end of epoch status - bool IsEpochEnd() const { return tree_state_ == TreeState::kDeTStateEpochEnd; } - - /// \brief Set the ExecutionTree to EOE state - void SetEpochEnd() { tree_state_ = TreeState::kDeTStateEpochEnd; } - - /// \brief Set the ExecutionTree to executing state - void SetExecuting() { tree_state_ = TreeState::kDeTStateExecuting; } - - /// \brief Set the ExecutionTree to Finished state. - void SetFinished() { tree_state_ = TreeState::kDeTStateFinished; } - - /// \brief Return if the ExecutionTree is finished (iterator receives EOF). - /// \return Bool - true is ExecutionTree is finished - bool isFinished() const { return tree_state_ == TreeState::kDeTStateFinished; } - - /// \brief Return if the ExecutionTree is ready. - /// \return Bool - true is ExecutionTree is ready - bool isPrepared() const { - return tree_state_ == TreeState::kDeTStatePrepared || tree_state_ == TreeState::kDeTStateExecuting || - tree_state_ == TreeState::kDeTStateFinished; - } - - /// \brief Get a unique identifier for the tree - /// \return unique ID as a string - std::string GetUniqueId() { return unique_id_; } - - private: - /// \brief A helper functions for doing the recursive printing - /// \param dataset_op - The dataset op to print - /// \param indent - an indent string for aligning child levels in output - /// \param last - an indicator if it's the last child or not - /// \param detailed - should it display the detailed node output or the summary line - void PrintNode(std::ostream &out, const std::shared_ptr &dataset_op, std::string indent, bool last, - bool detailed) const; - - std::unique_ptr tg_; // Class for worker management - std::shared_ptr root_; // The root node of the tree - int32_t id_count_; // Counter for generating operator id's - TreeState tree_state_; // Tracking the current tree state - std::string unique_id_; // A unique identifier for the tree - -#ifdef WITH_BACKEND - // Constructor for if defined(ENABLE_GPUQUE) || defined(ENABLE_TDTQUE) - explicit ExecutionTree(std::shared_ptr cfg); - // This rank_id is for numa and device_queue, one process work with only one rank_id, - // for standalone scenario, this rank_id may come from env 'CUDA_VISIBLE_DEVICES', - // but for distribute scenario, this rank_id come from _get_global_rank() in python - int32_t rank_id_; - bool numa_enable_; - std::shared_ptr handle_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_EXECUTION_TREE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/gpu_item_connector.h b/mindspore-lite/minddata/dataset/engine/gpu_item_connector.h deleted file mode 100644 index 6b1099523..000000000 --- a/mindspore-lite/minddata/dataset/engine/gpu_item_connector.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_GPU_ITEM_CONNECTOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_GPU_ITEM_CONNECTOR_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/connector.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore/ccsrc/include/runtime/hardware_abstract/data_queue/blocking_queue.h" - -using mindspore::device::DataQueueItem; - -namespace mindspore { -namespace dataset { - -struct GpuConnectorItem { - std::vector data_item; - bool eoe_flag; // flag to indicate an EOE item in the connector -}; - -class GpuConnector : public Connector { - public: - GpuConnector(int32_t num_producers, int32_t num_consumers, int32_t queue_capacity) - : Connector(num_producers, num_consumers, queue_capacity) { - for (int i = 0; i < num_producers; i++) { - is_queue_finished_.push_back(false); - } - } - - ~GpuConnector() = default; - - Status Add(int32_t worker_d, GpuConnectorItem &&element) noexcept { - return Connector::Push(worker_d, std::move(element)); - } - - Status Pop(int32_t worker_id, GpuConnectorItem *result) noexcept override { - RETURN_UNEXPECTED_IF_NULL(result); - { - MS_ASSERT(worker_id < num_consumers_); - std::unique_lock lock(m_); - RETURN_IF_NOT_OK(cv_.Wait(&lock, [this, worker_id]() { return expect_consumer_ == worker_id; })); - if (is_queue_finished_[pop_from_]) { - std::string errMsg = "ERROR: popping from a finished queue in GpuConnector"; - RETURN_STATUS_UNEXPECTED(errMsg); - } - - RETURN_IF_NOT_OK(queues_[pop_from_]->PopFront(result)); - // empty data_item and eoe_flag=false is EOF - if ((*result).data_item.empty() && !(*result).eoe_flag) { - is_queue_finished_[pop_from_] = true; - } - - for (int offset = 1; offset <= num_producers_; offset++) { - int32_t nextQueueIndex = (pop_from_ + offset) % num_producers_; - if (is_queue_finished_[nextQueueIndex] == false) { - pop_from_ = nextQueueIndex; - break; - } - } - - expect_consumer_ = (expect_consumer_ + 1) % num_consumers_; - } - - cv_.NotifyAll(); - return Status::OK(); - } - - private: - std::vector is_queue_finished_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_GPU_ITEM_CONNECTOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.cc b/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.cc deleted file mode 100644 index 9f62e239b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.cc +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" - -#include -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h" -#endif - -namespace mindspore::dataset { -#ifndef ENABLE_ANDROID -Status DatasetCache::from_json(nlohmann::json json_obj, std::shared_ptr *cache) { - if (json_obj.find("cache") != json_obj.end()) { - nlohmann::json json_cache = json_obj["cache"]; - CHECK_FAIL_RETURN_UNEXPECTED(json_cache.find("session_id") != json_cache.end(), "Failed to find session_id"); - CHECK_FAIL_RETURN_UNEXPECTED(json_cache.find("cache_memory_size") != json_cache.end(), - "Failed to find cache_memory_size"); - CHECK_FAIL_RETURN_UNEXPECTED(json_cache.find("spill") != json_cache.end(), "Failed to find spill"); - session_id_type id = static_cast(json_cache["session_id"]); - uint64_t mem_sz = json_cache["cache_memory_size"]; - bool spill = json_cache["spill"]; - std::optional> hostname_c = std::nullopt; - std::optional port = std::nullopt; - std::optional num_connections = std::nullopt; - std::optional prefetch_sz = std::nullopt; - if (json_cache.find("hostname") != json_cache.end()) { - std::optional hostname = json_cache["hostname"]; - hostname_c = std::vector(hostname->begin(), hostname->end()); - } - if (json_cache.find("port") != json_cache.end()) { - port = json_cache["port"]; - } - if (json_cache.find("num_connections") != json_cache.end()) { - num_connections = json_cache["num_connections"]; - } - if (json_cache.find("cache_prefetch_size") != json_cache.end()) { - prefetch_sz = json_cache["cache_prefetch_size"]; - } - *cache = std::make_shared(id, mem_sz, spill, hostname_c, port, num_connections, prefetch_sz); - } - return Status::OK(); -} -#endif -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h b/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h deleted file mode 100644 index 6457f500e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_H_ - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore::dataset { -class DatasetCache { - public: - virtual ~DatasetCache() = default; - - virtual Status Build() = 0; - - virtual Status ValidateParams() = 0; - - virtual Status CreateCacheOp(int32_t num_workers, int32_t connector_queue_size, std::shared_ptr sampler, - std::shared_ptr *ds) = 0; - - virtual Status CreateCacheLookupOp(int32_t num_workers, int32_t connector_queue_size, - std::shared_ptr sampler, std::shared_ptr *ds) = 0; - - virtual Status CreateCacheMergeOp(int32_t num_workers, int32_t connector_queue_size, - std::shared_ptr *ds) = 0; - - virtual Status to_json(nlohmann::json *out_json) { return Status::OK(); } - -#ifndef ENABLE_ANDROID - static Status from_json(nlohmann::json json_obj, std::shared_ptr *cache); -#endif -}; -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.cc b/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.cc deleted file mode 100644 index ab6c37a51..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.cc +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" - -namespace mindspore { -namespace dataset { -/// Method to initialize the DatasetCache by creating an instance of a CacheClient -/// \return Status Error code -Status DatasetCacheImpl::Build() { - // The same DatasetCache instance can be re-used for multiple pipelines for cache sharing, - // in this case, cache_client_ object might have been created. - if (cache_client_) { - return Status::OK(); - } - - CacheClient::Builder builder; - builder.SetSessionId(session_id_).SetCacheMemSz(cache_mem_sz_).SetSpill(spill_); - if (hostname_) { - (void)builder.SetHostname(hostname_.value()); - } - if (port_) { - (void)builder.SetPort(port_.value()); - } - if (num_connections_) { - (void)builder.SetNumConnections(num_connections_.value()); - } - if (prefetch_sz_) { - (void)builder.SetPrefetchSize(prefetch_sz_.value()); - } - return builder.Build(&cache_client_); -} - -Status DatasetCacheImpl::CreateCacheOp(int32_t num_workers, int32_t connector_queue_size, - std::shared_ptr sampler, std::shared_ptr *ds) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_client_ != nullptr, "CacheOp requires a CacheClient, but got nullptr."); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler->SamplerBuild(&sampler_rt)); - std::shared_ptr cache_op = - std::make_shared(num_workers, connector_queue_size, cache_client_, std::move(sampler_rt)); - *ds = cache_op; - - return Status::OK(); -} - -Status DatasetCacheImpl::CreateCacheLookupOp(int32_t num_workers, int32_t connector_queue_size, - std::shared_ptr sampler, std::shared_ptr *ds) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_client_ != nullptr, "CacheLookupOp requires a CacheClient, but got nullptr."); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler->SamplerBuild(&sampler_rt)); - std::shared_ptr lookup_op = - std::make_shared(num_workers, connector_queue_size, cache_client_, std::move(sampler_rt)); - *ds = lookup_op; - - return Status::OK(); -} - -Status DatasetCacheImpl::CreateCacheMergeOp(int32_t num_workers, int32_t connector_queue_size, - std::shared_ptr *ds) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_client_ != nullptr, "CacheMergeOp requires a CacheClient, but got nullptr."); - std::shared_ptr merge_op = - std::make_shared(num_workers, connector_queue_size, num_workers, cache_client_); - *ds = merge_op; - - return Status::OK(); -} - -Status DatasetCacheImpl::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["session_id"] = session_id_; - args["cache_memory_size"] = cache_mem_sz_; - args["spill"] = spill_; - if (hostname_) { - args["hostname"] = hostname_.value(); - } - if (port_) { - args["port"] = port_.value(); - } - if (num_connections_) { - args["num_connections"] = num_connections_.value(); - } - if (prefetch_sz_) { - args["cache_prefetch_size"] = prefetch_sz_.value(); - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h b/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h deleted file mode 100644 index e337fe6a6..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_IMPL_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_IMPL_H_ - -#include -#include -#include -#include -#include -#include "include/api/dual_abi_helper.h" -#include "mindspore-lite/minddata/dataset/engine/cache/cache_client.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" - -namespace mindspore { -namespace dataset { -/// DatasetCache is the IR of CacheClient -class DatasetCacheImpl : public DatasetCache { - public: - friend class PreBuiltDatasetCache; - /// - /// \brief Constructor - /// \param id A user assigned session id for the current pipeline. - /// \param mem_sz Size of the memory set aside for the row caching (default=0 which means unlimited, - /// note that it might bring in the risk of running out of memory on the machine). - /// \param spill Spill to disk if out of memory (default=False). - /// \param hostname optional host name. - /// \param port optional port (default=50052). - /// \param num_connections optional number of connections (default=12). - /// \param prefetch_sz optional prefetch size (default=20). - DatasetCacheImpl(session_id_type id, uint64_t mem_sz, bool spill, std::optional> hostname, - std::optional port, std::optional num_connections, - std::optional prefetch_sz) - : session_id_(id), - cache_mem_sz_(mem_sz), - spill_(spill), - port_(std::move(port)), - num_connections_(std::move(num_connections)), - prefetch_sz_(std::move(prefetch_sz)) { - if (hostname == std::nullopt) { - hostname_ = std::nullopt; - } else { - hostname_ = std::string(hostname->begin(), hostname->end()); - } - } - - /// Method to initialize the DatasetCache by creating an instance of a CacheClient - /// \return Status Error code - Status Build() override; - - Status CreateCacheOp(int32_t num_workers, int32_t connector_queue_size, std::shared_ptr sampler, - std::shared_ptr *ds) override; - - Status CreateCacheLookupOp(int32_t num_workers, int32_t connector_queue_size, std::shared_ptr sampler, - std::shared_ptr *ds) override; - - Status CreateCacheMergeOp(int32_t num_workers, int32_t connector_queue_size, std::shared_ptr *ds) override; - - Status ValidateParams() override { return Status::OK(); } - - Status to_json(nlohmann::json *out_json) override; - - ~DatasetCacheImpl() override = default; - - private: - std::shared_ptr cache_client_; - session_id_type session_id_; - uint64_t cache_mem_sz_; - bool spill_; - std::optional hostname_; - std::optional port_; - std::optional num_connections_; - std::optional prefetch_sz_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_DATASET_CACHE_IMPL_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.cc b/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.cc deleted file mode 100644 index b13ce0575..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" - -namespace mindspore { -namespace dataset { -/// Method to initialize the DatasetCache by creating an instance of a CacheClient -/// \return Status Error code -Status PreBuiltDatasetCache::Build() { - // we actually want to keep a reference of the runtime object so it can be shared by different pipelines - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.h b/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.h deleted file mode 100644 index ccfe00a5e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_PRE_BUILT_DATASET_CACHE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_PRE_BUILT_DATASET_CACHE_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/cache/cache_client.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache_impl.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" - -namespace mindspore { -namespace dataset { -/// DatasetCache is the IR of CacheClient -class PreBuiltDatasetCache : public DatasetCacheImpl { - public: - /// \brief Constructor - /// \param cc a pre-built cache client - explicit PreBuiltDatasetCache(std::shared_ptr cc) - : DatasetCacheImpl(cc->session_id(), cc->GetCacheMemSz(), cc->isSpill(), StringToChar(cc->GetHostname()), - cc->GetPort(), cc->GetNumConnections(), cc->GetPrefetchSize()) { - cache_client_ = std::move(cc); - } - - ~PreBuiltDatasetCache() override = default; - - /// Method to initialize the DatasetCache by creating an instance of a CacheClient - /// \return Status Error code - Status Build() override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_CACHE_PRE_BUILT_DATASET_CACHE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.cc deleted file mode 100644 index 71ba01cb9..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.cc +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/batch_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -#ifdef ENABLE_PYTHON -// constructor #1, called by Pybind -BatchNode::BatchNode(std::shared_ptr child, int32_t batch_size, bool drop_remainder, bool pad, - const std::vector &in_col_names, const std::vector &out_col_names, - py::function batch_size_func, py::function batch_map_func, - std::map>> pad_map, - std::shared_ptr python_multiprocessing_runtime) - : batch_size_(batch_size), - drop_remainder_(drop_remainder), - pad_(pad), - in_col_names_(in_col_names), - out_col_names_(out_col_names), - batch_size_func_(batch_size_func), - batch_map_func_(batch_map_func), - pad_map_(pad_map), - python_multiprocessing_runtime_(std::move(python_multiprocessing_runtime)) { - this->AddChild(child); -} -#endif - -// constructor #2, called by C++ API -BatchNode::BatchNode(std::shared_ptr child, int32_t batch_size, bool drop_remainder) - : batch_size_(batch_size), drop_remainder_(drop_remainder), pad_(false) { - this->AddChild(child); -} - -std::shared_ptr BatchNode::Copy() { -#ifdef ENABLE_PYTHON - auto node = std::make_shared(nullptr, batch_size_, drop_remainder_, pad_, in_col_names_, out_col_names_, - batch_size_func_, batch_map_func_, pad_map_, python_multiprocessing_runtime_); -#else - auto node = std::make_shared(nullptr, batch_size_, drop_remainder_); -#endif - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void BatchNode::Print(std::ostream &out) const { - out << (Name() + "(batch_size:" + std::to_string(batch_size_) + - " drop_remainder:" + (drop_remainder_ ? "true" : "false") + ")"); -} - -Status BatchNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (batch_size_ != -1 && batch_size_ <= 0) { - std::string err_msg = "Batch: 'batch_size' should be positive integer, but got: " + std::to_string(batch_size_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - -#ifdef ENABLE_PYTHON - if (batch_map_func_ && pad_) { - std::string err_msg = "Batch: 'per_batch_map' and 'pad_info' should not be used at the same time."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (!in_col_names_.empty() && !batch_map_func_) { - std::string err_msg = "Batch: per_batch_map needs to be specified when input_columns is set."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } -#endif - return Status::OK(); -} - -Status BatchNode::Build(std::vector> *const node_ops) { -#ifdef ENABLE_PYTHON - auto op = std::make_shared(batch_size_, drop_remainder_, pad_, connector_que_size_, num_workers_, - in_col_names_, out_col_names_, batch_size_func_, batch_map_func_, pad_map_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - if (python_multiprocessing_runtime_ != nullptr) { - op->SetPythonMp(python_multiprocessing_runtime_); - } - node_ops->push_back(op); -#else - node_ops->push_back(std::make_shared(batch_size_, drop_remainder_, pad_, connector_que_size_, num_workers_, - in_col_names_, pad_map_)); -#endif - - return Status::OK(); -} - -// Get Dataset size -Status BatchNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } -#ifdef ENABLE_PYTHON - if (batch_size_func_) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), dataset_size)); - dataset_size_ = *dataset_size; - return Status::OK(); - } -#endif - int64_t num_rows; - RETURN_IF_NOT_OK(children_[0]->GetDatasetSize(size_getter, estimate, &num_rows)); - if (num_rows > 0 && batch_size_ > 0) { - if (drop_remainder_) { - num_rows = static_cast(floor(num_rows / (1.0 * batch_size_))); - } else { - num_rows = static_cast(ceil(num_rows / (1.0 * batch_size_))); - } - } - *dataset_size = num_rows; - dataset_size_ = num_rows; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status BatchNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status BatchNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status BatchNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["batch_size"] = batch_size_; - args["drop_remainder"] = drop_remainder_; -#ifdef ENABLE_PYTHON - args["input_columns"] = in_col_names_; - args["output_columns"] = out_col_names_; - if (batch_map_func_ != nullptr) { - args["per_batch_map"] = "pyfunc"; - } -#endif - *out_json = args; - return Status::OK(); -} - -Status BatchNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kBatchNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kBatchNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "batch_size", kBatchNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "drop_remainder", kBatchNode)); - int32_t batch_size = json_obj["batch_size"]; - bool drop_remainder = json_obj["drop_remainder"]; - *result = std::make_shared(ds, batch_size, drop_remainder); - (void)(*result)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*result)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h deleted file mode 100644 index 22f1a6332..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BATCH_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BATCH_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { -class BatchNode : public DatasetNode { - public: -#ifdef ENABLE_PYTHON - /// \brief Constructor #1, for Python API to create a BatchNode - BatchNode(std::shared_ptr child, int32_t batch_size, bool drop_remainder, bool pad, - const std::vector &in_col_names, const std::vector &out_col_names, - py::function batch_size_func, py::function batch_map_func, - std::map>> pad_map, - std::shared_ptr python_multiprocessing_runtime = nullptr); -#endif - - /// \brief Constructor #2 for C++ API to create a BatchNode - BatchNode(std::shared_ptr child, int32_t batch_size, bool drop_remainder); - - /// \brief Destructor - ~BatchNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kBatchNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - int32_t BatchSize() const { return batch_size_; } - bool DropRemainder() const { return drop_remainder_; } -#ifdef ENABLE_PYTHON - bool Pad() const { return pad_; } - const std::vector &InColNames() const { return in_col_names_; } - const std::vector &OutColNames() const { return out_col_names_; } - const py::function &BatchSizeFunc() const { return batch_size_func_; } - const py::function &BatchMapFunc() const { return batch_map_func_; } - const std::map>> &PadMap() const { return pad_map_; } -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - private: - int32_t batch_size_; - bool drop_remainder_; - bool pad_; - std::vector in_col_names_; - std::vector out_col_names_; -#ifdef ENABLE_PYTHON - py::function batch_size_func_; - py::function batch_map_func_; -#endif - std::map>> pad_map_; - std::shared_ptr python_multiprocessing_runtime_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BATCH_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc deleted file mode 100644 index e1816e283..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -BucketBatchByLengthNode::BucketBatchByLengthNode( - std::shared_ptr child, const std::vector &column_names, - const std::vector &bucket_boundaries, const std::vector &bucket_batch_sizes, - std::shared_ptr element_length_function, - const std::map>> &pad_info, bool pad_to_bucket_boundary, - bool drop_remainder) - : column_names_(column_names), - bucket_boundaries_(bucket_boundaries), - bucket_batch_sizes_(bucket_batch_sizes), - element_length_function_(element_length_function), - pad_info_(pad_info), - pad_to_bucket_boundary_(pad_to_bucket_boundary), - drop_remainder_(drop_remainder) { - this->AddChild(child); -} - -std::shared_ptr BucketBatchByLengthNode::Copy() { - auto node = std::make_shared(nullptr, column_names_, bucket_boundaries_, bucket_batch_sizes_, - element_length_function_, pad_info_, pad_to_bucket_boundary_, - drop_remainder_); - return node; -} - -void BucketBatchByLengthNode::Print(std::ostream &out) const { - out << (Name() + "(columns:" + PrintColumns(column_names_)); - int i = 0; - for (auto it : bucket_boundaries_) { - if (i == 0) { - out << ",bucket_boundaries:{"; - } - out << it; - if (i < bucket_boundaries_.size() - 1) { - out << ","; - } else { - out << "}"; - } - i++; - } - i = 0; - for (auto it : bucket_batch_sizes_) { - if (i == 0) { - out << ",bucket_batch_sizes:{"; - } - out << it; - if (i < bucket_batch_sizes_.size() - 1) { - out << ","; - } else { - out << "}"; - } - i++; - } - out << ")"; -} - -Status BucketBatchByLengthNode::Build(std::vector> *const node_ops) { - bucket_boundaries_.insert(bucket_boundaries_.begin(), 0); - auto op = std::make_shared(column_names_, bucket_boundaries_, bucket_batch_sizes_, - element_length_function_, pad_info_, pad_to_bucket_boundary_, - drop_remainder_, connector_que_size_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - if (bucket_boundaries_[0] == 0) { - bucket_boundaries_.erase(bucket_boundaries_.begin()); - } - return Status::OK(); -} - -Status BucketBatchByLengthNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (element_length_function_ == nullptr && column_names_.size() != 1) { - std::string err_msg = - "BucketBatchByLengthNode: when element_length_function is not specified, size of column_name must be 1 but is: " + - std::to_string(column_names_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // Check bucket_boundaries: must be positive and strictly increasing - if (bucket_boundaries_.empty()) { - std::string err_msg = "BucketBatchByLengthNode: bucket_boundaries cannot be empty."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - for (int i = 0; i < bucket_boundaries_.size(); i++) { - if (bucket_boundaries_[i] <= 0) { - std::string err_msg = - "BucketBatchByLength: bucket_boundaries must only contain positive numbers. However, the element at index: " + - std::to_string(i) + " was: " + std::to_string(bucket_boundaries_[i]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (i > 0 && bucket_boundaries_[i - 1] >= bucket_boundaries_[i]) { - std::string err_msg = - "BucketBatchByLength: bucket_boundaries must be strictly increasing. However, the elements at index: " + - std::to_string(i - 1) + " and " + std::to_string(i) + " were: " + std::to_string(bucket_boundaries_[i - 1]) + - " and " + std::to_string(bucket_boundaries_[i]) + " respectively."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - if (!column_names_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("BucketBatchByLengthNode", "column_names", column_names_)); - } - - // Check bucket_batch_sizes: must be positive - if (bucket_batch_sizes_.empty()) { - std::string err_msg = "BucketBatchByLengthNode: bucket_batch_sizes must be non-empty"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (bucket_batch_sizes_.size() != bucket_boundaries_.size() + 1) { - std::string err_msg = - "BucketBatchByLengthNode: bucket_batch_sizes's size must equal the size of bucket_boundaries + 1"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (std::any_of(bucket_batch_sizes_.begin(), bucket_batch_sizes_.end(), [](int i) { return i <= 0; })) { - std::string err_msg = "BucketBatchByLengthNode: bucket_batch_sizes must only contain positive numbers."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h deleted file mode 100644 index 2de9b70a7..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUCKET_BATCH_BY_LENGTH_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUCKET_BATCH_BY_LENGTH_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class BucketBatchByLengthNode : public DatasetNode { - public: - /// \brief Constructor - BucketBatchByLengthNode(std::shared_ptr child, const std::vector &column_names, - const std::vector &bucket_boundaries, const std::vector &bucket_batch_sizes, - std::shared_ptr element_length_function = nullptr, - const std::map>> &pad_info = {}, - bool pad_to_bucket_boundary = false, bool drop_remainder = false); - - /// \brief Destructor - ~BucketBatchByLengthNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kBucketBatchByLengthNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - bool IsSizeDefined() override { return false; }; - - /// \brief Getter functions - const std::vector &ColumnNames() const { return column_names_; } - const std::vector &BucketBoundaries() const { return bucket_boundaries_; } - const std::vector &BucketBatchSizes() const { return bucket_batch_sizes_; } - const std::shared_ptr &ElementLengthFunction() const { return element_length_function_; } - const std::map>> &PadInfo() const { return pad_info_; } - bool PadToBucketBoundary() const { return pad_to_bucket_boundary_; } - bool DropRemainder() const { return drop_remainder_; } - - private: - std::vector column_names_; - std::vector bucket_boundaries_; - std::vector bucket_batch_sizes_; - std::shared_ptr element_length_function_; - std::map>> pad_info_; - bool pad_to_bucket_boundary_; - bool drop_remainder_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUCKET_BATCH_BY_LENGTH_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.cc deleted file mode 100644 index f2a677176..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.cc +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_sentence_piece_vocab_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -BuildSentenceVocabNode::BuildSentenceVocabNode(std::shared_ptr child, - std::shared_ptr vocab, - const std::vector &col_names, int32_t vocab_size, - float character_coverage, SentencePieceModel model_type, - const std::unordered_map ¶ms) - : vocab_(vocab), - col_names_(col_names), - vocab_size_(vocab_size), - character_coverage_(character_coverage), - model_type_(model_type), - params_(params) { - this->AddChild(child); -} - -std::shared_ptr BuildSentenceVocabNode::Copy() { - auto node = std::make_shared(nullptr, vocab_, col_names_, vocab_size_, character_coverage_, - model_type_, params_); - return node; -} - -void BuildSentenceVocabNode::Print(std::ostream &out) const { - out << (Name() + "," + "columns:" + PrintColumns(col_names_) + ",vocab_size:" + std::to_string(vocab_size_) + - ",...)"); -} - -// Function to build BuildSentenceVocabNode -Status BuildSentenceVocabNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(vocab_, col_names_, vocab_size_, character_coverage_, - model_type_, params_, connector_que_size_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -Status BuildSentenceVocabNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (vocab_ == nullptr) { - std::string err_msg = "BuildSentenceVocabNode: vocab is null."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (model_type_ != SentencePieceModel::kUnigram && model_type_ != SentencePieceModel::kBpe && - model_type_ != SentencePieceModel::kChar && model_type_ != SentencePieceModel::kWord) { - std::string err_msg = "BuildSentenceVocabNode: Invalid SentencePieceModel, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (vocab_size_ <= 0) { - std::string err_msg = - "BuildSentenceVocabNode: vocab_size should be positive, but got: " + std::to_string(vocab_size_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (character_coverage_ < 0.98f || character_coverage_ > 1.0f) { - std::string err_msg = "BuildSentenceVocabNode: character_coverage should to be between 0.98 and 1.0, but got " + - std::to_string(character_coverage_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (!col_names_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("BuildVocabNode", "columns", col_names_)); - } - - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status BuildSentenceVocabNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status BuildSentenceVocabNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h deleted file mode 100644 index dcabf6fea..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_SENTENCE_PIECE_VOCAB_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_SENTENCE_PIECE_VOCAB_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -namespace mindspore { -namespace dataset { -class BuildSentenceVocabNode : public DatasetNode { - public: - /// \brief Constructor - BuildSentenceVocabNode(std::shared_ptr child, std::shared_ptr vocab, - const std::vector &col_names, int32_t vocab_size, float character_coverage, - SentencePieceModel model_type, const std::unordered_map ¶ms); - - /// \brief Destructor - ~BuildSentenceVocabNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kBuildSentencePieceVocabNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - const std::shared_ptr &GetVocab() const { return vocab_; } - const std::vector &ColNames() const { return col_names_; } - int32_t VocabSize() const { return vocab_size_; } - float CharacterCoverage() const { return character_coverage_; } - SentencePieceModel ModelType() const { return model_type_; } - const std::unordered_map &Params() const { return params_; } - - private: - std::shared_ptr vocab_; - std::vector col_names_; - int32_t vocab_size_; - float character_coverage_; - SentencePieceModel model_type_; - std::unordered_map params_; -}; -} // namespace dataset -} // namespace mindspore -#endif // #ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_SENTENCE_PIECE_VOCAB_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc deleted file mode 100644 index 8dd0c68d6..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/build_vocab_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -BuildVocabNode::BuildVocabNode(std::shared_ptr child, std::shared_ptr vocab, - const std::vector &columns, const std::pair &freq_range, - int64_t top_k, const std::vector &special_tokens, bool special_first) - : vocab_(vocab), - columns_(columns), - freq_range_(freq_range), - top_k_(top_k), - special_tokens_(special_tokens), - special_first_(special_first) { - this->AddChild(child); -} - -std::shared_ptr BuildVocabNode::Copy() { - auto node = - std::make_shared(nullptr, vocab_, columns_, freq_range_, top_k_, special_tokens_, special_first_); - return node; -} - -void BuildVocabNode::Print(std::ostream &out) const { - out << (Name() + "(," + "columns:" + PrintColumns(columns_) + ",...)"); -} - -// Function to build BuildVocabNode -Status BuildVocabNode::Build(std::vector> *const node_ops) { - std::shared_ptr build_vocab_op; - build_vocab_op = std::make_shared(vocab_, columns_, freq_range_, top_k_, special_tokens_, - special_first_, num_workers_, connector_que_size_); - build_vocab_op->SetTotalRepeats(GetTotalRepeats()); - build_vocab_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(build_vocab_op); - return Status::OK(); -} - -Status BuildVocabNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (vocab_ == nullptr) { - std::string err_msg = "BuildVocabNode: vocab is null."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (top_k_ <= 0) { - std::string err_msg = "BuildVocabNode: top_k should be positive, but got: " + std::to_string(top_k_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (freq_range_.first < 0 || freq_range_.second > kDeMaxFreq || freq_range_.first > freq_range_.second) { - std::string err_msg = "BuildVocabNode: frequency_range [a,b] violates 0 <= a <= b (a,b are inclusive), but got [" + - std::to_string(freq_range_.first) + ", " + std::to_string(freq_range_.second) + "]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (!columns_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("BuildVocabNode", "columns", columns_)); - } - - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status BuildVocabNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status BuildVocabNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h deleted file mode 100644 index d0a5b7c70..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_VOCAB_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_VOCAB_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class BuildVocabNode : public DatasetNode { - public: - /// \brief Constructor - BuildVocabNode(std::shared_ptr child, std::shared_ptr vocab, - const std::vector &columns, const std::pair &freq_range, int64_t top_k, - const std::vector &special_tokens, bool special_first); - - /// \brief Destructor - ~BuildVocabNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kBuildVocabNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - const std::shared_ptr &GetVocab() const { return vocab_; } - const std::vector &Columns() const { return columns_; } - const std::pair &FreqRange() const { return freq_range_; } - int64_t TopK() const { return top_k_; } - const std::vector &SpecialTokens() const { return special_tokens_; } - bool SpecialFirst() const { return special_first_; } - - private: - std::shared_ptr vocab_; - std::vector columns_; - std::pair freq_range_; - int64_t top_k_; - std::vector special_tokens_; - bool special_first_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_BUILD_VOCAB_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.cc deleted file mode 100644 index 8b95fc7ac..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.cc +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -CacheLookupNode::CacheLookupNode(std::shared_ptr child, std::shared_ptr sampler, - std::shared_ptr cache) - : DatasetNode(std::move(cache)), sampler_(sampler), lookup_op_(nullptr), lookup_node_copy_(nullptr) { - this->AddChild(child); -} - -void CacheLookupNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr CacheLookupNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(nullptr, sampler, cache_); - lookup_node_copy_ = node; - return node; -} - -Status CacheLookupNode::ValidateParams() { - RETURN_IF_NOT_OK(ValidateDatasetSampler("CacheNode", sampler_)); - return Status::OK(); -} - -Status CacheLookupNode::Build(std::vector> *node_ops) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_ != nullptr, - "Internal error. Attempt to create a cache lookup node without cache client."); - RETURN_IF_NOT_OK(cache_->Build()); - RETURN_IF_NOT_OK(cache_->CreateCacheLookupOp(num_workers_, connector_que_size_, sampler_, &lookup_op_)); - lookup_op_->SetTotalRepeats(GetTotalRepeats()); - lookup_op_->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(lookup_op_); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status CacheLookupNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status CacheLookupNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -std::shared_ptr CacheLookupNode::SamplerCopy() { - // CacheLookupNode should already been copied, so we just return it here - return std::static_pointer_cast(lookup_node_copy_); -} - -Status CacheLookupNode::SamplerBuild(std::shared_ptr *const out) { - // Runtime cache lookup op should already been built, so we just return it here - auto lookup_op = std::dynamic_pointer_cast(lookup_op_); - *out = std::shared_ptr(lookup_op); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h deleted file mode 100644 index 400bcfa39..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_LOOKUP_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_LOOKUP_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_lookup_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CacheLookupNode : public DatasetNode, public SamplerObj { - public: - /// \brief Constructor - CacheLookupNode(std::shared_ptr child, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~CacheLookupNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCacheLookupNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to convert a SamplerObj class into a runtime sampler object - /// \param[out] out Shared pointer to the newly created Sampler - /// \return The Status code of the function. It returns OK status if sampler is created successfully. - Status SamplerBuild(std::shared_ptr *const out) override; - - /// \brief a base class override function to copy a SamplerObj class - /// \return Shared pointers to the newly copied SamplerObj - std::shared_ptr SamplerCopy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() { return sampler_; } - - private: - std::shared_ptr sampler_; - std::shared_ptr lookup_op_; - std::shared_ptr lookup_node_copy_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_LOOKUP_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.cc deleted file mode 100644 index 614a47915..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.cc +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_merge_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -CacheMergeNode::CacheMergeNode(std::shared_ptr child, std::shared_ptr cache) - : DatasetNode(std::move(cache)) { - nary_op_ = true; - this->AddChild(child); -} - -void CacheMergeNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr CacheMergeNode::Copy() { - auto node = std::make_shared(nullptr, cache_); - return node; -} - -Status CacheMergeNode::ValidateParams() { return Status::OK(); } - -Status CacheMergeNode::Build(std::vector> *node_ops) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_ != nullptr, - "Internal error. Attempt to create a cache merge node without cache client."); - RETURN_IF_NOT_OK(cache_->Build()); - std::shared_ptr merge_op = nullptr; - RETURN_IF_NOT_OK(cache_->CreateCacheMergeOp(num_workers_, connector_que_size_, &merge_op)); - merge_op->SetTotalRepeats(GetTotalRepeats()); - merge_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(merge_op); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status CacheMergeNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status CacheMergeNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h deleted file mode 100644 index e1b5baa0c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_MERGE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_MERGE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CacheMergeNode : public DatasetNode { - public: - /// \brief Constructor - CacheMergeNode(std::shared_ptr child, std::shared_ptr cache); - - /// \brief Destructor - ~CacheMergeNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCacheMergeNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_MERGE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.cc deleted file mode 100644 index 0d743b32b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.cc +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/cache_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -CacheNode::CacheNode(std::shared_ptr child, std::shared_ptr sampler, - std::shared_ptr cache) - : DatasetNode(std::move(cache)), sampler_(sampler) { - this->AddChild(child); -} - -void CacheNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr CacheNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(nullptr, sampler, cache_); - return node; -} - -Status CacheNode::ValidateParams() { - RETURN_IF_NOT_OK(ValidateDatasetSampler("CacheNode", sampler_)); - return Status::OK(); -} - -Status CacheNode::Build(std::vector> *node_ops) { - CHECK_FAIL_RETURN_UNEXPECTED(cache_ != nullptr, - "Internal error. Attempt to create a cache node without cache client."); - RETURN_IF_NOT_OK(cache_->Build()); - std::shared_ptr cache_op = nullptr; - RETURN_IF_NOT_OK(cache_->CreateCacheOp(num_workers_, connector_que_size_, sampler_, &cache_op)); - cache_op->SetTotalRepeats(GetTotalRepeats()); - cache_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(cache_op); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status CacheNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status CacheNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h deleted file mode 100644 index 929087040..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CacheNode : public DatasetNode { - public: - /// \brief Constructor - CacheNode(std::shared_ptr child, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~CacheNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCacheNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CACHE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.cc deleted file mode 100644 index 5a55c7a50..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.cc +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/concat_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -// Function to build ConcatOp -ConcatNode::ConcatNode(const std::vector> &datasets, - const std::shared_ptr &sampler, - const std::vector> &children_flag_and_nums, - const std::vector> &children_start_end_index, - const std::vector &children_sizes) - : sampler_(sampler), - children_flag_and_nums_(children_flag_and_nums), - children_start_end_index_(children_start_end_index), - children_sizes_(children_sizes) { - nary_op_ = true; - for (auto const &child : datasets) { - AddChild(child); - } -} - -std::shared_ptr ConcatNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - // create an empty vector to copy a concat - auto node = std::make_shared(std::vector>(), sampler, - children_flag_and_nums_, children_start_end_index_, children_sizes_); - return node; -} - -void ConcatNode::Print(std::ostream &out) const { out << Name(); } - -Status ConcatNode::ValidateParams() { - constexpr size_t kMinChildrenSize = 2; - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (children_.size() < kMinChildrenSize) { - std::string err_msg = "Concat: concatenated datasets are not specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (find(children_.begin(), children_.end(), nullptr) != children_.end()) { - std::string err_msg = "Concat: concatenated datasets should not be null."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // Either one of children_flag_and_nums_ or children_start_end_index_ should be non-empty. - if ((children_flag_and_nums_.empty() && !children_start_end_index_.empty()) || - (!children_flag_and_nums_.empty() && children_start_end_index_.empty())) { - std::string err_msg = "Concat: 'children_flag_and_nums' and 'children_start_end_index' should be used together"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Get Dataset size -Status ConcatNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - // calculate the total size of all nodes - int64_t total_dataset_size = 0; - int64_t child_dataset_size = 0; - for (int idx = 0; idx < children_.size(); idx++) { - if (children_flag_and_nums_.empty() || children_flag_and_nums_[idx].second == 0) { - RETURN_IF_NOT_OK(children_[idx]->GetDatasetSize(size_getter, false, &child_dataset_size)); - total_dataset_size += child_dataset_size; - } else { - total_dataset_size += children_flag_and_nums_[idx].second; - } - } - - // calculate the size of the shard - int64_t shard_dataset_size = 0; - std::shared_ptr sampler_rt_base = nullptr; - if (sampler_) { - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt_base)); - } - std::shared_ptr sampler_rt = - sampler_ ? std::dynamic_pointer_cast(sampler_rt_base) : nullptr; - if (sampler_rt != nullptr) { - RETURN_IF_NOT_OK(sampler_rt->SetNumRowsInDataset(total_dataset_size)); - RETURN_IF_NOT_OK(sampler_rt->InitSampler()); - - // (total_size % num_shards != 0) & shard_id >= (remainder) ? CalculateNumSamples()-1 : CalculateNumSamples() - // example: 23 rows, 10 shards --> shard sizes = {3,3,3,2,2,2,2,2,2,2} - if ((sampler_rt->GetNumSamples() % sampler_rt->GetDeviceNum()) >= 0 && - sampler_rt->GetDeviceID() >= (sampler_rt->GetNumSamples() % sampler_rt->GetDeviceNum())) { - shard_dataset_size = sampler_rt->GetNumSamples() / sampler_rt->GetDeviceNum(); - } else { - shard_dataset_size = sampler_rt->GetNumSamples() / sampler_rt->GetDeviceNum() + 1; - } - } else { - shard_dataset_size = total_dataset_size; - } - - *dataset_size = shard_dataset_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status ConcatNode::Build(std::vector> *const node_ops) { - std::shared_ptr op; - if (children_flag_and_nums_.empty() || children_start_end_index_.empty()) { - op = std::make_shared(); - } else { - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - op = std::make_shared(sampler_rt, children_flag_and_nums_, children_start_end_index_, children_sizes_); - } - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status ConcatNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status ConcatNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status ConcatNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["children_flag_and_nums"] = children_flag_and_nums_; - args["children_start_end_index"] = children_start_end_index_; - args["children_sizes"] = children_sizes_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ConcatNode::from_json(nlohmann::json json_obj, std::vector> datasets, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kConcatNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "children_flag_and_nums", kConcatNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "children_start_end_index", kConcatNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "children_sizes", kConcatNode)); - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::vector> children_flag_and_nums = json_obj["children_flag_and_nums"]; - std::vector> children_start_end_index = json_obj["children_start_end_index"]; - std::vector children_sizes = json_obj["children_sizes"]; - *result = - std::make_shared(datasets, sampler, children_flag_and_nums, children_start_end_index, children_sizes); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h deleted file mode 100644 index b68eca0b8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CONCAT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CONCAT_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class ConcatNode : public DatasetNode { - public: - /// \brief Constructor - explicit ConcatNode(const std::vector> &datasets, - const std::shared_ptr &sampler = nullptr, - const std::vector> &children_flag_and_nums = {}, - const std::vector> &children_start_end_index = {}, - const std::vector &children_sizes = {}); - - /// \brief Destructor - ~ConcatNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kConcatNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - bool IsSizeDefined() override { return false; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] datasets A vector of datasets for Concat input - /// \param[out] result Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::vector> datasets, - std::shared_ptr *result); -#endif - - /// \brief Getter functions - const std::vector> &ChildrenFlagAndNums() const { return children_flag_and_nums_; } - const std::vector> &ChildrenStartEndIndex() const { return children_start_end_index_; } - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - std::shared_ptr sampler_; - std::vector> children_flag_and_nums_; - std::vector> children_start_end_index_; - std::vector children_sizes_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_CONCAT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.cc deleted file mode 100644 index 27d9d14fb..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.cc +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/data_queue_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "utils/ms_context.h" - -namespace mindspore { -namespace dataset { -// Constructor for DataQueueNode -DataQueueNode::DataQueueNode(std::shared_ptr child, std::string queue_name, std::string device_type, - int32_t device_id, bool send_epoch_end, int32_t total_batch, bool create_data_info_queue) - : queue_name_(std::move(queue_name)), - device_id_(device_id), - device_type_(std::move(device_type)), - send_epoch_end_(send_epoch_end), - total_batch_(total_batch), - create_data_info_queue_(create_data_info_queue) { - this->AddChild(child); -} - -std::shared_ptr DataQueueNode::Copy() { - auto node = std::make_shared(nullptr, queue_name_, device_type_, device_id_, send_epoch_end_, - total_batch_, create_data_info_queue_); - return node; -} - -void DataQueueNode::Print(std::ostream &out) const { - out << (Name() + ",send_epoch_end:" + (send_epoch_end_ ? "true" : "false") + - ",total_batch:" + std::to_string(total_batch_) + ")"); -} - -// Validator for DataQueueNode -Status DataQueueNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateScalar("Transfer", "Total batches", total_batch_, {0}, false)); - return Status::OK(); -} - -// Function to build DataQueueNode -Status DataQueueNode::Build(std::vector> *const node_ops) { - if (queue_name_.empty()) { - // Get a uuid for queue name - queue_name_ = Services::GetUniqueID(); - } - - // This is an issue from MindSpore C++ user - // https://gitee.com/mindspore/mindspore/issues/I39J9A - // Link _c_expression.so and _c_dataengine.so simultaneously will cause heap overflow because MindData uses MSContext. - // We should find a new way to get device_type here. - if (device_type_.empty()) { - device_type_ = kCPUDevice; - } - - // Get device type from ms context - // Convert device_type_ from string to DeviceType - DataQueueOp::DeviceType type; - if (device_type_ == kCPUDevice) { - type = DataQueueOp::DeviceType::CPU; - } else if (device_type_ == kGPUDevice) { - type = DataQueueOp::DeviceType::GPU; - } else if (device_type_ == kAscendDevice) { - type = DataQueueOp::DeviceType::Ascend; - } else { - std::string err_msg = "Unknown device target, support CPU, GPU or Ascend"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Ascend does not support receiving empty data at the moment, so send_epoch_end needs to be set to false. - send_epoch_end_ = false; - auto op = std::make_shared(queue_name_, type, device_id_, send_epoch_end_, total_batch_, - create_data_info_queue_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status DataQueueNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status DataQueueNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status DataQueueNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["queue_name"] = queue_name_; - args["device_type"] = device_type_; - args["device_id"] = device_id_; - args["send_epoch_end"] = send_epoch_end_; - args["total_batch"] = total_batch_; - args["create_data_info_queue"] = create_data_info_queue_; - *out_json = args; - return Status::OK(); -} - -Status DataQueueNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "queue_name", kTransferNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "device_type", kTransferNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "device_id", kTransferNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "send_epoch_end", kTransferNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "total_batch", kTransferNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "create_data_info_queue", kTransferNode)); - std::string queue_name = json_obj["queue_name"]; - std::string device_type = json_obj["device_type"]; - int32_t device_id = json_obj["device_id"]; - bool send_epoch_end = json_obj["send_epoch_end"]; - int32_t total_batch = json_obj["total_batch"]; - bool create_data_info_queue = json_obj["create_data_info_queue"]; - *result = std::make_shared(ds, queue_name, device_type, device_id, send_epoch_end, total_batch, - create_data_info_queue); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h deleted file mode 100644 index eea75bed2..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DATA_QUEUE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DATA_QUEUE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class DataQueueNode : public DatasetNode { - public: - /// \brief Constructor - DataQueueNode(std::shared_ptr child, std::string queue_name, std::string device_type, int32_t device_id, - bool send_epoch_end, int32_t total_batch, bool create_data_info_queue); - - /// \brief Destructor - ~DataQueueNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kTransferNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - static Status get_distribution(std::shared_ptr ds, int32_t *device_id); - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - const std::string &QueueName() const { return queue_name_; } - int32_t DeviceId() const { return device_id_; } - const std::string &DeviceType() const { return device_type_; } - bool SendEpochEnd() const { return send_epoch_end_; } - int32_t TotalBatch() const { return total_batch_; } - bool CreateDataInfoQueue() const { return create_data_info_queue_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - private: - std::string queue_name_; - int32_t device_id_; - std::string device_type_; - bool send_epoch_end_; - int32_t total_batch_; - bool create_data_info_queue_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DATA_QUEUE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.cc deleted file mode 100644 index 83fb2a23b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.cc +++ /dev/null @@ -1,686 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// Helper function to compute a default shuffle size -Status ComputeShuffleSize(int64_t num_files, int64_t num_devices, int64_t num_rows, int64_t total_rows, - int64_t *shuffle_size) { - RETURN_UNEXPECTED_IF_NULL(shuffle_size); - const int64_t average_files_multiplier = 4; - const int64_t shuffle_max = 10000; - - // Adjust the num rows per shard if sharding was given - if (num_devices > 0) { - if (num_rows % num_devices == 0) { - num_rows = num_rows / num_devices; - } else { - num_rows = (num_rows / num_devices) + 1; - } - } - - // Cap based on total rows directive. Some ops do not have this and give value of 0. - if (total_rows > 0) { - num_rows = std::min(num_rows, total_rows); - } - - // get the average per file - CHECK_FAIL_RETURN_UNEXPECTED(num_files != 0, "The size of dataset_files must be greater than 0."); - int64_t avg_rows_per_file = num_rows / num_files; - - if (avg_rows_per_file != 0) { - *shuffle_size = std::min(avg_rows_per_file * average_files_multiplier, shuffle_max); - } else { - *shuffle_size = shuffle_max; - } - - return Status::OK(); -} - -// Helper function to inject a shuffle operator over top of current operator being built -Status AddShuffleOp(int64_t num_files, int64_t num_devices, int64_t num_rows, int64_t total_rows, - int32_t connector_que_size, std::shared_ptr *shuffle_op) { - RETURN_UNEXPECTED_IF_NULL(shuffle_op); - int64_t shuffle_size = 0; - RETURN_IF_NOT_OK(ComputeShuffleSize(num_files, num_devices, num_rows, total_rows, &shuffle_size)); - MS_LOG(INFO) << "Dataset::AddShuffleOp - num_rows: " << num_rows << ", shuffle_size: " << shuffle_size; - // Add the shuffle op - *shuffle_op = std::make_shared(shuffle_size, GetSeed(), connector_que_size, true); - return Status::OK(); -} - -// Helper function to validate dataset directory parameter -Status ValidateDatasetDirParam(const std::string &dataset_name, std::string dataset_dir) { - if (dataset_dir.empty()) { - std::string err_msg = dataset_name + ": dataset_dir is not specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::string real_path; - RETURN_IF_NOT_OK(Path::RealPath(dataset_dir, real_path)); - Path dir(dataset_dir); - if (!dir.IsDirectory()) { - std::string err_msg = dataset_name + ": dataset_dir: [" + dataset_dir + "] is an invalid directory path."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (access(dataset_dir.c_str(), R_OK) == -1) { - std::string err_msg = dataset_name + ": No access to specified dataset path: " + dataset_dir; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -// Helper function to validate dataset files parameter -Status ValidateDatasetFilesParam(const std::string &dataset_name, const std::vector &dataset_files, - const std::string &file_name) { - if (dataset_files.empty()) { - std::string err_msg = dataset_name + ": dataset files list is empty, check input dataset_files."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - for (const auto &f : dataset_files) { - Path dataset_file(f); - if (!dataset_file.Exists()) { - std::string err_msg = dataset_name + ": " + file_name + ": [" + f + "] is invalid or does not exist."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (access(dataset_file.ToString().c_str(), R_OK) == -1) { - std::string err_msg = dataset_name + ": No access to specified " + file_name + ": " + f; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - return Status::OK(); -} - -// Helper function to validate dataset num_shards and shard_id parameters -Status ValidateDatasetShardParams(const std::string &dataset_name, int32_t num_shards, int32_t shard_id) { - if (num_shards <= 0) { - std::string err_msg = dataset_name + ": Invalid num_shards: " + std::to_string(num_shards); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (shard_id < 0 || shard_id >= num_shards) { - // num_shards - std::string err_msg = dataset_name + ": Invalid input, shard_id: " + std::to_string(shard_id) + - ", num_shards: " + std::to_string(num_shards); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -// Helper function to validate dataset sampler parameter -Status ValidateDatasetSampler(const std::string &dataset_name, const std::shared_ptr &sampler) { - if (sampler == nullptr) { - std::string err_msg = dataset_name + ": Sampler is not constructed correctly, sampler: nullptr"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(sampler->ValidateParams()); - return Status::OK(); -} - -Status ValidateStringValue(const std::string &dataset_name, const std::string &str, - const std::unordered_set &valid_strings) { - if (valid_strings.find(str) == valid_strings.end()) { - std::string init; - std::string mode = std::accumulate(valid_strings.begin(), valid_strings.end(), init, - [](std::string a, std::string b) { return std::move(a) + " " + std::move(b); }); - std::string err_msg = dataset_name + ": " + str + " does not match any mode in [" + mode + " ]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Helper function to validate dataset input/output column parameter -Status ValidateDatasetColumnParam(const std::string &dataset_name, const std::string &column_param, - const std::vector &columns) { - if (columns.empty()) { - std::string err_msg = dataset_name + ": '" + column_param + "' should not be empty string"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (uint32_t i = 0; i < columns.size(); ++i) { - if (columns[i].empty()) { - std::string err_msg = dataset_name + ": '" + column_param + "' [" + std::to_string(i) + "] must not be empty"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - std::set columns_set; - for (auto &column_name : columns) { - auto result = columns_set.insert(column_name); - if (result.second == false) { - std::string err_msg = dataset_name + ": '" + column_param + - "' : Invalid parameter, duplicate column names are not allowed: " + *result.first; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - return Status::OK(); -} - -Status ValidateMapKey(const std::string &dataset_name, const std::string &key, - const std::map> &map) { - if (map.find(key) == map.end()) { - std::string init; - std::string mode = std::accumulate(map.begin(), map.end(), init, - [](std::string a, auto b) { return std::move(a) + " " + std::move(b.first); }); - std::string err_msg = dataset_name + ": " + key + " does not match any key in [" + mode + " ]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status ValidateMapValue(const std::string &dataset_name, const std::string &str, - const std::vector &valid_strings) { - if (find(valid_strings.begin(), valid_strings.end(), str) == valid_strings.end()) { - std::string init; - std::string mode = std::accumulate(valid_strings.begin(), valid_strings.end(), init, - [](std::string a, std::string b) { return std::move(a) + " " + std::move(b); }); - std::string err_msg = dataset_name + ": " + str + " does not match any string in [" + mode + " ]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, int32_t num_shards, - int32_t shard_id) { - if (shuffle_mode != dataset::ShuffleMode::kFalse) { - if (num_shards > 1) { - // If shuffle enabled, sharding enabled, use distributed random sampler - return DistributedSampler(num_shards, shard_id, shuffle_mode, num_samples).Parse(); - } - // If shuffle enabled, sharding disabled, use random sampler - return RandomSampler(num_samples >= 0, num_samples, shuffle_mode).Parse(); - } - if (num_shards > 1) { - // If shuffle disabled, sharding enabled, use distributed sequential sampler - return DistributedSampler(num_shards, shard_id, shuffle_mode, num_samples).Parse(); - } - // If shuffle disabled, sharding disabled, use sequential sampler - return SequentialSampler(0, num_samples).Parse(); -} - -// Constructor to initialize the cache -DatasetNode::DatasetNode(const std::shared_ptr &dataset_cache) : DatasetNode() { cache_ = dataset_cache; } - -std::shared_ptr DatasetNode::SetNumWorkers(int32_t num_workers) { - num_workers_ = num_workers; - return shared_from_this(); -} - -std::shared_ptr DatasetNode::SetConnectorQueueSize(int32_t connector_queue_size) { - connector_que_size_ = connector_queue_size; - return shared_from_this(); -} - -std::shared_ptr DatasetNode::SetDatasetCache(const std::shared_ptr &cache) { - cache_ = cache; - return shared_from_this(); -} - -DatasetNode::DatasetNode() - : cache_(nullptr), - parent_(nullptr), - children_({}), - dataset_size_(-1), - mappable_(kNotADataSource), - nary_op_(false), - descendant_of_cache_(false), - total_repeats_(-1), - num_epochs_(1) { - // Fetch some default value from config manager - std::shared_ptr cfg = GlobalContext::config_manager(); - num_workers_ = cfg->num_parallel_workers(); - connector_que_size_ = cfg->op_connector_size(); - worker_connector_size_ = cfg->worker_connector_size(); -} - -std::string DatasetNode::PrintColumns(const std::vector &columns) const { - std::string me; - if (columns.empty()) { - me = ""; - } else { - me = "["; - auto i = 0; - for (auto it = columns.begin(); it < columns.end(); ++it, ++i) { - me += *it; - if (i < columns.size() - 1) { - me += ", "; - } else { - me += "]"; - } - } - } - return me; -} - -void DatasetNode::PrintTree(std::ostream &out) const { - int level = 0; - PrintNode(out, &level); -} - -void DatasetNode::PrintNode(std::ostream &out, int *level) const { - const std::string prefix = "+-"; - const std::string indent = "| "; - out << prefix; - Print(out); - for (const auto &c : this->Children()) { - out << '\n'; - ++(*level); - for (auto i = 0; i < *level; i++) { - out << indent; - } - c->PrintNode(out, level); - --(*level); - } -} - -// Add a node as a child, node's parent needs to be empty -// This function will allow child to be a nullptr, in which case it will simply skip. -// This function is used only when building IR node one by one from parsing the user code. -// During the parsing, we allow a node to have more than one parent, possibly forming a graph. -// It does not maintain the parent_ attribute of the node, which enforces a single parent and a tree structure. -void DatasetNode::AddChild(std::shared_ptr child) { - if (child != nullptr) { - children_.push_back(child); - } -} - -/* - * AppendChild() appending as the last child of this node. The new node must have no parent. - * - * Input tree: - * ds4 - * / \ - * ds3 ds2 - * | - * ds1 - * - * ds4->AppendChild(ds6) yields this tree - * - * _ ds4 _ - * / | \ - * ds3 ds2 ds6 - * | - * ds1 - * - */ -Status DatasetNode::AppendChild(std::shared_ptr child) { - CHECK_FAIL_RETURN_UNEXPECTED(IsOrphanNode(child), "Node to append must be an orphan node."); - CHECK_FAIL_RETURN_UNEXPECTED((IsUnaryOperator() && Children().empty()) || IsNaryOperator(), - "This node must be a unary operator with no child or an n-ary operator"); - children_.push_back(child); - child->parent_ = this; - return Status::OK(); -} - -/* - * InsertChildAt(, ) inserts the to be at the index of the vector of its child nodes. - * As in the convention of C++, starts at position 0. - * If the is a negative number or larger than the size of the vector minus one, an error is raised. - */ -Status DatasetNode::InsertChildAt(int32_t pos, std::shared_ptr child) { - CHECK_FAIL_RETURN_UNEXPECTED(pos > -1 && pos <= children_.size(), "Position must in the range of [0, size]"); - CHECK_FAIL_RETURN_UNEXPECTED(IsOrphanNode(child), "Node to append must be an orphan node."); - CHECK_FAIL_RETURN_UNEXPECTED((IsUnaryOperator() && Children().empty()) || IsNaryOperator(), - "This node must be a unary operator with no child or an n-ary operator"); - children_.insert(children_.begin() + pos, child); - child->parent_ = this; - return Status::OK(); -} - -/* - * Insert the input above this node - * Input tree: - * ds4 - * / \ - * ds3 ds2 - * | - * ds1 - * - * Case 1: If we want to insert a new node ds5 between ds4 and ds3, use - * ds3->InsertAbove(ds5) - * - * ds4 - * / \ - * ds5 ds2 - * | - * ds3 - * | - * ds1 - * - * Case 2: Likewise, ds2->InsertAbove(ds6) yields - * - * ds4 - * / \ - * ds3 ds6 - * | | - * ds1 ds2 - * - * Case 3: We can insert a new node between ds3 and ds1 by ds1->InsertAbove(ds7) - * - * ds4 - * / \ - * ds3 ds2 - * | - * ds7 - * | - * ds1 - * - * InsertAbove() cannot use on the root node of a tree. - */ -Status DatasetNode::InsertAbove(std::shared_ptr node) { - CHECK_FAIL_RETURN_UNEXPECTED(IsOrphanNode(node), "Node to insert must be an orphan node."); - CHECK_FAIL_RETURN_UNEXPECTED(parent_ != nullptr, "This node must not be the root or a node without parent."); - auto parent = parent_; - - // The following fields of these three nodes are changed in this function: - // 1. parent->children_ - // 2. node->parent_ and node->children_ - // 3. this->parent_ - auto current_node_itr = std::find(parent_->children_.begin(), parent_->children_.end(), shared_from_this()); - *current_node_itr = node; // replace me in my parent's children list with the newly inserted node - node->parent_ = parent; // set the newly inserted node's parent ptr to my parent - node->children_.push_back(shared_from_this()); // add myself to the newly inserted node's children list - parent_ = node.get(); // set my parent ptr to the newly inserted node - - return Status::OK(); -} - -/* - * Drop() detaches this node from the tree it is in. Calling Drop() from a standalone node is a no-op. - * - * Input tree: - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds4 ds1 - * | / \ - * ds7 ds3 ds2 - * - * Case 1: When the node has no child and no sibling, Drop() detaches the node from its tree. - * - * ds7->Drop() yields the tree below: - * - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds4 ds1 - * / \ - * ds3 ds2 - * - * Case 2: When the node has one child and no sibling, Drop() detaches the node from its tree and the node's child - * becomes its parent's child. - * - * ds8->Drop() yields the tree below: - * - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds7 ds5 ds4 ds1 - * / \ - * ds3 ds2 - * - * Case 3: When the node has more than one child and no sibling, Drop() detaches the node from its tree and the node's - * children become its parent's children. - * - * When the input tree is - * - * ds10 - * / \ - * ds9 ds6 - * | | - * ds8 ds4 - * | / \ - * ds7 ds3 ds2 - * - * ds4->Drop() yields the tree below: - * - * ds10 - * / \ - * ds9 ds6 - * | / \ - * ds8 ds3 ds2 - * | - * ds7 - * - * But if ds6 is not an n-ary operator, ds4->Drop() will raise an error because we cannot add the children of an - * n-ary operator (ds4) to a unary operator (ds6). - * - * Case 4: When the node has no child but has siblings, Drop() detaches the node from its tree and its siblings will be - * squeezed left. - * - * Input tree: - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds4 ds1 - * | / \ - * ds7 ds3 ds2 - * - * ds5->Drop() yields the tree below: - * - * ds10 - * / \ - * ds9 ds6 - * | / \ - * ds8 ds4 ds1 - * | / \ - * ds7 ds3 ds2 - * - * Case 5: When the node has only one child but has siblings, Drop() detaches the node from its tree and the node's - * children become its parent's children. - * - * Input tree: - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds4 ds1 - * | | - * ds7 ds3 - * - * ds4->Drop() yields the tree below: - * - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds3 ds1 - * | - * ds7 - * - * Case 6: When the node has more than one child and more than one sibling, Drop() will raise an error. - * If we want to drop ds4 from the input tree, ds4->Drop() will not work. We will have to do it - * with a combination of Drop(), InsertChildAt() - * - * Input tree: - * ds10 - * / \ - * ds9 ds6 - * | / | \ - * ds8 ds5 ds4 ds1 - * | / \ - * ds7 ds3 ds2 - * - * If we want to form this tree below: - * - * ds10 - * / \ - * ds9 ds6_____ - * | / | | \ - * ds8 ds5 ds3 ds2 ds1 - * | - * ds7 - * - */ -Status DatasetNode::Drop() { - CHECK_FAIL_RETURN_UNEXPECTED(parent_ != nullptr, "This node to drop must not be the root or a node without parent."); - CHECK_FAIL_RETURN_UNEXPECTED(!(IsNaryOperator() && parent_->IsUnaryOperator()), - "Trying to drop an n-ary operator that is a child of a unary operator"); - CHECK_FAIL_RETURN_UNEXPECTED(!(children_.size() > 1 && parent_->children_.size() > 1), - "This node to drop must not have more than one child and more than one sibling."); - if (parent_->children_.size() == 1) { - auto my_parent = parent_; - // Case 2: When the node has one child and no sibling, Drop() detaches the node from its tree and the node's child - // becomes its parent's child. - // This is the most common use case. - if (children_.size() == 1) { - auto child = children_[0]; - // Move its child to be its parent's child - my_parent->children_[0] = child; - child->parent_ = my_parent; - } else if (children_.empty()) { - // Case 1: When the node has no child and no sibling, Drop() detaches the node from its tree. - // Remove this node from its parent's child - parent_->children_.clear(); - } else if (children_.size() > 1) { - // Case 3: When the node has more than one child and no sibling, Drop() detaches the node from its tree and - // the node's children become its parent's children. - // Remove this node from its parent's child - my_parent->children_.clear(); - // Move its child to be its parent's child - for (auto &my_child : children_) { - my_parent->children_.push_back(my_child); - my_child->parent_ = my_parent; - } - } - // And mark itself as an orphan - parent_ = nullptr; - children_.clear(); - } else if (children_.empty() && parent_->children_.size() > 1) { - // Case 4: When the node has no child but has siblings, Drop() detaches the node from its tree and its siblings will - // be squeezed left. - auto parent = parent_; - // Remove this node from its parent's child - parent->children_.erase(std::remove(parent->children_.begin(), parent->children_.end(), shared_from_this()), - parent->children_.end()); // removal using "erase remove idiom" - // And mark itself as an orphan - parent_ = nullptr; - children_.clear(); - } else if (children_.size() == 1 && parent_->children_.size() > 1) { - // Case 5: When the node has only one child but has siblings, Drop() detaches the node from its tree and the node's - // children become its parent's children. - auto itr = std::find(parent_->children_.begin(), parent_->children_.end(), shared_from_this()); - CHECK_FAIL_RETURN_UNEXPECTED(itr != parent_->children_.end(), "I am not in my parent's children list."); - *itr = children_[0]; // replace this node in its parent's children list with its single child - children_[0]->parent_ = parent_; // set its single child's parent ptr to its parent - // And mark itself as an orphan - parent_ = nullptr; - children_.clear(); - } else { - RETURN_STATUS_UNEXPECTED("Internal error: we should not reach here."); - } - return Status::OK(); -} - -// In DFS tree traversal, each node is visited twice. Accept is called on the first visit. -Status DatasetNode::Accept(IRNodePass *const p, bool *const modified) { - // This method will only be called if its derived class does not implement one. - return p->Visit(shared_from_this(), modified); -} - -// In DFS tree traversal, each node is visited twice. AcceptAfter is called on the second visit -// after all child nodes are visited. -Status DatasetNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // This method will only be called if its derived class does not implement one. - return p->VisitAfter(shared_from_this(), modified); -} - -Status DatasetNode::GetShardId(int32_t *const shard_id) { - if (children_.size() == 1) { - // Get shard id from the child node - return children_[0]->GetShardId(shard_id); - } else if (children_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetShardId shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in front of the child_ structure, so we get the dataset size from the last child. - return children_.back()->GetShardId(shard_id); - } else { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Get Shard Id failed at source node: " + Name() + "\n"); - } -} - -// Gets the dataset size -Status DatasetNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - if (!IsSizeDefined() && size_getter != nullptr) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), dataset_size)); - dataset_size_ = *dataset_size; - return Status::OK(); - } - if (children_.size() == 1) { - return children_.front()->GetDatasetSize(size_getter, estimate, dataset_size); - } else if (children_.size() > 1) { - // It is okay for dataset to have more than 1 child, GetDatasetSize shouldn't fail in this case. - // This is done mostly for cache, which injects cache lookup/merge operators. Cache path will - // always be in front of the child_ structure, so we get the dataset size from the last child. - return children_.back()->GetDatasetSize(size_getter, estimate, dataset_size); - } else { - RETURN_STATUS_UNEXPECTED("Trying to get dataset size from leaf node, missing override"); - } -} -Status DatasetNode::ValidateParams() { - int32_t num_threads = GlobalContext::config_manager()->num_cpu_threads(); - // in case std::thread::hardware_concurrency returns 0, use an artificial upper limit - num_threads = num_threads > 0 ? num_threads : std::numeric_limits::max(); - CHECK_FAIL_RETURN_UNEXPECTED( - num_workers_ > 0 && num_workers_ <= num_threads, - Name() + "'s num_workers=" + std::to_string(num_workers_) + - ", this value is not within the required range of [1, cpu_thread_cnt=" + std::to_string(num_threads) + "]."); - return Status::OK(); -} - -Status DatasetNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - *out_json = args; - return Status::OK(); -} - -Status MappableSourceNode::Accept(IRNodePass *const p, bool *const modified) { - return p->Visit(shared_from_base(), modified); -} - -Status NonMappableSourceNode::Accept(IRNodePass *const p, bool *const modified) { - return p->Visit(shared_from_base(), modified); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h deleted file mode 100644 index 7df414ef7..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h +++ /dev/null @@ -1,478 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_DATASET_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_DATASET_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/project_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/take_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { - -class Dataset; -class DatasetCache; -class SamplerObj; -class IRNodePass; -class DatasetSizeGetter; - -// Names for non-leaf IR node -constexpr char kBatchNode[] = "Batch"; -constexpr char kBucketBatchByLengthNode[] = "BucketBatchByLength"; -constexpr char kBuildSentencePieceVocabNode[] = "BuildSentencePieceVocab"; -constexpr char kBuildVocabNode[] = "BuildVocab"; -constexpr char kCacheLookupNode[] = "CacheLookup"; -constexpr char kCacheMergeNode[] = "CacheMerge"; -constexpr char kCacheNode[] = "Cache"; -constexpr char kConcatNode[] = "Concat"; -constexpr char kEpochCtrlNode[] = "EpochCtrl"; -constexpr char kFilterNode[] = "Filter"; -constexpr char kMapNode[] = "Map"; -constexpr char kProjectNode[] = "Project"; -constexpr char kRenameNode[] = "Rename"; -constexpr char kRepeatNode[] = "Repeat"; -constexpr char kRootNode[] = "Top"; -constexpr char kShuffleNode[] = "Shuffle"; -constexpr char kSkipNode[] = "Skip"; -constexpr char kSyncWaitNode[] = "SyncWait"; -constexpr char kTakeNode[] = "Take"; -constexpr char kTransferNode[] = "Transfer"; -constexpr char kZipNode[] = "Zip"; - -// Names for leaf IR node -constexpr char kAGNewsNode[] = "AGNewsDataset"; -constexpr char kAlbumNode[] = "AlbumDataset"; -constexpr char kAmazonReviewNode[] = "AmazonReviewDataset"; -constexpr char kCaltech256Node[] = "Caltech256Dataset"; -constexpr char kCelebANode[] = "CelebADataset"; -constexpr char kCifar100Node[] = "Cifar100Dataset"; -constexpr char kCifar10Node[] = "Cifar10Dataset"; -constexpr char kCityscapesNode[] = "CityscapesDataset"; -constexpr char kCLUENode[] = "CLUEDataset"; -constexpr char kCMUArcticNode[] = "CMUArcticDataset"; -constexpr char kCocoNode[] = "CocoDataset"; -constexpr char kCoNLL2000Node[] = "CoNLL2000Dataset"; -constexpr char kCSVNode[] = "CSVDataset"; -constexpr char kDBpediaNode[] = "DBpediaDataset"; -constexpr char kDIV2KNode[] = "DIV2KDataset"; -constexpr char kEMnistNode[] = "EMnistDataset"; -constexpr char kEnWik9Node[] = "EnWik9Dataset"; -constexpr char kFakeImageNode[] = "FakeImageDataset"; -constexpr char kFashionMnistNode[] = "FashionMnistDataset"; -constexpr char kFlickrNode[] = "FlickrDataset"; -constexpr char kFood101Node[] = "Food101Dataset"; -constexpr char kGeneratorNode[] = "GeneratorDataset"; -constexpr char kGTZANNode[] = "GTZANDataset"; -constexpr char kImageFolderNode[] = "ImageFolderDataset"; -constexpr char kIMDBNode[] = "IMDBDataset"; -constexpr char kIWSLT2016Node[] = "IWSLT2016Dataset"; -constexpr char kIWSLT2017Node[] = "IWSLT2017Dataset"; -constexpr char kKITTINode[] = "KITTIDataset"; -constexpr char kKMnistNode[] = "KMnistDataset"; -constexpr char kLFWNode[] = "LFWDataset"; -constexpr char kLibriTTSNode[] = "LibriTTSDataset"; -constexpr char kLJSpeechNode[] = "LJSpeechDataset"; -constexpr char kLSUNNode[] = "LSUNDataset"; -constexpr char kManifestNode[] = "ManifestDataset"; -constexpr char kMindDataNode[] = "MindDataDataset"; -constexpr char kMnistNode[] = "MnistDataset"; -constexpr char kMulti30kNode[] = "Multi30kDataset"; -constexpr char kOmniglotNode[] = "OmniglotDataset"; -constexpr char kPennTreebankNode[] = "PennTreebankDataset"; -constexpr char kPhotoTourNode[] = "PhotoTourDataset"; -constexpr char kPlaces365Node[] = "Places365Dataset"; -constexpr char kQMnistNode[] = "QMnistDataset"; -constexpr char kRandomNode[] = "RandomDataset"; -constexpr char kRenderedSST2Node[] = "RenderedSST2Dataset"; -constexpr char kSBUNode[] = "SBUDataset"; -constexpr char kSemeionNode[] = "SemeionDataset"; -constexpr char kSogouNewsNode[] = "SogouNewsDataset"; -constexpr char kSpeechCommandsNode[] = "SpeechCommandsDataset"; -constexpr char kSQuADNode[] = "SQuADDataset"; -constexpr char kSST2Node[] = "SST2Dataset"; -constexpr char kSTL10Node[] = "STL10Dataset"; -constexpr char kSUN397Node[] = "SUN397Dataset"; -constexpr char kTedliumNode[] = "TedliumDataset"; -constexpr char kTextFileNode[] = "TextFileDataset"; -constexpr char kTFRecordNode[] = "TFRecordDataset"; -constexpr char kUDPOSNode[] = "UDPOSDataset"; -constexpr char kUSPSNode[] = "USPSDataset"; -constexpr char kVOCNode[] = "VOCDataset"; -constexpr char kWIDERFaceNode[] = "WIDERFaceDataset"; -constexpr char kWikiTextNode[] = "WikiTextDataset"; -constexpr char kYahooAnswersNode[] = "YahooAnswersDataset"; -constexpr char kYelpReviewNode[] = "YelpReviewDataset"; -constexpr char kYesNoNode[] = "YesNoDataset"; - -Status AddShuffleOp(int64_t num_files, int64_t num_devices, int64_t num_rows, int64_t total_rows, - int32_t connector_que_size, std::shared_ptr *shuffle_op); - -// Helper function to validate dataset files parameter -Status ValidateDatasetFilesParam(const std::string &dataset_name, const std::vector &dataset_files, - const std::string &file_name = "dataset file"); - -// Helper function to validate dataset num_shards and shard_id parameters -Status ValidateDatasetShardParams(const std::string &dataset_name, int32_t num_shards, int32_t shard_id); - -// Helper function to validate dataset sampler parameter -Status ValidateDatasetSampler(const std::string &dataset_name, const std::shared_ptr &sampler); - -Status ValidateStringValue(const std::string &dataset_name, const std::string &str, - const std::unordered_set &valid_strings); - -// Helper function to validate dataset input/output column parameterCD - -Status ValidateDatasetColumnParam(const std::string &dataset_name, const std::string &column_param, - const std::vector &columns); - -// Helper function to validate dataset directory parameter -Status ValidateDatasetDirParam(const std::string &dataset_name, std::string dataset_dir); - -Status ValidateMapKey(const std::string &dataset_name, const std::string &key, - const std::map> &map); - -Status ValidateMapValue(const std::string &dataset_name, const std::string &str, - const std::vector &valid_strings); - -/// \brief Function to create a sampler for non-mappable dataset (to be used by cache op later). -/// \notes Non-mappable dataset does not directly support a sampler. It has provided sampling arguments (shuffle, -/// num_samples, num_shards, shard_id) and it DOES support sampling if somewhere above it in the pipeline contains -/// a cache. If there is no cache above it, then the sampler is not used. -/// \param[in] num_samples The number of samples to be included in the dataset. -/// \param[in] shuffle If it is not kFalse, the indices are shuffled. -/// \param[in] num_shards Number of shards to divide the dataset into. -/// \param[in] shard_id Shard ID of the current shard within num_shards. -/// \return Shared pointer to the current Sampler. -std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, int32_t num_shards, - int32_t shard_id); - -// The base class of all IR nodes -class DatasetNode : public std::enable_shared_from_this { - // Allow DeepCopyPass to access internal members - friend class DeepCopyPass; - - public: - /// \brief Constructor - DatasetNode(); - - /// \brief Constructor that initializes the cache - /// \param dataset_cache DatasetCache - explicit DatasetNode(const std::shared_ptr &dataset_cache); - - /// \brief Destructor - virtual ~DatasetNode() = default; - - /// \brief Node name getter - /// \return Name of the current node - virtual std::string Name() const = 0; - - /// \brief Pure virtual function to print the description - /// \param out - The output stream to write output to - virtual void Print(std::ostream &out) const = 0; - - /// \brief Pure virtual function to clone a new copy of the node - /// \return The new copy of the node - virtual std::shared_ptr Copy() = 0; - - /// \brief Print the IR tree to output stream - /// \param out - The output stream to write output to - void PrintTree(std::ostream &out) const; - - /// \brief << Stream output operator overload - /// \notes This allows you to write the debug print info using stream operators - /// \param out - reference to the output stream being overloaded - /// \param node - reference to the DatasetNode to display - /// \return - the output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const DatasetNode &node) { - node.PrintTree(out); - return out; - } - - /// \brief Pure virtual function to convert a DatasetNode class into a runtime dataset object - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - virtual Status Build(std::vector> *const node_ops) = 0; - - /// \brief base virtual function for derived class to implement parameters validation - /// \return Status Status::OK() if all the parameters are valid - virtual Status ValidateParams(); - - /// \brief Pure virtual function for derived class to get the shard id of specific node - /// \return Status Status::OK() if get shard id successfully - virtual Status GetShardId(int32_t *const shard_id); - - /// \brief Gets the dataset size - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \return Status - The status code return - virtual Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size); - - /// \brief Getter function for child nodes - /// \return Child nodes - const std::vector> Children() const { return children_; } - - /// \brief Get the parent dataset node. - /// \return The parent dataset node. - DatasetNode *Parent() const { return parent_; } - - /// \brief Establish a parent-child relationship between this node and the input node. - /// Used during the cloning of the user-input IR tree (temporary use) - Status AppendChild(std::shared_ptr child); - - /// \brief Insert the input above this node - Status InsertAbove(std::shared_ptr node); - - /// \brief Add the input node as the next sibling (future use) - Status InsertChildAt(int32_t pos, std::shared_ptr node); - - /// \brief detach this node from its parent, add its child (if any) to its parent - /// \return error code, return error if node has more than 1 children - Status Drop(); - - /// \brief Check if this node has cache - /// \return True if the data of this node will be cached - const bool IsCached() const { return (cache_ != nullptr); } - - /// \brief Check if this node is a leaf node. - /// \return True if this is a leaf node. - const bool IsLeaf() const { return children_.empty(); } - - /// \brief Check if this node is a unary operator node. - /// \return True if this node is semantically a unary operator node - const bool IsUnaryOperator() const { return (mappable_ == kNotADataSource && !nary_op_); } - - /// \brief Check if this node is a n-ary operator node. - /// \return True if this node is semantically a n-ary operator node - const bool IsNaryOperator() const { return (mappable_ == kNotADataSource && nary_op_); } - - /// \brief Check if this node is a mappable dataset. Only applicable to leaf nodes - /// \return True if this node is a mappable dataset - const bool IsMappableDataSource() const { return (mappable_ == kMappableSource); } - - /// \brief Check if this node is a non-mappable dataset. Only applicable to leaf nodes - /// \return True if this node is a non-mappable dataset - const bool IsNonMappableDataSource() const { return (mappable_ == kNonMappableSource); } - - /// \brief Check if this node is a data source node. - /// \return True if this node is a data source node - const bool IsDataSource() const { return (mappable_ == kMappableSource || mappable_ == kNonMappableSource); } - - /// \brief Check if this node is not a data source node. - /// \return True if this node is not a data source node - const bool IsNotADataSource() const { return (mappable_ == kNotADataSource); } - - /// \brief Check if this node is a descendant of an operator with cache. - /// \return True if a cache-enabled operator is an ancestor of this node - const bool IsDescendantOfCache() const { return descendant_of_cache_; } - - /// \brief Check if this node is an orphan node - /// \return True if this node isn't nullptr nor does it have any children and a parent - static bool IsOrphanNode(const std::shared_ptr &node) { - return node != nullptr && node->parent_ == nullptr && node->Children().empty(); - } - - /// \brief Mark to indicate this node is a descendant of an operator with cache. - void HasCacheAbove() { descendant_of_cache_ = true; } - - /// \brief Getter of the number of workers - int32_t NumWorkers() const { return num_workers_; } - - /// \brief Getter of the connector queue size - int32_t ConnectorQueueSize() const { return connector_que_size_; } - - /// \brief Getter of dataset cache - std::shared_ptr GetDatasetCache() { return cache_; } - - /// \brief Setter function for runtime number of workers - /// \param[in] num_workers The number of threads in this operator - /// \return Shared pointer to the original object - std::shared_ptr SetNumWorkers(int32_t num_workers); - - std::shared_ptr SetConnectorQueueSize(int32_t connector_queue_size); - - /// \brief Setter function for DatasetCache - /// \param[in] cache Shared pointer to DatasetCache - /// \return Shared pointer to the original object - std::shared_ptr SetDatasetCache(const std::shared_ptr &cache); - - /// \brief Setter function for descendant_of_cache_ - /// \param[in] descendant_of_cache Indicator for whether this node is a descendant of cache. - void setDescendantOfCache(bool descendant_of_cache) { descendant_of_cache_ = descendant_of_cache; } - - /// \brief A helper templated function for casting "this" pointer to shared_ptr - /// Similar to shared_from_this, except this one will give you the derived class as shared_ptr - /// \return A shared_ptr casted to the derived class - template - std::shared_ptr shared_from_base() { - return std::static_pointer_cast(shared_from_this()); - } - - /// \brief Base method for IRNodePass visit. A tree walk consists of walking down the tree and also walking back up - /// in a depth-first order. Accept is the node visit on the way down, whereas AcceptAfter is the node - /// visit on the way back up the tree after its descendants are visited. - /// \notes Subclass needs to override this if it requires special node visit access. - /// Check "dataset/engine/opt/pass.h" for more details. - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - virtual Status Accept(IRNodePass *const p, bool *const modified); - - /// \brief Base method for IRNodePass visit on the way back up the tree after its descendants are visited. - /// \notes Subclass needs to override this if it requires special node visit access. - /// Check "dataset/engine/opt/pass.h" for more details. - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - virtual Status AcceptAfter(IRNodePass *const p, bool *const modified); - - virtual bool IsSizeDefined() { return true; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - virtual Status to_json(nlohmann::json *out_json); - - /// \brief Setter function, set the number of total repeats for the operator - void SetTotalRepeats(int32_t total_repeats) { total_repeats_ = total_repeats; } - - /// \brief Setter function, set the number of epochs for the operator - virtual void SetNumEpochs(int32_t num_epochs) { num_epochs_ = num_epochs; } - - /// \brief Getter function - /// \return The number of required repeats for the operator - int32_t GetTotalRepeats() const { return total_repeats_; } - - /// \brief Getter function - /// \return The number of epochs for the operator - int32_t GetNumEpochs() const { return num_epochs_; } - - /// \brief Getter function - /// \return The number of repeats per epoch for the operator - int32_t GetNumRepeatsPerEpoch() const { return total_repeats_ / num_epochs_; } - - protected: - std::vector> children_; - DatasetNode *parent_; // used to record the only one parent of an IR node after parsing phase - std::shared_ptr cache_; - int64_t dataset_size_; - int32_t num_workers_; - int32_t connector_que_size_; - int32_t worker_connector_size_; - int32_t total_repeats_; // Number of times required to run this operator - int32_t num_epochs_; // Number of epochs - // Establish a parent-child relationship between this node and the input node. - // Used only in the constructor of the class and its derived classes. - void AddChild(std::shared_ptr child); - std::string PrintColumns(const std::vector &columns) const; - void PrintNode(std::ostream &out, int *level) const; - enum DataSource { kNotADataSource = 0, kNonMappableSource = 1, kMappableSource = 2 }; - enum DataSource mappable_; - bool nary_op_; // an indicator of whether the current node supports multiple children, true for concat/zip node - bool descendant_of_cache_; // an indicator of whether the current node is a descendant of cache. - // Initially set to false, will set to true by the optimizer when conditions are met. -}; - -// MappableSourceNode represents the leaf nodes that can be randomly accessed with indexes. -class MappableSourceNode : public DatasetNode { - public: - /// \brief Constructor - MappableSourceNode() : DatasetNode() { mappable_ = kMappableSource; } - - /// \brief Constructor that initializes the cache - /// \param dataset_cache DatasetCache - explicit MappableSourceNode(const std::shared_ptr &dataset_cache) : DatasetNode(dataset_cache) { - mappable_ = kMappableSource; - // Initially set to false, and set to true by the optimizer when conditions are met. - descendant_of_cache_ = false; - } - - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Destructor - virtual ~MappableSourceNode() = default; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - virtual std::shared_ptr Sampler() = 0; - - /// \brief Sampler setter - virtual void SetSampler(std::shared_ptr sampler) = 0; -}; - -// NonMappableSourceNode represents the leaf nodes that can not be randomly accessed. -class NonMappableSourceNode : public DatasetNode { - public: - /// \brief Constructor - NonMappableSourceNode() : DatasetNode() { mappable_ = kNonMappableSource; } - - /// \brief Constructor that initializes the cache - /// \param dataset_cache DatasetCache - explicit NonMappableSourceNode(const std::shared_ptr &dataset_cache) : DatasetNode(dataset_cache) { - mappable_ = kNonMappableSource; - // Initially set to false, and set to true by the optimizer when conditions are met. - descendant_of_cache_ = false; - } - - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Destructor - virtual ~NonMappableSourceNode() = default; - - /// \brief By default non-mappable dataset does not support sampling. However, if a cache operator - /// is injected at some other place higher in the tree, that cache can inherit this sampler - /// from the leaf, providing sampling support from the caching layer. - /// This function sets up the sampler for a leaf node that does not use sampling. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - virtual Status SetupSamplerForCache(std::shared_ptr *sampler) = 0; - - /// \brief If a cache has been added into the ascendant tree over this non-mappable source node, then the cache will - /// be executing a sampler for fetching the data. As such, any options in the source node need to be reset to its - /// defaults so that this source node will produce the full set of data into the cache. - /// \return Status of the function - virtual Status MakeSimpleProducer() = 0; - - void SetSkipSteps(int64_t skip_steps) { skip_steps_ = skip_steps; } - - protected: - int64_t skip_steps_ = 0; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_DATASET_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc deleted file mode 100644 index 89ae86e1e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for EpochCtrlNode -EpochCtrlNode::EpochCtrlNode(std::shared_ptr child, int32_t num_epochs) : RepeatNode() { - // The root node's parent must set to null pointer. - this->AddChild(child); - repeat_count_ = num_epochs; -} - -std::shared_ptr EpochCtrlNode::Copy() { - auto node = std::make_shared(repeat_count_); - return node; -} - -void EpochCtrlNode::Print(std::ostream &out) const { - out << (Name() + "(epoch:" + std::to_string(repeat_count_) + ")"); -} - -// Function to build the EpochCtrlOp -Status EpochCtrlNode::Build(std::vector> *const node_ops) { - auto new_op_ = std::make_shared(repeat_count_); - new_op_->SetTotalRepeats(GetTotalRepeats()); - new_op_->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(new_op_); - op_ = new_op_; - return Status::OK(); -} - -// Function to validate the parameters for EpochCtrlNode -Status EpochCtrlNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (repeat_count_ <= 0 && repeat_count_ != -1) { - std::string err_msg = - "EpochCtrlNode: num_epochs should be either -1 or positive integer, num_epochs: " + std::to_string(repeat_count_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (children_.size() != 1 || children_[0] == nullptr) { - std::string err_msg = "Internal error: epoch control node should have one child node"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status EpochCtrlNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status EpochCtrlNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h deleted file mode 100644 index e28b5ab02..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_EPOCH_CTRL_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_EPOCH_CTRL_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/epoch_ctrl_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" - -namespace mindspore { -namespace dataset { -class EpochCtrlNode : public RepeatNode { - // Allow GeneratorNode to access internal members - friend class GeneratorNode; - - public: - /// \brief Constructor - explicit EpochCtrlNode(int32_t num_epochs) : RepeatNode() { repeat_count_ = num_epochs; } - - /// \brief Constructor - EpochCtrlNode(std::shared_ptr child, int32_t num_epochs); - - /// \brief Destructor - ~EpochCtrlNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kEpochCtrlNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_EPOCH_CTRL_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.cc deleted file mode 100644 index 2e7a6e61e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.cc +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/filter_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for FilterNode -FilterNode::FilterNode(std::shared_ptr child, std::shared_ptr predicate, - std::vector input_columns) - : predicate_(predicate), input_columns_(input_columns) { - this->AddChild(child); -} - -std::shared_ptr FilterNode::Copy() { - auto node = std::make_shared(nullptr, predicate_, input_columns_); - return node; -} - -void FilterNode::Print(std::ostream &out) const { - out << (Name() + "(," + "input_cols:" + PrintColumns(input_columns_) + ")"); -} - -Status FilterNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(input_columns_, num_workers_, connector_que_size_, predicate_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -Status FilterNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (predicate_ == nullptr) { - std::string err_msg = "FilterNode: predicate is not specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (!input_columns_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("FilterNode", "input_columns", input_columns_)); - } - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status FilterNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status FilterNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status FilterNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["input_columns"] = input_columns_; - args["num_parallel_workers"] = num_workers_; - args["predicate"] = "pyfunc"; - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h deleted file mode 100644 index ae2b3e427..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_FILTER_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_FILTER_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class FilterNode : public DatasetNode { - public: - /// \brief Constructor - FilterNode(std::shared_ptr child, std::shared_ptr predicate, - std::vector input_columns = {}); - - /// \brief Destructor - ~FilterNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kFilterNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - bool IsSizeDefined() override { return false; }; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - const std::shared_ptr &Predicate() const { return predicate_; } - const std::vector &InputColumns() const { return input_columns_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - private: - std::shared_ptr predicate_; - std::vector input_columns_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_FILTER_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.cc deleted file mode 100644 index 5767f9241..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.cc +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" - -#include -#include -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -MapNode::MapNode(std::shared_ptr child, std::vector> operations, - std::vector input_columns, std::vector output_columns, - const std::shared_ptr &cache, std::vector> callbacks, - ManualOffloadMode offload, - std::shared_ptr python_multiprocessing_runtime) - : operations_(std::move(operations)), - input_columns_(std::move(input_columns)), - output_columns_(std::move(output_columns)), - DatasetNode(cache), - callbacks_(std::move(callbacks)), - offload_(offload), - python_multiprocessing_runtime_(std::move(python_multiprocessing_runtime)) { - this->AddChild(std::move(child)); -} - -MapNode::MapNode(std::vector> operations, std::vector input_columns, - std::vector output_columns) - : operations_(std::move(operations)), - input_columns_(std::move(input_columns)), - output_columns_(std::move(output_columns)), - DatasetNode(nullptr), - callbacks_({}), - offload_(ManualOffloadMode::kUnspecified), - python_multiprocessing_runtime_(nullptr) {} - -std::shared_ptr MapNode::Copy() { - std::vector> operations = operations_; - auto node = std::make_shared(nullptr, operations, input_columns_, output_columns_, cache_, callbacks_, - offload_, python_multiprocessing_runtime_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void MapNode::Print(std::ostream &out) const { - out << (Name() + "(" + ",input:" + PrintColumns(input_columns_) + ",output:" + PrintColumns(output_columns_) + - ",num_tensor_ops:") - << operations_.size() << ",...)"; -} - -Status MapNode::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - std::vector> tensor_ops; - - // Build tensorOp from tensorOperation vector - // This is to ensure each iterator hold its own copy of the tensorOp objects. - (void)std::transform( - operations_.begin(), operations_.end(), std::back_inserter(tensor_ops), - [](std::shared_ptr operation) -> std::shared_ptr { return operation->Build(); }); - - // This is temporary code. - // Because the randomness of its tensor operations is not known in TensorOperation form until we convert them - // to TensorOp, we need to check the randomness here. - // When TensorOperation captures the randomness behavior, remove this code - // and the temporary code in CacheValidation pre pass in IR optimizer. - if (IsDescendantOfCache()) { - auto itr = std::find_if(tensor_ops.begin(), tensor_ops.end(), [](const auto &it) { return !it->Deterministic(); }); - if (itr != tensor_ops.end()) { - RETURN_STATUS_UNEXPECTED("MapNode containing random operation is not supported as a descendant of cache."); - } - } - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // whether mixing DVPP operators with CPU operators - bool dvpp_op_flag = false; - bool cpu_op_flag = false; - std::string op_names = ""; - for (const auto &op : operations_) { - if (op->Type() == MapTargetDevice::kAscend910B) { - dvpp_op_flag = true; - } else { - cpu_op_flag = true; - } - op_names += op->Name() + ", "; - } - - if (dvpp_op_flag == true && cpu_op_flag == true) { - MS_LOG(WARNING) << "The dvpp operator(s) and cpu operator(s) are used together which may lead to low efficiency. " - << "The operator(s) are: " << op_names - << "you are advised to use just one or more dvpp operators in the same map operation " - << "to achieve higher efficiency."; - } -#endif - - auto map_op = - std::make_shared(input_columns_, output_columns_, operations_, num_workers_, connector_que_size_); - - if (!callbacks_.empty()) { - map_op->AddCallbacks(callbacks_); - } - - map_op->SetTotalRepeats(GetTotalRepeats()); - map_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - if (python_multiprocessing_runtime_ != nullptr) { - map_op->SetPythonMp(python_multiprocessing_runtime_); - } - node_ops->push_back(map_op); - return Status::OK(); -} - -Status MapNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (operations_.empty()) { - std::string err_msg = "Map: No 'operations' are specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (const auto &op : operations_) { - if (op == nullptr) { - std::string err_msg = "Map: 'operations' must not be nullptr."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } else { - RETURN_IF_NOT_OK(op->ValidateParams()); - } - } - if (!input_columns_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("Map", "input_columns", input_columns_)); - } - - if (!output_columns_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("Map", "output_columns", output_columns_)); - } - - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status MapNode::Accept(IRNodePass *const p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status MapNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -void MapNode::setOperations(const std::vector> &operations) { - operations_ = operations; -} -std::vector> MapNode::operations() { return operations_; } - -void MapNode::SetOffload(ManualOffloadMode offload) { offload_ = offload; } - -Status MapNode::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["input_columns"] = input_columns_; - args["output_columns"] = output_columns_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - std::vector ops; - std::vector cbs; - for (auto op : operations_) { - RETURN_UNEXPECTED_IF_NULL(op); - nlohmann::json op_args; - RETURN_IF_NOT_OK(op->to_json(&op_args)); - if (op->Name() == "PyFuncOp") { - ops.push_back(op_args); - } else { - nlohmann::json op_item; - op_item["tensor_op_params"] = op_args; - op_item["tensor_op_name"] = op->Name(); - ops.push_back(op_item); - } - } - args["operations"] = ops; - (void)std::transform(callbacks_.begin(), callbacks_.end(), std::back_inserter(cbs), - [](std::shared_ptr cb) -> int32_t { return cb != nullptr ? cb->step_size() : 0; }); - args["callback"] = cbs; - - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status MapNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kMapNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kMapNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "input_columns", kMapNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "output_columns", kMapNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "operations", kMapNode)); - std::vector input_columns = json_obj["input_columns"]; - std::vector output_columns = json_obj["output_columns"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(json_obj["operations"], &operations)); - *result = std::make_shared(ds, operations, input_columns, output_columns); - (void)(*result)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*result)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif - -// Gets the dataset size -Status MapNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - // If cache is injected after a MapNode, it is possible that the pipeline needs to handle different numbers of rows - // compared to a non-cached pipeline. This is mostly true for TFRecord dataset, since it uses row-based sharding - // with cache but file-based sharding without cache. However, MapNode couldn't tell whether the leaf below is - // TFRecord or not, therefore it doesn't rely on its child here but simply run through the tree. - if (!IsSizeDefined() || IsCached()) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), dataset_size)); - dataset_size_ = *dataset_size; - return Status::OK(); - } - if (children_.size() == 1) { - return children_.front()->GetDatasetSize(size_getter, estimate, dataset_size); - } else { - RETURN_STATUS_UNEXPECTED("Trying to get dataset size from leaf node, missing override"); - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h deleted file mode 100644 index e798694ed..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_MAP_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_MAP_NODE_H_ - -#include -#include -#include - -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class MapNode : public DatasetNode { - public: - /// \brief Constructor - MapNode(std::shared_ptr child, std::vector> operations, - std::vector input_columns = {}, std::vector output_columns = {}, - const std::shared_ptr &cache = nullptr, std::vector> callbacks = {}, - ManualOffloadMode offload = ManualOffloadMode::kUnspecified, - std::shared_ptr python_multiprocessing_runtime = nullptr); - - /// \brief Constructor used in InsertMap pass. - MapNode(std::vector> operations, std::vector input_columns, - std::vector output_columns); - - /// \brief Destructor - ~MapNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kMapNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief clear all callbacks - void ClearCallbacks() { callbacks_.clear(); } - - /// \brief getter to get all tensor operations - std::vector> operations(); - - /// \brief setter to set all tensor operations - void setOperations(const std::vector> &operations); - - /// \brief Getter functions - /// \brief Getter of tensor operations - /// \return Vector of operations the Map node will process - const auto &TensorOperations() const { return operations_; } - const std::vector &InputColumns() const { return input_columns_; } - const std::vector &OutputColumns() const { return output_columns_; } - const std::vector> &Callbacks() const { return callbacks_; } - ManualOffloadMode GetOffload() const { return offload_; } - - /// \brief setter to set offload flag of node - void SetOffload(ManualOffloadMode offload); - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); -#endif - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - private: - std::vector> operations_; - std::vector input_columns_; - std::vector output_columns_; - std::vector> callbacks_; - - /// \brief ManualOffloadMode to indicate manual_offload status - ManualOffloadMode offload_; - - std::shared_ptr python_multiprocessing_runtime_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_MAP_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.cc deleted file mode 100644 index dd68ff869..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.cc +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/project_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Function to build ProjectOp -ProjectNode::ProjectNode(std::shared_ptr child, const std::vector &columns) - : columns_(columns) { - this->AddChild(child); -} - -std::shared_ptr ProjectNode::Copy() { - auto node = std::make_shared(nullptr, this->columns_); - return node; -} - -void ProjectNode::Print(std::ostream &out) const { out << (Name() + "(column: " + PrintColumns(columns_) + ")"); } - -Status ProjectNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (columns_.empty()) { - std::string err_msg = "Project: No 'columns' are specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("Project", "columns", columns_)); - - return Status::OK(); -} - -Status ProjectNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(columns_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -Status ProjectNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["columns"] = columns_; - *out_json = args; - return Status::OK(); -} - -Status ProjectNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "columns", kProjectNode)); - std::vector columns = json_obj["columns"]; - *result = std::make_shared(ds, columns); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status ProjectNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status ProjectNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h deleted file mode 100644 index 837da38a8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_PROJECT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_PROJECT_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class ProjectNode : public DatasetNode { - public: - /// \brief Constructor - explicit ProjectNode(std::shared_ptr child, const std::vector &columns); - - /// \brief Destructor - ~ProjectNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kProjectNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter functions - const std::vector &Columns() const { return columns_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - std::vector columns_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_PROJECT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.cc deleted file mode 100644 index a9a78e3f0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.cc +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/rename_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// Function to build RenameOp -RenameNode::RenameNode(std::shared_ptr child, const std::vector &input_columns, - const std::vector &output_columns) - : input_columns_(input_columns), output_columns_(output_columns) { - this->AddChild(child); -} - -std::shared_ptr RenameNode::Copy() { - auto node = std::make_shared(nullptr, input_columns_, output_columns_); - return node; -} - -void RenameNode::Print(std::ostream &out) const { - out << (Name() + "(input:" + PrintColumns(input_columns_) + ",output:" + PrintColumns(output_columns_) + ")"); -} - -Status RenameNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (input_columns_.size() != output_columns_.size()) { - std::string err_msg = "Rename: 'input columns' and 'output columns' must have the same size."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("Rename", "input_columns", input_columns_)); - - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("Rename", "output_columns", output_columns_)); - - return Status::OK(); -} - -Status RenameNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(input_columns_, output_columns_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -Status RenameNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["input_columns"] = input_columns_; - args["output_columns"] = output_columns_; - *out_json = args; - return Status::OK(); -} - -Status RenameNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "input_columns", kRenameNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "output_columns", kRenameNode)); - std::vector input_columns = json_obj["input_columns"]; - std::vector output_columns = json_obj["output_columns"]; - *result = std::make_shared(ds, input_columns, output_columns); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status RenameNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status RenameNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h deleted file mode 100644 index 423fd9561..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_RENAME_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_RENAME_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class RenameNode : public DatasetNode { - public: - /// \brief Constructor - explicit RenameNode(std::shared_ptr child, const std::vector &input_columns, - const std::vector &output_columns); - - /// \brief Destructor - ~RenameNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kRenameNode; } - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter functions - const std::vector &InputColumns() const { return input_columns_; } - const std::vector &OutputColumns() const { return output_columns_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - private: - std::vector input_columns_; - std::vector output_columns_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_RENAME_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.cc deleted file mode 100644 index af0e81523..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.cc +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RepeatNode::RepeatNode(std::shared_ptr child, int32_t count) : op_(nullptr), repeat_count_(count) { - this->AddChild(child); -} - -std::shared_ptr RepeatNode::Copy() { - auto node = std::make_shared(nullptr, this->repeat_count_); - return node; -} - -void RepeatNode::Print(std::ostream &out) const { out << (Name() + "(count:" + std::to_string(repeat_count_) + ") "); } - -Status RepeatNode::Build(std::vector> *const node_ops) { - auto new_op = std::make_shared(repeat_count_); - new_op->SetTotalRepeats(GetTotalRepeats()); - new_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(new_op); - op_ = new_op; - - // Add this RepeatOp to its RepeatOp/EpochCtrlOp ancestor's EOE list. - // When the ancestor reaches an end-of-epoch boundary, it will send a "reset" signal to all the ops in the EOE list. - // The ancestor is updated by GeneratorNodePass post pass. - // Assumption: - // We build the run-time ops from IR nodes from top to bottom. Hence Repeat/EpochCtrl ancestor ops are built - // before this leaf Generator op is built. - std::shared_ptr tmp_repeat_node = reset_ancestor_.lock(); - if (tmp_repeat_node != nullptr) { - tmp_repeat_node->op_->AddToEoeList(new_op); - } - return Status::OK(); -} - -Status RepeatNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (repeat_count_ <= 0 && repeat_count_ != -1) { - std::string err_msg = - "Repeat: 'repeat_count' should be either -1 or positive integer, but got: " + std::to_string(repeat_count_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -// Get Dataset size -Status RepeatNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows; - RETURN_IF_NOT_OK(children_[0]->GetDatasetSize(size_getter, estimate, &num_rows)); - if (num_rows > 0 && repeat_count_ > 0) { - num_rows = num_rows * repeat_count_; - } - *dataset_size = num_rows; - dataset_size_ = num_rows; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status RepeatNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status RepeatNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status RepeatNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["count"] = repeat_count_; - *out_json = args; - return Status::OK(); -} - -Status RepeatNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "count", kRepeatNode)); - int32_t count = json_obj["count"]; - *result = std::make_shared(ds, count); - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h deleted file mode 100644 index f64368371..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_REPEAT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_REPEAT_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class RepeatOp; - -class RepeatNode : public DatasetNode { - // Allow GeneratorNode to access internal members - friend class GeneratorNode; - - public: - /// \brief Constructor - RepeatNode() : op_(nullptr), repeat_count_(-1) {} - - /// \brief Constructor - RepeatNode(std::shared_ptr child, int32_t count); - - /// \brief Destructor - ~RepeatNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kRepeatNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter - /// \return Number of cycles to repeat the execution - int32_t Count() const { return repeat_count_; } - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Record the Repeat/EpochCtrl node that is the closest ancestor of this node - /// \param[in] the ancestor node - /// \return Status of the function - Status AddResetAncestor(const std::shared_ptr &src) { - /* - * This check is to ensure we don't overwrite an existing value of its ancestor. - * It is okay to assign to the same value more than once in RepeatNode (but not in GeneratorNode). - * Consider the following scenario - * EpochCtrl(-1) - * | - * Repeat - * | - * Concat - * / \ - * GenData1 GenData2 - * - * We will record the ancestor relationship of (Repeat, EpochCtrl) twice, one at Visit(GenData1), the other at - * Vist(GenData2). - */ - std::shared_ptr tmp_repeat_node = reset_ancestor_.lock(); - CHECK_FAIL_RETURN_UNEXPECTED(tmp_repeat_node == nullptr || tmp_repeat_node == src, - "Internal error: Overwriting an existing value"); - reset_ancestor_ = src; - return Status::OK(); - } - - /// \brief Getter functions - int32_t RepeatCount() const { return repeat_count_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - protected: - std::shared_ptr op_; // keep its corresponding run-time op of EpochCtrlNode and RepeatNode - std::weak_ptr reset_ancestor_; // updated its immediate Repeat/EpochCtrl ancestor in GeneratorNodePass - int32_t repeat_count_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_REPEAT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.cc deleted file mode 100644 index 27ec910d5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.cc +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for RootNode -RootNode::RootNode(std::shared_ptr child) : DatasetNode() { - // The root node's parent must remain nullptr, which is set in the constructor of DatasetNode. - AddChild(child); -} - -std::shared_ptr RootNode::Copy() { - auto node = std::make_shared(nullptr); - node->SetNumEpochs(num_epochs_); - return node; -} - -void RootNode::Print(std::ostream &out) const { out << Name(); } - -Status RootNode::Build(std::vector> *const node_ops) { - // root node doesn't build a runtime Op. this function should return Status::Error when called. - std::string err_msg = "Root node doesn't build a runtime Op"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); -} - -// Function to validate the parameters for RootNode -Status RootNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (num_epochs_ <= 0 && num_epochs_ != -1) { - std::string err_msg = - "RootNode: num_epochs should be either -1 or positive integer, num_epochs: " + std::to_string(num_epochs_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (parent_ != nullptr) { - std::string err_msg = "[Internal ERROR]: root node should not have a parent"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (children_.size() != 1) { - std::string err_msg = "[Internal ERROR]: root node should have one child node"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (children_[0] == nullptr) { - std::string err_msg = "[Internal ERROR]: root node's child is a null pointer"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status RootNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status RootNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h deleted file mode 100644 index 7d960d054..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ROOT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ROOT_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { - -class RootNode : public DatasetNode { - public: - /// \brief Constructor - RootNode() : DatasetNode(), num_epochs_(0), step_(0), dataset_size_(-1) {} - - /// \brief Constructor - explicit RootNode(std::shared_ptr child); - - /// \brief Destructor - ~RootNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kRootNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *node_ops) override; - - /// \brief Getter of number of epochs - int32_t NumEpochs() const { return num_epochs_; } - - /// \brief Getter of number of epochs - int64_t Step() const { return step_; } - - /// \brief Getter of number of steps in one epoch - int64_t DatasetSize() const { return dataset_size_; } - - /// \brief Setter of number of epochs - void SetStep(int64_t step) { step_ = step; } - - /// \brief Setter of number of epochs - void SetNumEpochs(int32_t num_epochs) override { num_epochs_ = num_epochs; } - - /// \brief Setter of number of steps in one epoch - void SetDatasetSize(int64_t dataset_size) { dataset_size_ = dataset_size; } - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - int32_t num_epochs_; - int64_t step_; // to support reset - int64_t dataset_size_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ROOT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.cc deleted file mode 100644 index c095777e2..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/shuffle_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -// Constructor for ShuffleNode -ShuffleNode::ShuffleNode(std::shared_ptr child, int32_t shuffle_size, bool reset_every_epoch) - : shuffle_size_(shuffle_size), shuffle_seed_(GetSeed()), reset_every_epoch_(reset_every_epoch) { - this->AddChild(child); -} - -std::shared_ptr ShuffleNode::Copy() { - auto node = std::make_shared(nullptr, shuffle_size_, reset_every_epoch_); - return node; -} - -void ShuffleNode::Print(std::ostream &out) const { - out << (Name() + "(shuffle_size:" + std::to_string(shuffle_size_) + - ",reset_every_epoch:" + (reset_every_epoch_ ? "true" : "false") + ")"); -} - -// Function to build the ShuffleOp -Status ShuffleNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(shuffle_size_, shuffle_seed_, connector_que_size_, reset_every_epoch_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Function to validate the parameters for ShuffleNode -Status ShuffleNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateScalar("Shuffle", "shuffle_size", shuffle_size_, {1}, true)); - return Status::OK(); -} - -Status ShuffleNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["buffer_size"] = shuffle_size_; - args["reset_each_epoch"] = reset_every_epoch_; - *out_json = args; - return Status::OK(); -} - -Status ShuffleNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("buffer_size") != json_obj.end(), "Failed to find buffer_size"); - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("reset_each_epoch") != json_obj.end(), "Failed to find reset_each_epoch"); - int32_t buffer_size = json_obj["buffer_size"]; - bool reset_every_epoch = json_obj["reset_each_epoch"]; - *result = std::make_shared(ds, buffer_size, reset_every_epoch); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status ShuffleNode::Accept(IRNodePass *const p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status ShuffleNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h deleted file mode 100644 index 9ffefbf6e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SHUFFLE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SHUFFLE_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class ShuffleNode : public DatasetNode { - public: - ShuffleNode(std::shared_ptr child, int32_t shuffle_size, bool reset_every_epoch); - - ~ShuffleNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kShuffleNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - Status ValidateParams() override; - - /// \brief Getter functions - int32_t ShuffleSize() const { return shuffle_size_; } - uint32_t ShuffleSeed() const { return shuffle_seed_; } - bool ResetEveryEpoch() const { return reset_every_epoch_; } - - /// \brief Setter function for shuffle_seed_ - /// \param[in] shuffle_seed The shuffle seed value to be set - void SetShuffleSeed(uint32_t shuffle_seed) { shuffle_seed_ = shuffle_seed; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - int32_t shuffle_size_; - uint32_t shuffle_seed_; - bool reset_every_epoch_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SHUFFLE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.cc deleted file mode 100644 index 397d93ce9..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.cc +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/skip_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for SkipNode -SkipNode::SkipNode(int32_t count) : skip_count_(count) {} - -SkipNode::SkipNode(const std::shared_ptr &child, int32_t count) : skip_count_(count) { - this->AddChild(child); -} - -std::shared_ptr SkipNode::Copy() { - auto node = std::make_shared(nullptr, skip_count_); - return node; -} - -void SkipNode::Print(std::ostream &out) const { out << (Name() + "(skip_count:" + std::to_string(skip_count_) + ")"); } - -// Function to build the SkipOp -Status SkipNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(skip_count_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - if (once_only_) { - op->SetOnceOnly(true); - } - node_ops->push_back(op); - return Status::OK(); -} - -// Function to validate the parameters for SkipNode -Status SkipNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (skip_count_ <= -1) { - std::string err_msg = "Skip: 'skip_count' should not be negative, but got: " + std::to_string(skip_count_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -// Get Dataset size -Status SkipNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows; - RETURN_IF_NOT_OK(children_[0]->GetDatasetSize(size_getter, estimate, &num_rows)); - *dataset_size = 0; - if (skip_count_ < num_rows) { - *dataset_size = num_rows - skip_count_; - } - dataset_size_ = *dataset_size; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status SkipNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status SkipNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status SkipNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["count"] = skip_count_; - *out_json = args; - return Status::OK(); -} - -Status SkipNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "count", kSkipNode)); - int32_t count = json_obj["count"]; - *result = std::make_shared(ds, count); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h deleted file mode 100644 index d2447abcc..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SKIP_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SKIP_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class SkipNode : public DatasetNode { - public: - explicit SkipNode(int32_t count); - - /// \brief Constructor - explicit SkipNode(const std::shared_ptr &child, int32_t count); - - /// \brief Destructor - ~SkipNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kSkipNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter - /// \return Number of rows to skip - int32_t Count() const { return skip_count_; } - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - int32_t SkipCount() const { return skip_count_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - void SetOnceOnly(bool flag) { once_only_ = flag; } - - /// \brief Getter functions - const bool OnceOnly() const { return once_only_; } - - private: - int32_t skip_count_; - bool once_only_ = false; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SKIP_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc deleted file mode 100644 index 65e15172c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/ag_news_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for AGNewsNode. -AGNewsNode::AGNewsNode(const std::string &dataset_dir, int64_t num_samples, ShuffleMode shuffle, - const std::string &usage, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - usage_(usage), - ag_news_files_list_(WalkAllFiles(usage, dataset_dir)) { - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr AGNewsNode::Copy() { - auto node = - std::make_shared(dataset_dir_, num_samples_, shuffle_, usage_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void AGNewsNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status AGNewsNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("AGNewsDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("AGNewsDataset", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateScalar("AGNewsDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("AGNewsDataset", num_shards_, shard_id_)); - RETURN_IF_NOT_OK(ValidateEnum("AGNewsDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - if (!column_names_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("AGNewsDataset", "column_names", column_names_)); - } - return Status::OK(); -} - -// Function to build AGNewsNode. -Status AGNewsNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = ag_news_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - // Because AGNews does not have external column_defaults nor column_names parameters, - // they need to be set before AGNewsOp is initialized. - // AGNews data set is formatted as three columns of data, so three columns are added. - std::vector> column_default; - column_default.push_back(std::make_shared>(AGNewsOp::STRING, "")); - column_default.push_back(std::make_shared>(AGNewsOp::STRING, "")); - column_default.push_back(std::make_shared>(AGNewsOp::STRING, "")); - std::vector column_name = {"index", "title", "description"}; - // AGNews data values are always delimited by a comma. - char field_delim_ = ','; - std::shared_ptr ag_news_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, connector_que_size_, shuffle_files, - num_shards_, shard_id_, field_delim_, column_default, column_name, sorted_dataset_files); - RETURN_IF_NOT_OK(ag_news_op->Init()); - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(AGNewsOp::CountAllFileRows(ag_news_files_list_, false, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - ag_news_op->SetTotalRepeats(GetTotalRepeats()); - ag_news_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(ag_news_op); - return Status::OK(); -} - -// Get the shard id of node. -Status AGNewsNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size. -Status AGNewsNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(AGNewsOp::CountAllFileRows(ag_news_files_list_, false, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status AGNewsNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and -// should be promoted to its parent class. AGNews (for which internally is based off CSV) -// by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, -// that cache can inherit this sampler from the leaf, providing sampling support from -// the caching layer. -// Should be promoted to its parent class. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status AGNewsNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this AGNews node, then -// the cache will be executing a sampler for fetching the data. As such, any -// options in the AGNews node need to be reset to its defaults so that this -// AGNews node will produce the full set of data into the cache. -Status AGNewsNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector AGNewsNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector ag_news_files_list; - Path train_prefix("train.csv"); - Path test_prefix("test.csv"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - ag_news_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - ag_news_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - ag_news_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - ag_news_files_list.push_back(temp_path1.ToString()); - } - return ag_news_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h deleted file mode 100644 index 41dcdc8ec..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/ag_news_node.h +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AG_NEWS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AG_NEWS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \brief class AGNewsNode. -/// \brief Dataset derived class to represent AGNews dataset. -class AGNewsNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - AGNewsNode(const std::string &dataset_dir, int64_t num_samples, ShuffleMode shuffle, const std::string &usage, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~AGNewsNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kAGNewsNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief AGNews by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in - /// the tree, that cache can inherit this sampler from the leaf, providing - /// sampling support from the caching layer. That is why we setup the - /// sampler for a leaf node that does not use sampling. Note: This - /// function is common among NonMappableSourceNode and should be promoted - /// to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this ag_news node, - /// then the cache will be executing a sampler for fetching the data. - /// As such, any options in the AGNews node need to be reset to its defaults - /// so that this AGNews node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its - /// parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of AGNews. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - std::vector> column_defaults_; - std::vector column_names_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::vector ag_news_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AG_NEWS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.cc deleted file mode 100644 index 694964e64..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.cc +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/album_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -// Constructor for AlbumNode -AlbumNode::AlbumNode(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - schema_path_(data_schema), - column_names_(column_names), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr AlbumNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, schema_path_, column_names_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void AlbumNode::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status AlbumNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("AlbumDataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("AlbumDataset", {schema_path_})); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("AlbumDataset", sampler_)); - - if (!column_names_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("AlbumDataset", "column_names", column_names_)); - } - - return Status::OK(); -} - -// Function to build AlbumNode -Status AlbumNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->LoadSchemaFile(schema_path_, column_names_)); - - // Argument that is not exposed to user in the API. - std::set extensions = {".json", ".JSON"}; - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto album_op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, decode_, extensions, - std::move(schema), std::move(sampler_rt)); - album_op->SetTotalRepeats(GetTotalRepeats()); - album_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(album_op); - return Status::OK(); -} - -// Get the shard id of node -Status AlbumNode::GetShardId(int32_t *const shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status AlbumNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0; - // iterate over the files in the directory and count files to initiate num_rows - Path folder(dataset_dir_); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (!folder.Exists() || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open folder: " + dataset_dir_); - } - std::set extensions = {".json", ".JSON"}; - - while (dirItr->HasNext()) { - Path file = dirItr->Next(); - if (extensions.empty() || extensions.find(file.Extension()) != extensions.end()) { - num_rows += 1; - } - } - // give sampler the total number of files and check if num_samples is smaller - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - int64_t sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - // We cache dataset size so as to not duplicated run - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status AlbumNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - args["data_schema"] = schema_path_; - args["column_names"] = column_names_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status AlbumNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "data_schema", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "column_names", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kAlbumNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kAlbumNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string data_schema = json_obj["data_schema"]; - std::vector column_names = json_obj["column_names"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, data_schema, column_names, decode, sampler, cache); - (void)((*ds)->SetNumWorkers(json_obj["num_parallel_workers"])); - (void)((*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"])); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h deleted file mode 100644 index a127cf195..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_ALBUM_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_ALBUM_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class AlbumNode : public MappableSourceNode { - public: - /// \brief Constructor - AlbumNode(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor - ~AlbumNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kAlbumNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *const shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &SchemaPath() const { return schema_path_; } - const std::vector &ColumnNames() const { return column_names_; } - bool Decode() const { return decode_; } - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - private: - std::string dataset_dir_; - std::string schema_path_; - std::vector column_names_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_ALBUM_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.cc deleted file mode 100755 index 951ba96c4..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.cc +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h" - -#include -#include - -namespace mindspore { -namespace dataset { -// Constructor for AmazonReviewNode -AmazonReviewNode::AmazonReviewNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - usage_(usage), - amazon_review_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. - // User discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work - // if the num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to - // return num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr AmazonReviewNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void AmazonReviewNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status AmazonReviewNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("AmazonReviewDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("AmazonReviewDataset", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("AmazonReviewDataset", amazon_review_files_list_)); - RETURN_IF_NOT_OK(ValidateScalar("AmazonReviewDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateEnum("AmazonReviewDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("AmazonReviewDataset", num_shards_, shard_id_)); - return Status::OK(); -} - -Status AmazonReviewNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = amazon_review_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - std::vector> column_default; - column_default.push_back(std::make_shared>(AmazonReviewOp::STRING, "")); - column_default.push_back(std::make_shared>(AmazonReviewOp::STRING, "")); - column_default.push_back(std::make_shared>(AmazonReviewOp::STRING, "")); - - std::vector column_name = {"label", "title", "content"}; - char field_delim = ','; - std::shared_ptr amazon_review_op = std::make_shared( - num_workers_, num_samples_, worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_, - field_delim, column_default, column_name, sorted_dataset_files); - RETURN_IF_NOT_OK(amazon_review_op->Init()); - - // If a global shuffle is used for AmazonReview, it will inject a shuffle op over the AmazonReview. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be - // built.This is achieved in the cache transform pass where we call MakeSimpleProducer to reset AmazonReview's - // shuffle option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(AmazonReviewOp::CountAllFileRows(sorted_dataset_files, false, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - amazon_review_op->SetTotalRepeats(GetTotalRepeats()); - amazon_review_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(amazon_review_op); - return Status::OK(); -} - -Status AmazonReviewNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size -Status AmazonReviewNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(AmazonReviewOp::CountAllFileRows(amazon_review_files_list_, false, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status AmazonReviewNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent -// class. AmazonReview by itself is a non-mappable dataset that does not support sampling. However, if a cache -// operator is injected at some other place higher in the tree, that cache can inherit this sampler from the leaf, -// providing sampling support from the caching layer. That is why we setup the sampler for a leaf node that does not -// use sampling. -Status AmazonReviewNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this AmazonReview node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the AmazonReview node need to be reset to its defaults so -// If a cache has been added into the ascendant tree over this AmazonReview node, then the cache will be executing -Status AmazonReviewNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector AmazonReviewNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector amazon_review_files_list; - Path train_prefix("train.csv"); - Path test_prefix("test.csv"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - amazon_review_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - amazon_review_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - amazon_review_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - amazon_review_files_list.push_back(temp_path1.ToString()); - } - return amazon_review_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h deleted file mode 100755 index ca55a17a8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AMAZON_REVIEW_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AMAZON_REVIEW_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/amazon_review_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class AmazonReviewNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - AmazonReviewNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~AmazonReviewNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kAmazonReviewNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief AmazonReview by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this AmazonReview node, then the cache will be - /// executing a sampler for fetching the data. As such, any options in the AmazonReview node need to be reset - /// to its defaults so that this AmazonReview node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of AmazonReview. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - std::vector> column_defaults_; - std::vector column_names_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::vector amazon_review_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_AMAZON_REVIEW_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.cc deleted file mode 100755 index 2334d56be..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.cc +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/caltech_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const std::set kExts = {".jpg", ".JPEG"}; - -Caltech256Node::Caltech256Node(const std::string &dataset_dir, bool decode, std::shared_ptr sampler, - std::shared_ptr cache = nullptr) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), decode_(decode), sampler_(sampler) {} - -std::shared_ptr Caltech256Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, decode_, sampler, cache_); - return node; -} - -void Caltech256Node::Print(std::ostream &out) const { - out << (Name() + "(path: " + dataset_dir_ + ", decode: " + (decode_ ? "true" : "false") + ")"); -} - -Status Caltech256Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Caltech256Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("Caltech256Node", sampler_)); - return Status::OK(); -} - -Status Caltech256Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg exists in CaltechOp, but is not externalized (in Python API). - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, decode_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node. -Status Caltech256Node::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status Caltech256Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - RETURN_IF_NOT_OK(CaltechOp::CountRowsAndClasses(dataset_dir_, kExts, &num_rows, nullptr, {})); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Caltech256Node::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status Caltech256Node::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCaltech256Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCaltech256Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCaltech256Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kCaltech256Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kCaltech256Node)); - std::string dataset_dir = json_obj["dataset_dir"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, decode, sampler, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h deleted file mode 100755 index 762a83d97..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/caltech256_node.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CALTECH256_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CALTECH256_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class Caltech256Node. -/// \brief A Dataset derived class to represent Caltech256 dataset. -class Caltech256Node : public MappableSourceNode { - public: - /// \brief Constructor. - Caltech256Node(const std::string &dataset_dir, bool decode, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~Caltech256Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kCaltech256Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[out] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json. - /// \param[in] json_obj The JSON object to be deserialized. - /// \param[out] ds Deserialized dataset. - /// \return Status The status code returned. - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CALTECH256_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.cc deleted file mode 100644 index 0ab569b1e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.cc +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h" - -#include -#include -#include -#include -#include -#include - -#include "utils/file_utils.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/celeba_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -#ifdef ENABLE_PYTHON -// Constructor for CelebANode -CelebANode::CelebANode(const std::string &dataset_dir, const std::string &usage, - const std::shared_ptr &sampler, const bool &decode, - const std::set &extensions, const std::shared_ptr &cache, - py::function decrypt) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - sampler_(sampler), - decode_(decode), - extensions_(extensions), - decrypt_(decrypt) {} -#else -// Constructor for CelebANode -CelebANode::CelebANode(const std::string &dataset_dir, const std::string &usage, - const std::shared_ptr &sampler, const bool &decode, - const std::set &extensions, const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - sampler_(sampler), - decode_(decode), - extensions_(extensions) {} -#endif - -std::shared_ptr CelebANode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); -#ifdef ENABLE_PYTHON - auto node = std::make_shared(dataset_dir_, usage_, sampler, decode_, extensions_, cache_, decrypt_); -#else - auto node = std::make_shared(dataset_dir_, usage_, sampler, decode_, extensions_, cache_); -#endif - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CelebANode::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status CelebANode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("CelebADataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("CelebADataset", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("CelebADataset", usage_, {"all", "train", "valid", "test"})); - - return Status::OK(); -} - -// Function to build CelebANode -Status CelebANode::Build(std::vector> *const node_ops) { - std::unique_ptr schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - // label is like this:0 1 0 0 1...... - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("attr", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - -#ifdef ENABLE_PYTHON - auto celeba_op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, decode_, usage_, - extensions_, std::move(schema), std::move(sampler_rt), decrypt_); -#else - auto celeba_op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, decode_, usage_, - extensions_, std::move(schema), std::move(sampler_rt)); -#endif - celeba_op->SetTotalRepeats(GetTotalRepeats()); - celeba_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(celeba_op); - - return Status::OK(); -} - -// Get the shard id of node -Status CelebANode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status CelebANode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - std::ifstream partition_file; - std::string line; - Path folder_path(dataset_dir_); - - auto realpath = FileUtils::GetRealPath((folder_path / "list_attr_celeba.txt").ToString().c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file, get real path failed, path=" << (folder_path / "list_attr_celeba.txt").ToString(); - RETURN_STATUS_UNEXPECTED("Invalid file, get real path failed, path=" + - (folder_path / "list_attr_celeba.txt").ToString()); - } - - std::ifstream attr_file(realpath.value(), std::ios::in); - if (!attr_file.is_open()) { - std::string attr_file_name = (folder_path / "list_attr_celeba.txt").ToString(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open Celeba attr file: " + attr_file_name); - } - - std::string rows_num; - (void)getline(attr_file, rows_num); - int64_t num_rows; - try { - num_rows = static_cast(std::stoul(rows_num)); // First line is rows number in attr file - } catch (std::invalid_argument &e) { - attr_file.close(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, failed to convert rows_num from attr_file to unsigned long, invalid argument: " + rows_num); - } catch (std::out_of_range &e) { - attr_file.close(); - RETURN_STATUS_UNEXPECTED( - "Invalid data, failed to convert rows_num from attr_file to unsigned long, out of range: " + rows_num); - } - if (usage_ != "all") { - int64_t partition_num = 0; - char usage_type; - if (usage_ == "train") { - usage_type = '0'; - } else { - if (usage_ == "valid") { - usage_type = '1'; - } else { - if (usage_ == "test") { - usage_type = '2'; - } else { - attr_file.close(); - RETURN_STATUS_UNEXPECTED("Invalid usage."); - } - } - } - if (!partition_file.is_open()) { - auto realpath_eval = FileUtils::GetRealPath((folder_path / "list_eval_partition.txt").ToString().c_str()); - if (!realpath_eval.has_value()) { - attr_file.close(); - MS_LOG(ERROR) << "Invalid file, get real path failed, path=" - << (folder_path / "list_eval_partition.txt").ToString(); - RETURN_STATUS_UNEXPECTED("Invalid file, get real path failed, path=" + - (folder_path / "list_eval_partition.txt").ToString()); - } - - partition_file.open(realpath_eval.value(), std::ios::in); - } - if (partition_file.is_open()) { - while (getline(partition_file, line)) { - std::size_t start = line.find(' '); - if (start == std::string::npos) { - continue; - } - if (line.at(start + 1) == usage_type) { - partition_num++; - } - } - } else { - attr_file.close(); - std::string partition_file_name = "list_eval_partition.txt"; - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open CelebA partition file: " + partition_file_name); - } - num_rows = std::min(num_rows, partition_num); - } - - std::shared_ptr sampler_rt = nullptr; - auto s = sampler_->SamplerBuild(&sampler_rt); - if (s != Status::OK()) { - attr_file.close(); - partition_file.close(); - return s; - } - int64_t sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - s = size_getter->DryRun(shared_from_this(), &sample_size); - if (s != Status::OK()) { - attr_file.close(); - partition_file.close(); - return s; - } - } - *dataset_size = sample_size; - attr_file.close(); - partition_file.close(); - return Status::OK(); -} - -Status CelebANode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - args["extensions"] = extensions_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status CelebANode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kCelebANode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "extensions", kCelebANode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - bool decode = json_obj["decode"]; - std::set extension = json_obj["extensions"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, sampler, decode, extension, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h deleted file mode 100644 index a75c79e45..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CELEBA_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CELEBA_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CelebANode : public MappableSourceNode { - public: -#ifdef ENABLE_PYTHON - /// \brief Constructor - CelebANode(const std::string &dataset_dir, const std::string &usage, const std::shared_ptr &sampler, - const bool &decode, const std::set &extensions, const std::shared_ptr &cache, - py::function decrypt = py::none()); -#else - /// \brief Constructor - CelebANode(const std::string &dataset_dir, const std::string &usage, const std::shared_ptr &sampler, - const bool &decode, const std::set &extensions, const std::shared_ptr &cache); -#endif - - /// \brief Destructor - ~CelebANode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCelebANode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - bool Decode() const { return decode_; } - const std::set &Extensions() const { return extensions_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool decode_; - std::set extensions_; - std::shared_ptr sampler_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CELEBA_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.cc deleted file mode 100644 index 6996f87f5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -// Constructor for Cifar100Node -Cifar100Node::Cifar100Node(const std::string &dataset_dir, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr Cifar100Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void Cifar100Node::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status Cifar100Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Cifar100Dataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("Cifar100Dataset", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("Cifar100Dataset", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -// Function to build CifarOp for Cifar100 -Status Cifar100Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("coarse_label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("fine_label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto cifar_op = std::make_shared(CifarOp::CifarType::kCifar100, usage_, num_workers_, dataset_dir_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - cifar_op->SetTotalRepeats(GetTotalRepeats()); - cifar_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(cifar_op); - - return Status::OK(); -} - -// Get the shard id of node -Status Cifar100Node::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status Cifar100Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(CifarOp::CountTotalRows(dataset_dir_, usage_, false, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Cifar100Node::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status Cifar100Node::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCifar100Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCifar100Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCifar100Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kCifar100Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kCifar100Node)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, sampler, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h deleted file mode 100644 index 07987c300..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR100_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR100_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class Cifar100Node : public MappableSourceNode { - public: - /// \brief Constructor - Cifar100Node(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~Cifar100Node() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCifar100Node; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR100_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.cc deleted file mode 100644 index 7817625c4..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.cc +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cifar_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -// Constructor for Cifar10Node -Cifar10Node::Cifar10Node(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr Cifar10Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void Cifar10Node::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status Cifar10Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Cifar10Dataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("Cifar10Dataset", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("Cifar10Dataset", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -// Function to build CifarOp for Cifar10 -Status Cifar10Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto cifar_op = std::make_shared(CifarOp::CifarType::kCifar10, usage_, num_workers_, dataset_dir_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - cifar_op->SetTotalRepeats(GetTotalRepeats()); - cifar_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(cifar_op); - - return Status::OK(); -} - -// Get the shard id of node -Status Cifar10Node::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status Cifar10Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(CifarOp::CountTotalRows(dataset_dir_, usage_, true, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Cifar10Node::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status Cifar10Node::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCifar10Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCifar10Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCifar10Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kCifar10Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kCifar10Node)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, sampler, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h deleted file mode 100644 index 699a53090..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR10_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR10_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class Cifar10Node : public MappableSourceNode { - public: - /// \brief Constructor - Cifar10Node(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~Cifar10Node() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCifar10Node; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CIFAR10_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.cc deleted file mode 100644 index 9fcd39409..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cityscapes_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for CityscapesNode -CityscapesNode::CityscapesNode(const std::string &dataset_dir, const std::string &usage, - const std::string &quality_mode, const std::string &task, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) - : MappableSourceNode(cache), - dataset_dir_(dataset_dir), - usage_(usage), - quality_mode_(quality_mode), - task_(task), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr CityscapesNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, quality_mode_, task_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CityscapesNode::Print(std::ostream &out) const { - out << Name() + "(dataset dir:" + dataset_dir_; - out << ", task:" + task_ << ", quality mode:" + quality_mode_ << ", usage:" + usage_; - if (sampler_ != nullptr) { - out << ", sampler"; - } - if (cache_ != nullptr) { - out << ", cache"; - } - out << ")"; -} - -Status CityscapesNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("CityscapesNode", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateStringValue("CityscapesNode", task_, {"instance", "semantic", "polygon", "color"})); - RETURN_IF_NOT_OK(ValidateStringValue("CityscapesNode", quality_mode_, {"fine", "coarse"})); - if (quality_mode_ == "fine") { - RETURN_IF_NOT_OK(ValidateStringValue("CityscapesNode", usage_, {"train", "test", "val", "all"})); - } else { - RETURN_IF_NOT_OK(ValidateStringValue("CityscapesNode", usage_, {"train", "train_extra", "val", "all"})); - } - RETURN_IF_NOT_OK(ValidateDatasetSampler("CityscapesNode", sampler_)); - return Status::OK(); -} - -// Function to build CityscapesOp for Cityscapes -Status CityscapesNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - if (task_ == "polygon") { - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("task", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - } else { - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("task", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 0, &scalar))); - } - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto cityscapes_op = std::make_shared(num_workers_, dataset_dir_, usage_, quality_mode_, task_, decode_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - cityscapes_op->SetTotalRepeats(GetTotalRepeats()); - cityscapes_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(cityscapes_op); - return Status::OK(); -} - -// Get the shard id of node -Status CityscapesNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size -Status CityscapesNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(CityscapesOp::CountTotalRows(dataset_dir_, usage_, quality_mode_, task_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CityscapesNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["quality_mode"] = quality_mode_; - args["task"] = task_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h deleted file mode 100644 index 290c2246b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CITYSCAPES_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CITYSCAPES_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CityscapesNode : public MappableSourceNode { - public: - /// \brief Constructor. - CityscapesNode(const std::string &dataset_dir, const std::string &usage, const std::string &quality_mode, - const std::string &task, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor. - ~CityscapesNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kCityscapesNode; } - - /// \brief Print the description. - /// \param[out] out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - const std::string &QualityMode() const { return quality_mode_; } - - /// \brief Getter functions. - const std::string &Task() const { return task_; } - - /// \brief Getter functions. - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::string quality_mode_; - std::string task_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CITYSCAPES_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.cc deleted file mode 100644 index 3f29e6bf6..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.cc +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/clue_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// Constructor for CLUENode -CLUENode::CLUENode(const std::vector clue_files, std::string task, std::string usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_files_(clue_files), - task_(task), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) {} - -std::shared_ptr CLUENode::Copy() { - auto node = - std::make_shared(dataset_files_, task_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CLUENode::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ",..." + - ",num_shards:" + std::to_string(num_shards_) + ",shard_id:" + std::to_string(shard_id_) + ")"); -} - -Status CLUENode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("CLUEDataset", dataset_files_)); - RETURN_IF_NOT_OK(ValidateStringValue("CLUEDataset", task_, {"AFQMC", "TNEWS", "IFLYTEK", "CMNLI", "WSC", "CSL"})); - RETURN_IF_NOT_OK(ValidateStringValue("CLUEDataset", usage_, {"train", "test", "eval"})); - RETURN_IF_NOT_OK(ValidateEnum("CLUEDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateScalar("CLUEDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("CLUEDataset", num_shards_, shard_id_)); - - return Status::OK(); -} - -// Function to split string based on a character delimiter -std::vector CLUENode::split(const std::string &s, char delim) { - std::vector res; - std::stringstream ss(s); - std::string item; - - while (getline(ss, item, delim)) { - res.push_back(item); - } - return res; -} - -std::map CLUENode::CreateKeyMapForAFQMCOrCMNLITask() { - std::map key_map; - if (usage_ == "train" || usage_ == "eval") { - key_map["label"] = "label"; - } else { // usage_ == "test" - key_map["id"] = "id"; - } - key_map["sentence1"] = "sentence1"; - key_map["sentence2"] = "sentence2"; - return key_map; -} - -std::map CLUENode::CreateKeyMapForCSLTask() { - std::map key_map; - if (usage_ == "train" || usage_ == "eval") { - key_map["label"] = "label"; - } - key_map["id"] = "id"; - key_map["abst"] = "abst"; - key_map["keyword"] = "keyword"; - return key_map; -} - -std::map CLUENode::CreateKeyMapForIFLYTEKTask() { - std::map key_map; - if (usage_ == "train" || usage_ == "eval") { - key_map["label"] = "label"; - key_map["label_des"] = "label_des"; - } else { // usage_ == "test" - key_map["id"] = "id"; - } - key_map["sentence"] = "sentence"; - return key_map; -} - -std::map CLUENode::CreateKeyMapForTNEWSTask() { - std::map key_map; - if (usage_ == "train" || usage_ == "eval") { - key_map["label"] = "label"; - key_map["label_desc"] = "label_desc"; - } else { // usage_ == "test" - key_map["id"] = "id"; - } - key_map["sentence"] = "sentence"; - key_map["keywords"] = "keywords"; - return key_map; -} - -std::map CLUENode::CreateKeyMapForWSCTask() { - std::map key_map; - if (usage_ == "train" || usage_ == "eval") { - key_map["label"] = "label"; - } - key_map["span1_index"] = "target/span1_index"; - key_map["span2_index"] = "target/span2_index"; - key_map["span1_text"] = "target/span1_text"; - key_map["span2_text"] = "target/span2_text"; - key_map["idx"] = "idx"; - key_map["text"] = "text"; - return key_map; -} - -std::map CLUENode::CreateKeyMap() { - std::map key_map; - if (task_ == "AFQMC" || task_ == "CMNLI") { - key_map = CreateKeyMapForAFQMCOrCMNLITask(); - } else if (task_ == "CSL") { - key_map = CreateKeyMapForCSLTask(); - } else if (task_ == "IFLYTEK") { - key_map = CreateKeyMapForIFLYTEKTask(); - } else if (task_ == "TNEWS") { - key_map = CreateKeyMapForTNEWSTask(); - } else if (task_ == "WSC") { - key_map = CreateKeyMapForWSCTask(); - } - return key_map; -} - -// Function to build CLUENode -Status CLUENode::Build(std::vector> *const node_ops) { - auto key_map = CreateKeyMap(); - ColKeyMap ck_map; - for (auto &p : key_map) { - ck_map.insert({p.first, split(p.second, '/')}); - } - - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order - std::vector sorted_dataset_files = dataset_files_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - std::shared_ptr clue_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, ck_map, sorted_dataset_files, - connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(clue_op->Init()); - - // If a global shuffle is used for Clue, it will inject a shuffle op over the Clue. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset Clue's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(ClueOp::CountAllFileRows(sorted_dataset_files, &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - clue_op->SetTotalRepeats(GetTotalRepeats()); - clue_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(clue_op); - - return Status::OK(); -} - -// Get the shard id of node -Status CLUENode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size -Status CLUENode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(ClueOp::CountAllFileRows(dataset_files_, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CLUENode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_files_; - args["task"] = task_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status CLUENode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "task", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_samples", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_shards", kCLUENode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_id", kCLUENode)); - std::vector dataset_files = json_obj["dataset_dir"]; - std::string task = json_obj["task"]; - std::string usage = json_obj["usage"]; - int64_t num_samples = json_obj["num_samples"]; - ShuffleMode shuffle = static_cast(json_obj["shuffle"]); - int32_t num_shards = json_obj["num_shards"]; - int32_t shard_id = json_obj["shard_id"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_files, task, usage, num_samples, shuffle, num_shards, shard_id, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent -// class. CLUE by itself is a non-mappable dataset that does not support sampling. However, if a cache operator is -// injected at some other place higher in the tree, that cache can inherit this sampler from the leaf, providing -// sampling support from the caching layer. That is why we setup the sampler for a leaf node that does not use -// sampling. -Status CLUENode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this clue node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so -// that this clue node will produce the full set of data into the cache. -Status CLUENode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h deleted file mode 100644 index 78a20da94..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CLUE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CLUE_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class CLUENode -/// \brief A Dataset derived class to represent CLUE dataset -class CLUENode : public NonMappableSourceNode { - public: - /// \brief Constructor - CLUENode(const std::vector dataset_files, std::string task, std::string usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor - ~CLUENode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCLUENode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::vector &DatasetFiles() const { return dataset_files_; } - const std::string &Task() const { return task_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief CLUE by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this clue node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so - /// that this clue node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function - Status MakeSimpleProducer() override; - - private: - /// \brief Split string based on a character delimiter - /// \return A string vector - std::vector split(const std::string &s, char delim); - - /// \brief Generate a key map for AFQMC or CMNLI task according to usage - /// \return The generated key map - std::map CreateKeyMapForAFQMCOrCMNLITask(); - - /// \brief Generate a key map for CSL task according to usage - /// \return The generated key map - std::map CreateKeyMapForCSLTask(); - - /// \brief Generate a key map for IFLYTEK task according to usage - /// \return The generated key map - std::map CreateKeyMapForIFLYTEKTask(); - - /// \brief Generate a key map for TNEWS task according to usage - /// \return The generated key map - std::map CreateKeyMapForTNEWSTask(); - - /// \brief Generate a key map for WSC task according to usage - /// \return The generated key map - std::map CreateKeyMapForWSCTask(); - - /// \brief Generate a key map to be used in Build() according to usage and task - /// \return The generated key map - std::map CreateKeyMap(); - - std::vector dataset_files_; - std::string task_; - std::string usage_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CLUE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.cc deleted file mode 100644 index be43a0ac2..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.cc +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/cmu_arctic_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -CMUArcticNode::CMUArcticNode(const std::string &dataset_dir, const std::string &name, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), name_(name), sampler_(sampler) {} - -void CMUArcticNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr CMUArcticNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, name_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -Status CMUArcticNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("CMUArcticDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("CMUArcticDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("CMUArcticDataset", name_, - {"aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", "fem", "gka", "jmk", - "ksp", "ljm", "lnh", "rms", "rxr", "slp", "slt"})); - return Status::OK(); -} - -Status CMUArcticNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_utterance = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("transcript", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance))); - TensorShape scalar_utterance_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("utterance_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance_id))); - - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, name_, num_workers_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -Status CMUArcticNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status CMUArcticNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(CMUArcticOp::CountTotalRows(dataset_dir_, name_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CMUArcticNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["name"] = name_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h deleted file mode 100644 index 5179bcb09..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CMU_ARCTIC_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CMU_ARCTIC_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CMUArcticNode : public MappableSourceNode { - public: - /// \brief Constructor. - CMUArcticNode(const std::string &dataset_dir, const std::string &name, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~CMUArcticNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kCMUArcticNode; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &GetName() const { return name_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Tells CMUArcticOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string name_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CMU_ARCTIC_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.cc deleted file mode 100644 index 83bd6a09b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.cc +++ /dev/null @@ -1,251 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/coco_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -#ifdef ENABLE_PYTHON -// Constructor for CocoNode -CocoNode::CocoNode(const std::string &dataset_dir, const std::string &annotation_file, const std::string &task, - const bool &decode, const std::shared_ptr &sampler, std::shared_ptr cache, - const bool &extra_metadata, py::function decrypt) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - annotation_file_(annotation_file), - task_(task), - decode_(decode), - sampler_(sampler), - extra_metadata_(extra_metadata), - decrypt_(decrypt) {} -#else -// Constructor for CocoNode -CocoNode::CocoNode(const std::string &dataset_dir, const std::string &annotation_file, const std::string &task, - const bool &decode, const std::shared_ptr &sampler, std::shared_ptr cache, - const bool &extra_metadata) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - annotation_file_(annotation_file), - task_(task), - decode_(decode), - sampler_(sampler), - extra_metadata_(extra_metadata) {} -#endif - -std::shared_ptr CocoNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); -#ifdef ENABLE_PYTHON - auto node = std::make_shared(dataset_dir_, annotation_file_, task_, decode_, sampler, cache_, - extra_metadata_, decrypt_); -#else - auto node = - std::make_shared(dataset_dir_, annotation_file_, task_, decode_, sampler, cache_, extra_metadata_); -#endif - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CocoNode::Print(std::ostream &out) const { out << Name(); } - -Status CocoNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("CocoDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("CocoDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("CocoDataset", {annotation_file_}, "annotation file")); - RETURN_IF_NOT_OK( - ValidateStringValue("CocoDataset", task_, {"Detection", "Stuff", "Panoptic", "Keypoint", "Captioning"})); - - return Status::OK(); -} - -// Function to build CocoNode -Status CocoNode::Build(std::vector> *const node_ops) { - CocoOp::TaskType task_type = CocoOp::TaskType::Detection; - if (task_ == "Detection") { - task_type = CocoOp::TaskType::Detection; - } else if (task_ == "Stuff") { - task_type = CocoOp::TaskType::Stuff; - } else if (task_ == "Keypoint") { - task_type = CocoOp::TaskType::Keypoint; - } else if (task_ == "Panoptic") { - task_type = CocoOp::TaskType::Panoptic; - } else if (task_ == "Captioning") { - task_type = CocoOp::TaskType::Captioning; - } else { - std::string err_msg = "Task type:'" + task_ + "' is not supported."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::unique_ptr schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("image"), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - switch (task_type) { - case CocoOp::TaskType::Detection: - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("bbox"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("category_id"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("iscrowd"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - break; - case CocoOp::TaskType::Stuff: - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("segmentation"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("iscrowd"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - break; - case CocoOp::TaskType::Keypoint: - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("keypoints"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("num_keypoints"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - break; - case CocoOp::TaskType::Panoptic: - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("bbox"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("category_id"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("iscrowd"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("area"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - break; - case CocoOp::TaskType::Captioning: - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("captions"), DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - break; - default: - std::string err_msg = "CocoNode::Build : Invalid task type"; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (extra_metadata_) { - std::string meta_column = std::string(kDftMetaColumnPrefix) + std::string("filename"); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(meta_column, DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - } - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - -#ifdef ENABLE_PYTHON - std::shared_ptr op = - std::make_shared(task_type, dataset_dir_, annotation_file_, num_workers_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt), extra_metadata_, decrypt_); -#else - std::shared_ptr op = - std::make_shared(task_type, dataset_dir_, annotation_file_, num_workers_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt), extra_metadata_); -#endif - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status CocoNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status CocoNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "[Internal ERROR] Unable to build CocoOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CocoNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["annotation_file"] = annotation_file_; - args["task"] = task_; - args["decode"] = decode_; - args["extra_metadata"] = extra_metadata_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status CocoNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "annotation_file", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "task", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kCocoNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "extra_metadata", kCocoNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string annotation_file = json_obj["annotation_file"]; - std::string task = json_obj["task"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - bool extra_metadata = json_obj["extra_metadata"]; - *ds = std::make_shared(dataset_dir, annotation_file, task, decode, sampler, cache, extra_metadata); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h deleted file mode 100644 index fa1933a5a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_COCO_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_COCO_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class CocoNode : public MappableSourceNode { - public: -#ifdef ENABLE_PYTHON - /// \brief Constructor. - CocoNode(const std::string &dataset_dir, const std::string &annotation_file, const std::string &task, - const bool &decode, const std::shared_ptr &sampler, std::shared_ptr cache, - const bool &extra_metadata, py::function decrypt = py::none()); -#else - /// \brief Constructor. - CocoNode(const std::string &dataset_dir, const std::string &annotation_file, const std::string &task, - const bool &decode, const std::shared_ptr &sampler, std::shared_ptr cache, - const bool &extra_metadata); -#endif - - /// \brief Destructor. - ~CocoNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kCocoNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &AnnotationFile() const { return annotation_file_; } - const std::string &Task() const { return task_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json. - /// \param[in] json_obj The JSON object to be deserialized. - /// \param[out] ds Deserialized dataset. - /// \return Status The status code returned. - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string annotation_file_; - std::string task_; - bool decode_; - std::shared_ptr sampler_; - bool extra_metadata_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_COCO_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.cc deleted file mode 100644 index bfb2bde4d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.cc +++ /dev/null @@ -1,206 +0,0 @@ -/** - Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/conll2000_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for CoNLL2000Node. -CoNLL2000Node::CoNLL2000Node(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - conll2000_file_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr CoNLL2000Node::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CoNLL2000Node::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status CoNLL2000Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("CoNLL2000Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("CoNLL2000Node", usage_, {"train", "test", "all"})); - - if (num_samples_ < 0) { - std::string err_msg = "CoNLL2000Node: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateDatasetShardParams("CoNLL2000Node", num_shards_, shard_id_)); - return Status::OK(); -} - -// Function to build CoNLL2000Node. -Status CoNLL2000Node::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = conll2000_file_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("word", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("pos_tag", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("chunk_tag", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - // Create and initialize CoNLL2000Op. - std::shared_ptr conll2000_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(conll2000_op->Init()); - - // If a global shuffle is used for CoNLL2000, it will inject a shuffle op over the CoNLL2000. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset CoNLL2000's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(CoNLL2000Op::CountAllFileRows(sorted_dataset_files, &num_rows)); - - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - conll2000_op->SetTotalRepeats(GetTotalRepeats()); - conll2000_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add CoNLL2000Op. - node_ops->push_back(conll2000_op); - - return Status::OK(); -} - -// Get the shard id of node. -Status CoNLL2000Node::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size. -Status CoNLL2000Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(CoNLL2000Op::CountAllFileRows(conll2000_file_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CoNLL2000Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// CoNLL2000 by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status CoNLL2000Node::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this CoNLL2000 node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the CoNLL2000 node need to be reset to its defaults so -// that this CoNLL2000 node will produce the full set of data into the cache. -Status CoNLL2000Node::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector CoNLL2000Node::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector conll2000_file_list; - Path train_prefix("train.txt"); - Path test_prefix("test.txt"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - conll2000_file_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - conll2000_file_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - conll2000_file_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - conll2000_file_list.push_back(temp_path1.ToString()); - } - return conll2000_file_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h deleted file mode 100644 index ba1f8ef32..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/conll2000_node.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CONLL2000_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CONLL2000_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class CoNLL2000Node. -/// \brief A Dataset derived class to represent CoNLL2000 dataset. -class CoNLL2000Node : public NonMappableSourceNode { - public: - /// \brief Constructor. - CoNLL2000Node(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~CoNLL2000Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "CoNLL2000Node"; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - int64_t NumSamples() const { return num_samples_; } - - /// \brief Getter functions. - int32_t NumShards() const { return num_shards_; } - - /// \brief Getter functions. - int32_t ShardId() const { return shard_id_; } - - /// \brief Getter functions. - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief CoNLL2000 by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this CoNLL2000 node, then the cache will be - /// executing a sampler for fetching the data. As such, any options in the CoNLL2000 node need to be - /// reset to its defaults so that this CoNLL2000 node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \Read all files in the directory. - /// \param[in] usage Part of dataset of CoNLL2000. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return Status The status code returned. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - int32_t num_shards_; - int32_t shard_id_; - ShuffleMode shuffle_; - std::vector conll2000_file_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CONLL2000_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.cc deleted file mode 100644 index 000b3045a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.cc +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/csv_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// Constructor for CSVNode -CSVNode::CSVNode(const std::vector &csv_files, char field_delim, - const std::vector> &column_defaults, - const std::vector &column_names, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_files_(csv_files), - field_delim_(field_delim), - column_defaults_(column_defaults), - column_names_(column_names), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr CSVNode::Copy() { - auto node = std::make_shared(dataset_files_, field_delim_, column_defaults_, column_names_, num_samples_, - shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void CSVNode::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ",..." + - ",num_shards:" + std::to_string(num_shards_) + ",shard_id:" + std::to_string(shard_id_) + ")"); -} - -Status CSVNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("CSVNode", dataset_files_)); - - if (field_delim_ == '"' || field_delim_ == '\r' || field_delim_ == '\n') { - std::string err_msg = "CSVNode: The field delimiter should not be \", \\r, \\n"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateEnum("CSVDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateScalar("CSVDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("CSVDataset", num_shards_, shard_id_)); - - if (find(column_defaults_.begin(), column_defaults_.end(), nullptr) != column_defaults_.end()) { - std::string err_msg = "CSVDataset: column_default should not be null."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (!column_names_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("CSVDataset", "column_names", column_names_)); - } - - return Status::OK(); -} - -// Function to build CSVNode -Status CSVNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order - std::vector sorted_dataset_files = dataset_files_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - std::vector> column_default_list; - for (auto v : column_defaults_) { - if (v->type == CsvType::INT) { - column_default_list.push_back( - std::make_shared>(CsvOp::INT, std::dynamic_pointer_cast>(v)->value)); - } else if (v->type == CsvType::FLOAT) { - column_default_list.push_back( - std::make_shared>(CsvOp::FLOAT, std::dynamic_pointer_cast>(v)->value)); - } else if (v->type == CsvType::STRING) { - column_default_list.push_back(std::make_shared>( - CsvOp::STRING, (std::dynamic_pointer_cast>(v))->value)); - } - } - - std::shared_ptr csv_op = std::make_shared( - sorted_dataset_files, field_delim_, column_default_list, column_names_, num_workers_, num_samples_, - worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(csv_op->Init()); - - // If a global shuffle is used for CSV, it will inject a shuffle op over the CSV. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset CSV's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(CsvOp::CountAllFileRows(sorted_dataset_files, column_names_.empty(), &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - csv_op->SetTotalRepeats(GetTotalRepeats()); - csv_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(csv_op); - - return Status::OK(); -} - -// Get the shard id of node -Status CSVNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size -Status CSVNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(CsvOp::CountAllFileRows(dataset_files_, column_names_.empty(), &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status CSVNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_files"] = dataset_files_; - args["field_delim"] = std::string(1, field_delim_); - args["column_names"] = column_names_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status CSVNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_files", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "field_delim", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "column_names", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_samples", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_shards", kCSVNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_id", kCSVNode)); - std::vector dataset_files = json_obj["dataset_files"]; - std::string field_delim = json_obj["field_delim"]; - std::vector> column_defaults = {}; - std::vector column_names = json_obj["column_names"]; - int64_t num_samples = json_obj["num_samples"]; - ShuffleMode shuffle = static_cast(json_obj["shuffle"]); - int32_t num_shards = json_obj["num_shards"]; - int32_t shard_id = json_obj["shard_id"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_files, field_delim.c_str()[0], column_defaults, column_names, num_samples, - shuffle, num_shards, shard_id, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// CSV by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status CSVNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this CSV node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the CSV node need to be reset to its defaults so -// that this CSV node will produce the full set of data into the cache. -Status CSVNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h deleted file mode 100644 index c2a13df67..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CSV_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CSV_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \brief Record type for CSV -enum CsvType : uint8_t { INT = 0, FLOAT, STRING }; - -/// \brief Base class of CSV Record -class CsvBase { - public: - CsvBase() = default; - - explicit CsvBase(CsvType t) : type(t) {} - - virtual ~CsvBase() = default; - - CsvType type{INT}; -}; - -/// \brief CSV Record that can represent integer, float and string. -template -class CsvRecord : public CsvBase { - public: - CsvRecord() = default; - - CsvRecord(CsvType t, T v) : CsvBase(t), value(v) {} - - ~CsvRecord() override = default; - - T value; -}; - -class CSVNode : public NonMappableSourceNode { - public: - /// \brief Constructor - CSVNode(const std::vector &dataset_files, char field_delim, - const std::vector> &column_defaults, const std::vector &column_names, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - std::shared_ptr cache); - - /// \brief Destructor - ~CSVNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kCSVNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::vector &DatasetFiles() const { return dataset_files_; } - char FieldDelim() const { return field_delim_; } - const std::vector> &ColumnDefaults() const { return column_defaults_; } - const std::vector &ColumnNames() const { return column_names_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief CSV by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this CSV node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the CSV node need to be reset to its defaults so - /// that this CSV node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function - Status MakeSimpleProducer() override; - - private: - std::vector dataset_files_; - char field_delim_; - std::vector> column_defaults_; - std::vector column_names_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_CSV_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.cc deleted file mode 100644 index c00b10112..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.cc +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -DBpediaNode::DBpediaNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr DBpediaNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void DBpediaNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status DBpediaNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("DBpediaDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("DBpediaDataset", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateScalar("DBpediaDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("DBpediaDataset", num_shards_, shard_id_)); - RETURN_IF_NOT_OK(ValidateEnum("DBpediaDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - return Status::OK(); -} - -// Function to build DBpediaNode. -Status DBpediaNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files; - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &sorted_dataset_files)); - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - char field_delim = ','; - - std::vector column_names = {"class", "title", "content"}; - - std::vector> column_default_list; - for (auto c : column_names) { - column_default_list.push_back(std::make_shared>(DBpediaOp::STRING, "")); - } - - std::shared_ptr dbpedia_op = std::make_shared( - sorted_dataset_files, field_delim, column_default_list, column_names, num_workers_, num_samples_, - worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(dbpedia_op->Init()); - - // If a global shuffle is used for DBpedia, it will inject a shuffle op over the DBpedia. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset DBpedia's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(DBpediaOp::CountAllFileRows(sorted_dataset_files, column_names.empty(), &num_rows)); - - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - dbpedia_op->SetTotalRepeats(GetTotalRepeats()); - dbpedia_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(dbpedia_op); - - return Status::OK(); -} - -Status DBpediaNode::WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files) { - Path train_file_name("train.csv"); - Path test_file_name("test.csv"); - Path dir(dataset_dir); - if (usage == "train") { - Path file_path = dir / train_file_name; - dataset_files->push_back(file_path.ToString()); - } else if (usage == "test") { - Path file_path = dir / test_file_name; - dataset_files->push_back(file_path.ToString()); - } else { - Path file_path_1 = dir / train_file_name; - if (file_path_1.Exists()) { - dataset_files->push_back(file_path_1.ToString()); - } - Path file_path_2 = dir / test_file_name; - if (file_path_2.Exists()) { - dataset_files->push_back(file_path_2.ToString()); - } - } - return Status::OK(); -} - -// Get the shard id of node. -Status DBpediaNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size. -Status DBpediaNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - std::vector column_names = {"class", "title", "content"}; - std::vector dataset_files; - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &dataset_files)); - RETURN_IF_NOT_OK(DBpediaOp::CountAllFileRows(dataset_files, column_names.empty(), &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status DBpediaNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// DBpedia by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status DBpediaNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this DBpedia node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the DBpedia node need to be reset to its defaults so -// that this DBpedia node will produce the full set of data into the cache. -Status DBpediaNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h deleted file mode 100644 index 599ee5544..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DBPEDIA_NODE_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DBPEDIA_NODE_H - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/dbpedia_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class DBpediaNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - DBpediaNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~DBpediaNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kDBpediaNode; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of YahooAnswers. - /// \param[in] dataset_files List of filepaths for the dataset files - /// \return Status of the function. - Status WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files); - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief DBpedia by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this DBpedia node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the DBpedia node need to be reset to its defaults so - /// that this DBpedia node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DBPEDIA_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.cc deleted file mode 100644 index 333e1c624..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.cc +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/div2k_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for DIV2KNode -DIV2KNode::DIV2KNode(const std::string &dataset_dir, const std::string &usage, const std::string &downgrade, - int32_t scale, bool decode, const std::shared_ptr &sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - downgrade_(downgrade), - scale_(scale), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr DIV2KNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, downgrade_, scale_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void DIV2KNode::Print(std::ostream &out) const { - out << Name() + "(dataset dir:" + dataset_dir_; - out << ", usage:" + usage_ << ", scale:" + std::to_string(scale_) << ", downgrade:" + downgrade_; - if (sampler_ != nullptr) { - out << ", sampler"; - } - if (cache_ != nullptr) { - out << ", cache"; - } - out << ")"; -} - -Status DIV2KNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("DIV2KNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("DIV2KNode", usage_, {"train", "valid", "all"})); - RETURN_IF_NOT_OK(ValidateStringValue("DIV2KNode", downgrade_, {"bicubic", "unknown", "mild", "difficult", "wild"})); - RETURN_IF_NOT_OK(ValidateDatasetSampler("DIV2KNode", sampler_)); - - std::set scale_arr = {2, 3, 4, 8}; - auto s_it = scale_arr.find(scale_); - if (s_it == scale_arr.end()) { - std::string err_msg = "DIV2KNode: " + std::to_string(scale_) + " does not match any mode in [2, 3, 4, 8]."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (scale_ == 8 && downgrade_ != "bicubic") { - std::string err_msg = "DIV2KNode: scale equal to 8 is allowed only in bicubic downgrade."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::set downgrade_2018 = {"mild", "difficult", "wild"}; - auto it = downgrade_2018.find(downgrade_); - if (it != downgrade_2018.end() && scale_ != 4) { - std::string err_msg = "DIV2KNode: " + downgrade_ + " downgrade requires scale equal to 4."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Function to build DIV2KOp for DIV2K -Status DIV2KNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("hr_image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("lr_image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto div2k_op = std::make_shared(num_workers_, dataset_dir_, usage_, downgrade_, scale_, decode_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - div2k_op->SetTotalRepeats(GetTotalRepeats()); - div2k_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(div2k_op); - return Status::OK(); -} - -// Get the shard id of node -Status DIV2KNode::GetShardId(int32_t *shard_id) { - *shard_id = static_cast(sampler_->ShardId()); - return Status::OK(); -} - -// Get Dataset size -Status DIV2KNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(DIV2KOp::CountTotalRows(dataset_dir_, usage_, downgrade_, scale_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status DIV2KNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["downgrade"] = downgrade_; - args["scale"] = scale_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h deleted file mode 100644 index c588e2fa5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/div2k_node.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DIV2K_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DIV2K_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class DIV2KNode : public MappableSourceNode { - public: - /// \brief Constructor. - DIV2KNode(const std::string &dataset_dir, const std::string &usage, const std::string &downgrade, int32_t scale, - bool decode, const std::shared_ptr &sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~DIV2KNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kDIV2KNode; } - - /// \brief Print the description. - /// \param[out] out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - const int32_t &Scale() const { return scale_; } - - /// \brief Getter functions. - const std::string &Downgrade() const { return downgrade_; } - - /// \brief Getter functions - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::string downgrade_; - int32_t scale_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_DIV2K_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.cc deleted file mode 100644 index 731ec0b46..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.cc +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/emnist_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -EMnistNode::EMnistNode(const std::string &dataset_dir, const std::string &name, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), name_(name), usage_(usage), sampler_(sampler) {} - -std::shared_ptr EMnistNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, name_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void EMnistNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status EMnistNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("EMnistNode", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("EMnistNode", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("EMnistNode", usage_, {"train", "test", "all"})); - - RETURN_IF_NOT_OK( - ValidateStringValue("EMnistNode", name_, {"byclass", "bymerge", "balanced", "letters", "digits", "mnist"})); - - return Status::OK(); -} - -Status EMnistNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(name_, usage_, num_workers_, dataset_dir_, connector_que_size_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status EMnistNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status EMnistNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(EMnistOp::CountTotalRows(dataset_dir_, name_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status EMnistNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["name"] = name_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h deleted file mode 100644 index 7680ccb37..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/emnist_node.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EMNIST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EMNIST_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class EMnistNode : public MappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Dataset directory of emnist. - /// \param[in] name Class of this dataset, can be "byclass", "bymerge", "balanced", "letters", "digits", "mnist". - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] sampler Tells EMnistOp what to read. - /// \param[in] cache Tensor cache to use. - EMnistNode(const std::string &dataset_dir, const std::string &name, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~EMnistNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "EMnistNode"; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - /// \return Dataset direction. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - /// \return Usage. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - /// \return Name. - const std::string &GetName() const { return name_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Tells EMnistOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string name_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EMNIST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.cc deleted file mode 100644 index b546476a3..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.cc +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/en_wik9_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for EnWik9Node -EnWik9Node::EnWik9Node(const std::string &dataset_dir, int32_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache) - : NonMappableSourceNode(cache), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - dataset_dir_(dataset_dir) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); - DirToPath(dataset_dir_); -} - -std::shared_ptr EnWik9Node::Copy() { - auto node = std::make_shared(dataset_dir_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void EnWik9Node::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status EnWik9Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("EnWik9Dataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateEnum("EnWik9Dataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateScalar("EnWik9Dataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("EnWik9Dataset", num_shards_, shard_id_)); - - return Status::OK(); -} - -void EnWik9Node::DirToPath(const std::string &dataset_dir) { - Path train_prefix("enwik9"); - Path dir(dataset_dir); - Path temp_path = dir / train_prefix; - src_target_file_list_.push_back(temp_path.ToString()); -} - -// Function to build EnWik9Node -Status EnWik9Node::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - - // Create and initialize EnWik9Op - std::shared_ptr en_wik9_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - src_target_file_list_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(en_wik9_op->Init()); - - // If a global shuffle is used for EnWik9, it will inject a shuffle op over the EnWik9. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset EnWik9's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(EnWik9Op::CountAllFileRows(src_target_file_list_, &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(src_target_file_list_.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - en_wik9_op->SetTotalRepeats(GetTotalRepeats()); - en_wik9_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add EnWik9Op - node_ops->push_back(en_wik9_op); - return Status::OK(); -} - -// Get the shard id of node -Status EnWik9Node::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size -Status EnWik9Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(EnWik9Op::CountAllFileRows(src_target_file_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status EnWik9Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// EnWik9 by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status EnWik9Node::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this EnWik9 node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the EnWik9 node need to be reset to its defaults so -// that this EnWik9 node will produce the full set of data into the cache. -Status EnWik9Node::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h deleted file mode 100644 index 0e3997482..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EN_WIK9_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EN_WIK9_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class EnWik9Node. -/// \brief A Dataset derived class to represent EnWik9 dataset. -class EnWik9Node : public NonMappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] dataset_dir The directory of dataset. - /// \param[in] num_samples The number of samples that users want to get. - /// \param[in] shuffle Decide the dataset shuffle pattern. - /// \param[in] num_shards The number of shards that users want to part. - /// \param[in] shard_id The id of shard. - /// \param[in] cache Tensor cache to use. - EnWik9Node(const std::string &dataset_dir, int32_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~EnWik9Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kEnWik9Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id Id of this shard. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - /// \return Directory of dataset. - const std::string &DatasetDir() const { return dataset_dir_; } - - // \brief Getter functions. - /// \return The number of samples. - int32_t NumSamples() const { return num_samples_; } - - // \brief Getter functions. - /// \return The number of shards. - int32_t NumShards() const { return num_shards_; } - - // \brief Getter functions. - /// \return Id of shard. - int32_t ShardId() const { return shard_id_; } - - // \brief Getter functions. - /// \return Shuffle pattern. - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief EnWik9 by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this EnWik9 node, then the cache will be executing. - /// a sampler for fetching the data. As such, any options in the EnWik9 node need to be reset to its defaults - /// so that this EnWik9 node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Change file's directory into file's path, and put it into a list. - /// \param[in] dataset_dir Directory of enwik9 dataset. - /// \return A list of read file names. - void DirToPath(const std::string &dataset_dir); - - private: - std::string dataset_dir_; // dataset of file. - int32_t num_samples_; // the number of samples. - int32_t num_shards_; // the number of shards. - int32_t shard_id_; // the id of shard. - ShuffleMode shuffle_; // a object of ShuffleMode, which belongs to num. - std::vector src_target_file_list_; // file list; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_EN_WIK9_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.cc deleted file mode 100644 index aa99fb037..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.cc +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/fake_image_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -FakeImageNode::FakeImageNode(int32_t num_images, const std::vector &image_size, int32_t num_classes, - int32_t base_seed, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - num_images_(num_images), - image_size_(image_size), - num_classes_(num_classes), - base_seed_(base_seed), - sampler_(sampler) {} - -std::shared_ptr FakeImageNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(num_images_, image_size_, num_classes_, base_seed_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void FakeImageNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status FakeImageNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("FakeImageDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateScalar("FakeImageDataset", "num_images", num_images_, {0}, true)); - - if (image_size_.size() != 3) { - std::string err_msg = "FakeImageDataset: 'image_size' expecting size 3, but got image_size.size(): " + - std::to_string(image_size_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - for (auto i = 0; i < 3; i++) { - RETURN_IF_NOT_OK( - ValidateScalar("FakeImageDataset", "image_size[" + std::to_string(i) + "]", image_size_[i], {0}, true)); - } - - RETURN_IF_NOT_OK(ValidateScalar("FakeImageDataset", "num_classes", num_classes_, {0}, true)); - return Status::OK(); -} - -Status FakeImageNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_images_, image_size_, num_classes_, base_seed_, num_workers_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status FakeImageNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status FakeImageNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - num_rows = num_images_; - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status FakeImageNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["num_images"] = num_images_; - args["image_size"] = image_size_; - args["num_classes"] = num_classes_; - args["base_seed"] = base_seed_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h deleted file mode 100644 index 1814c9b37..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fake_image_node.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FAKE_IMAGE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FAKE_IMAGE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class FakeImageNode : public MappableSourceNode { - public: - /// \brief Constructor. - FakeImageNode(int32_t num_images, const std::vector &image_size, int32_t num_classes, int32_t base_seed, - std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~FakeImageNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "FakeImageNode"; } - - /// \brief Print the description. - /// \param[in] out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id - The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter - Shared pointer to DatasetSizeGetter. - /// \param[in] estimate - This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size - The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::vector &ImageSize() const { return image_size_; } - int32_t NumImages() const { return num_images_; } - int32_t NumClasses() const { return num_classes_; } - int32_t BaseSeed() const { return base_seed_; } - - /// \brief Get the arguments of node. - /// \param[out] - out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - int32_t num_images_; - std::vector image_size_; - int32_t num_classes_; - int32_t base_seed_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FAKE_IMAGE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.cc deleted file mode 100644 index e1f0f00d9..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.cc +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/fashion_mnist_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -FashionMnistNode::FashionMnistNode(const std::string &dataset_dir, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr FashionMnistNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void FashionMnistNode::Print(std::ostream &out) const { out << Name(); } - -Status FashionMnistNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("FashionMnistNode", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("FashionMnistNode", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("FashionMnistNode", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -Status FashionMnistNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(usage_, num_workers_, dataset_dir_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status FashionMnistNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status FashionMnistNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(FashionMnistOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status FashionMnistNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h deleted file mode 100644 index 1861cb49e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FASHION_MNIST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FASHION_MNIST_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class FashionMnistNode : public MappableSourceNode { - public: - /// \brief Constructor. - FashionMnistNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~FashionMnistNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kFashionMnistNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting. - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FASHION_MNIST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.cc deleted file mode 100644 index d52223fc8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/flickr_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for FlickrNode -FlickrNode::FlickrNode(const std::string &dataset_dir, const std::string &annotation_file, bool decode, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - annotation_file_(annotation_file), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr FlickrNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, annotation_file_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void FlickrNode::Print(std::ostream &out) const { - out << Name() + "(dataset dir:" + dataset_dir_; - out << ", annotation file:" + annotation_file_; - if (sampler_ != nullptr) { - out << ", sampler"; - } - if (cache_ != nullptr) { - out << ", cache"; - } - out << ")"; -} - -Status FlickrNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("FlickrDataset", dataset_dir_)); - - if (annotation_file_.empty()) { - std::string err_msg = "FlickrDataset: 'annotation_file' is not specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - std::vector forbidden_symbols = {':', '*', '?', '"', '<', '>', '|', '`', '&', '\'', ';'}; - for (char c : annotation_file_) { - auto p = std::find(forbidden_symbols.begin(), forbidden_symbols.end(), c); - if (p != forbidden_symbols.end()) { - std::string err_msg = - "FlickrDataset: 'annotation_file': [" + annotation_file_ + "] should not contain :*?\"<>|`&;\'."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("FlickrDataset", {annotation_file_}, "annotation file")); - RETURN_IF_NOT_OK(ValidateDatasetSampler("FlickrDataset", sampler_)); - return Status::OK(); -} - -// Function to build FlickrOp for Flickr -Status FlickrNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("annotation", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto flickr_op = std::make_shared(num_workers_, dataset_dir_, annotation_file_, decode_, - connector_que_size_, std::move(schema), std::move(sampler_rt)); - flickr_op->SetTotalRepeats(GetTotalRepeats()); - flickr_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(flickr_op); - return Status::OK(); -} - -// Get the shard id of node -Status FlickrNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size -Status FlickrNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(FlickrOp::CountTotalRows(dataset_dir_, annotation_file_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status FlickrNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["annotation_file"] = annotation_file_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status FlickrNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kFlickrNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kFlickrNode)); - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("dataset_dir") != json_obj.end(), "Failed to find dataset_dir"); - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("annotation_file") != json_obj.end(), "Failed to find annotation_file"); - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("decode") != json_obj.end(), "Failed to find decode"); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string annotation_file = json_obj["annotation_file"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, annotation_file, decode, sampler, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h deleted file mode 100644 index fd9470e75..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FLICKR_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FLICKR_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class FlickrNode : public MappableSourceNode { - public: - /// \brief Constructor - FlickrNode(const std::string &dataset_dir, const std::string &annotation_file, bool decode, - std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor - ~FlickrNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kFlickrNode; } - - /// \brief Print the description - /// \param[out] out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param[out] node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions - const std::string &AnnotationFile() const { return annotation_file_; } - - /// \brief Getter functions - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string annotation_file_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FLICKR_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.cc deleted file mode 100644 index 98e1bc121..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.cc +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/food101_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Food101Node::Food101Node(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr Food101Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void Food101Node::Print(std::ostream &out) const { out << Name(); } - -Status Food101Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Food101Dataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("Food101Dataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("Food101Dataset", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -Status Food101Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - RETURN_UNEXPECTED_IF_NULL(node_ops); - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - auto op = std::make_shared(dataset_dir_, usage_, num_workers_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status Food101Node::GetShardId(int32_t *const shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status Food101Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0; - int64_t sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build Food101Op."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Food101Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - nlohmann::json sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status Food101Node::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kFood101Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kFood101Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kFood101Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kFood101Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kFood101Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kFood101Node)); - std::string dataset_dir = json_obj["dataset_dir"]; - bool decode = json_obj["decode"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, decode, sampler, cache); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h deleted file mode 100644 index d67e6d06d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/food101_node.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FOOD101_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FOOD101_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class Food101Node : public MappableSourceNode { - public: - /// \brief Constructor. - Food101Node(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~Food101Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kFood101Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id of node. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *const shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json. - /// \param[in] json_obj The JSON object to be deserialized. - /// \param[out] ds Deserialized dataset. - /// \return Status The status code returned. - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_FOOD101_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.cc deleted file mode 100644 index d33a1999f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.cc +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/repeat_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -GeneratorNode::GeneratorNode(const py::function &generator_function, const std::vector &column_names, - const std::vector &column_types, int64_t source_len, - std::shared_ptr sampler, uint32_t num_parallel_workers, - std::shared_ptr python_multiprocessing_runtime, - bool has_batch_sampler) - : MappableSourceNode(), - column_names_(column_names), - column_types_(column_types), - source_len_(source_len), - sampler_(std::move(sampler)), - num_parallel_workers_(num_parallel_workers), - python_multiprocessing_runtime_(std::move(python_multiprocessing_runtime)), - has_batch_sampler_(has_batch_sampler) { - py::gil_scoped_acquire gil_acquire; - generator_function_ = generator_function; -} - -GeneratorNode::GeneratorNode(const py::function &generator_function, const std::shared_ptr &schema, - int64_t source_len, std::shared_ptr sampler, uint32_t num_parallel_workers, - std::shared_ptr python_multiprocessing_runtime, - bool has_batch_sampler) - : MappableSourceNode(), - schema_(schema), - source_len_(source_len), - sampler_(std::move(sampler)), - num_parallel_workers_(num_parallel_workers), - python_multiprocessing_runtime_(std::move(python_multiprocessing_runtime)), - has_batch_sampler_(has_batch_sampler) { - py::gil_scoped_acquire gil_acquire; - generator_function_ = generator_function; -} - -GeneratorNode::~GeneratorNode() { - py::gil_scoped_acquire gil_acquire; - generator_function_ = py::object(); -} - -std::shared_ptr GeneratorNode::Copy() { - std::shared_ptr node; - if (schema_ == nullptr) { - node = std::make_shared(generator_function_, column_names_, column_types_, source_len_, sampler_, - num_parallel_workers_, python_multiprocessing_runtime_, has_batch_sampler_); - } else { - node = std::make_shared(generator_function_, schema_, source_len_, sampler_, num_parallel_workers_, - python_multiprocessing_runtime_, has_batch_sampler_); - } - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void GeneratorNode::Print(std::ostream &out) const { - out << (Name() + "(:" + ",columns:" + PrintColumns(column_names_) + ",) "); -} - -Status GeneratorNode::Build(std::vector> *const node_ops) { - std::unique_ptr data_schema = std::make_unique(); - - if (schema_ != nullptr) { - column_names_.clear(); - column_types_.clear(); - std::string schema_json_string = schema_->to_json(); - RETURN_IF_NOT_OK(data_schema->LoadSchemaString(schema_json_string, {})); - - for (int32_t i = 0; i < data_schema->NumColumns(); i++) { - ColDescriptor col = data_schema->Column(i); - column_names_.push_back(col.Name()); - column_types_.push_back((col.Type())); - } - } - std::shared_ptr sampler_rt = nullptr; - if (sampler_) { - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - } - - // GeneratorOp's constructor takes in a prefetch_size, which isn't being set by user nor is it being used by - // GeneratorOp internally. Here it is given a zero which is the default in generator builder - std::shared_ptr op = - std::make_shared(generator_function_, column_names_, column_types_, 0, connector_que_size_, sampler_rt, - num_parallel_workers_, has_batch_sampler_); - // set the number of rows from source length - op->SetNumRows(source_len_); - - // Add this GeneratorOp to its RepeatOp/EpochCtrlOp ancestor's EOE list. - // When the ancestor reaches an end-of-epoch boundary, it will send a "reset" signal to all the ops in the EOE list. - // The ancestor is updated by GeneratorNodePass post pass. - // Assumption: - // We build the run-time ops from IR nodes from top to bottom. Hence Repeat/EpochCtrl ancestor ops are built - // before this leaf Generator op is built. - std::shared_ptr tmp_repeat_node = reset_ancestor_.lock(); - if (tmp_repeat_node != nullptr) { - tmp_repeat_node->op_->AddToEoeList(op); - } - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - if (python_multiprocessing_runtime_ != nullptr) { - op->SetPythonMp(python_multiprocessing_runtime_); - } - node_ops->push_back(op); - return Status::OK(); -} - -// no validation is needed for generator op. -Status GeneratorNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (source_len_ == 0) { - std::string err_msg = "GeneratorNode: data row of input source must not be 0, got: " + std::to_string(source_len_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status GeneratorNode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = 0; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status GeneratorNode::Accept(IRNodePass *p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status GeneratorNode::AcceptAfter(IRNodePass *p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status GeneratorNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - if (source_len_ == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), dataset_size)); - dataset_size_ = *dataset_size; - return Status::OK(); - } else { - int64_t sample_size; - int64_t num_rows = source_len_; - std::shared_ptr sampler_rt = nullptr; - if (sampler_) { - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - } - sample_size = sampler_ ? sampler_rt->CalculateNumSamples(num_rows) : num_rows; - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h deleted file mode 100644 index 9731b3f17..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GENERATOR_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GENERATOR_NODE_H_ - -#include -#include -#include - -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/generator_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \class GeneratorNode -/// \brief A Dataset derived class to represent GeneratorNode dataset -class GeneratorNode : public MappableSourceNode { - public: - /// \brief Constructor - GeneratorNode(const py::function &generator_function, const std::vector &column_names, - const std::vector &column_types, int64_t source_len, std::shared_ptr sampler, - uint32_t num_parallel_workers, - std::shared_ptr python_multiprocessing_runtime, bool has_batch_sampler); - - /// \brief Constructor - GeneratorNode(const py::function &generator_function, const std::shared_ptr &schema, int64_t source_len, - std::shared_ptr sampler, uint32_t num_parallel_workers, - std::shared_ptr python_multiprocessing_runtime, bool has_batch_sampler); - - /// \brief Destructor - ~GeneratorNode() override; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kGeneratorNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node, is always 0 because generator_node doesn't support sharding - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - bool IsSizeDefined() override { return false; } - - /// \brief Check for batch sampler. - /// \return Contain batch sampler or not. - bool HasBatchSampler() const { return has_batch_sampler_; } - - /// \brief Record the vector of Repeat/EpochCtrl nodes that are ancestors of this node - /// \param[in] the ancestor node - /// \return Status of the function - Status AddResetAncestor(const std::shared_ptr &src) { - std::shared_ptr tmp_repeat_node = reset_ancestor_.lock(); - CHECK_FAIL_RETURN_UNEXPECTED(tmp_repeat_node == nullptr, "Internal error: Overwriting an existing value"); - reset_ancestor_ = src; - return Status::OK(); - } - - /// Returns the dataset size of GeneratorOp. If is mappable (sampler isn not null), the sampler is used. - /// Otherwise, a dry run is needed. - /// \param[in] size_getter TreeConsumer to be used for a dryrun - /// \param[in] estimate - /// \param[out] dataset_size - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const py::function &GeneratorFunction() const { return generator_function_; } - const std::vector &ColumnNames() const { return column_names_; } - const std::vector &ColumnTypes() const { return column_types_; } - const std::shared_ptr &Schema() const { return schema_; } - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - bool IsMappableSource() { return sampler_ != nullptr; } - - private: - py::function generator_function_; - std::vector column_names_; - std::vector column_types_; - std::shared_ptr schema_; - std::weak_ptr reset_ancestor_; // updated its immediate Repeat/EpochCtrl ancestor in GeneratorNodePass - std::shared_ptr sampler_; - uint32_t num_parallel_workers_; - int64_t source_len_; // Length of the dataset source provided by the user, -1 means it's unknown - std::shared_ptr python_multiprocessing_runtime_; - bool has_batch_sampler_; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *p, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GENERATOR_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.cc deleted file mode 100644 index 2d9c24759..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.cc +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/gtzan_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -GTZANNode::GTZANNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -void GTZANNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr GTZANNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -Status GTZANNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("GTZANDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("GTZANDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("GTZANDataset", usage_, {"train", "valid", "test", "all"})); - return Status::OK(); -} - -Status GTZANNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT64), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_label = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_label))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - auto op = std::make_shared(usage_, num_workers_, dataset_dir_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node. -Status GTZANNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status GTZANNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(GTZANOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status GTZANNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h deleted file mode 100644 index 1f239b738..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/gtzan_node.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GTZAN_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GTZAN_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class GTZANNode : public MappableSourceNode { - public: - /// \brief Constructor - GTZANNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~GTZANNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "kGTZANNode"; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Tells GTZANOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_GTZAN_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.cc deleted file mode 100644 index 3758f31a4..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.cc +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/image_folder_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -#ifdef ENABLE_PYTHON -ImageFolderNode::ImageFolderNode(std::string dataset_dir, bool decode, std::shared_ptr sampler, - bool recursive, std::set extensions, - std::map class_indexing, - std::shared_ptr cache = nullptr, py::function decrypt) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - decode_(decode), - sampler_(sampler), - recursive_(recursive), - class_indexing_(class_indexing), - exts_(extensions), - decrypt_(decrypt) {} -#else -ImageFolderNode::ImageFolderNode(std::string dataset_dir, bool decode, std::shared_ptr sampler, - bool recursive, std::set extensions, - std::map class_indexing, - std::shared_ptr cache = nullptr) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - decode_(decode), - sampler_(sampler), - recursive_(recursive), - class_indexing_(class_indexing), - exts_(extensions) {} -#endif - -std::shared_ptr ImageFolderNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); -#ifdef ENABLE_PYTHON - auto node = std::make_shared(dataset_dir_, decode_, sampler, recursive_, exts_, class_indexing_, - cache_, decrypt_); -#else - auto node = - std::make_shared(dataset_dir_, decode_, sampler, recursive_, exts_, class_indexing_, cache_); -#endif - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void ImageFolderNode::Print(std::ostream &out) const { - out << (Name() + "(path:" + dataset_dir_ + ",decode:" + (decode_ ? "true" : "false") + ",...)"); -} - -Status ImageFolderNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("ImageFolderDataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("ImageFolderDataset", sampler_)); - - return Status::OK(); -} - -Status ImageFolderNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg is exist in ImageFolderOp, but not externalized (in Python API). - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - -#ifdef ENABLE_PYTHON - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, recursive_, decode_, exts_, - class_indexing_, std::move(schema), std::move(sampler_rt), decrypt_); -#else - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, recursive_, decode_, exts_, - class_indexing_, std::move(schema), std::move(sampler_rt)); -#endif - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status ImageFolderNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status ImageFolderNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - RETURN_IF_NOT_OK(ImageFolderOp::CountRowsAndClasses(dataset_dir_, exts_, &num_rows, nullptr, {})); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status ImageFolderNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["recursive"] = recursive_; - args["decode"] = decode_; - args["extensions"] = exts_; - args["class_indexing"] = class_indexing_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ImageFolderNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "recursive", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "extensions", kImageFolderNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "class_indexing", kImageFolderNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - bool recursive = json_obj["recursive"]; - std::set extension = json_obj["extensions"]; - std::map class_indexing; - nlohmann::json class_map = json_obj["class_indexing"]; - for (const auto &class_map_child : class_map) { - std::string class_ = class_map_child[0]; - int32_t indexing = class_map_child[1]; - class_indexing.insert({class_, indexing}); - } - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, decode, sampler, recursive, extension, class_indexing, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h deleted file mode 100644 index 3d5817aa1..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMAGE_FOLDER_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMAGE_FOLDER_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class ImageFolderNode -/// \brief A Dataset derived class to represent ImageFolder dataset -class ImageFolderNode : public MappableSourceNode { - public: -#ifdef ENABLE_PYTHON - /// \brief Constructor - ImageFolderNode(std::string dataset_dir, bool decode, std::shared_ptr sampler, bool recursive, - std::set extensions, std::map class_indexing, - std::shared_ptr cache, py::function decrypt = py::none()); -#else - /// \brief Constructor - ImageFolderNode(std::string dataset_dir, bool decode, std::shared_ptr sampler, bool recursive, - std::set extensions, std::map class_indexing, - std::shared_ptr cache); -#endif - /// \brief Destructor - ~ImageFolderNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kImageFolderNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - bool Decode() const { return decode_; } - bool Recursive() const { return recursive_; } - const std::map &ClassIndexing() const { return class_indexing_; } - const std::set &Exts() const { return exts_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - bool decode_; - bool recursive_; - std::shared_ptr sampler_; - std::map class_indexing_; - std::set exts_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMAGE_FOLDER_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.cc deleted file mode 100644 index d16c632c5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.cc +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/imdb_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -IMDBNode::IMDBNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache = nullptr) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), sampler_(sampler), usage_(usage) {} - -std::shared_ptr IMDBNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void IMDBNode::Print(std::ostream &out) const { - out << (Name() + "(path: " + dataset_dir_ + ", usage: " + usage_ + ")"); -} - -Status IMDBNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("IMDBDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("IMDBDataset", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateDatasetSampler("IMDBDataset", sampler_)); - return Status::OK(); -} - -Status IMDBNode::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - // Do internal Schema generation. - // This arg is exist in IMDBOp, but not externalized (in Python API). - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, usage_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node -Status IMDBNode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size -Status IMDBNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - RETURN_IF_NOT_OK(IMDBOp::CountRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status IMDBNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status IMDBNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_UNEXPECTED_IF_NULL(ds); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kIMDBNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kIMDBNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kIMDBNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kIMDBNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kIMDBNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, sampler, cache); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h deleted file mode 100644 index 6676a8c1e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/imdb_node.h +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMDB_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMDB_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class IMDBNode -/// \brief A Dataset derived class to represent IMDB dataset -class IMDBNode : public MappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] std::string dataset_dir - dir directory of IMDB dataset. - /// \param[in] std::string usage - the type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] std::unique_ptr sampler - sampler tells Folder what to read. - /// \param[in] cache Tensor cache to use. - IMDBNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~IMDBNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kIMDBNode; } - - /// \brief Print the description. - /// \param[out] ostream out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] DatasetOp *node_ops A vector containing shared pointer to the Dataset Ops that this object will - /// create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \param[out] int32_t *shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] DatasetSizeGetter size_getter Shared pointer to DatasetSizeGetter - /// \param[in] bool estimate This is only supported by some of the ops and it's used to speed up the process of - /// getting dataset size at the expense of accuracy. - /// \param[out] int64_t *dataset_size The size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] json *out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - /// \param[in] sampler Tells IMDBOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IMDB_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.cc deleted file mode 100644 index 1f329078c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.cc +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h" - -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -// Constructor for IWSLT2016Node. -IWSLT2016Node::IWSLT2016Node(const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, const std::string &valid_set, - const std::string &test_set, int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - language_pair_(language_pair), - valid_set_(valid_set), - test_set_(test_set), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. - // User discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work - // if the num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to - // return num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); - support_language_pair_map_["en"] = {"ar", "de", "fr", "cs"}; - support_language_pair_map_["ar"] = {"en"}; - support_language_pair_map_["fr"] = {"en"}; - support_language_pair_map_["de"] = {"en"}; - support_language_pair_map_["cs"] = {"en"}; -} - -std::shared_ptr IWSLT2016Node::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, language_pair_, valid_set_, test_set_, num_samples_, - shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void IWSLT2016Node::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status IWSLT2016Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("IWSLT2016Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("IWSLT2016Node", usage_, {"train", "valid", "test", "all"})); - const int kLanguagePairSize = 2; - if (language_pair_.size() != kLanguagePairSize) { - std::string err_msg = - "IWSLT2016Node: language_pair expecting size 2, but got: " + std::to_string(language_pair_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateMapKey("IWSLT2016Node", language_pair_[0], support_language_pair_map_)); - RETURN_IF_NOT_OK(ValidateMapValue("IWSLT2016Node", language_pair_[1], support_language_pair_map_[language_pair_[0]])); - RETURN_IF_NOT_OK(ValidateStringValue("IWSLT2016Node", valid_set_, - {"dev2010", "tst2010", "tst2011", "tst2012", "tst2013", "tst2014"})); - RETURN_IF_NOT_OK(ValidateStringValue("IWSLT2016Node", test_set_, - {"dev2010", "tst2010", "tst2011", "tst2012", "tst2013", "tst2014"})); - RETURN_IF_NOT_OK(ValidateEnum("IWSLT2016Node", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - if (num_samples_ < 0) { - std::string err_msg = "IWSLT2016Node: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("IWSLT2016Node", num_shards_, shard_id_)); - return Status::OK(); -} - -Status IWSLT2016Node::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("translation", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - - std::shared_ptr iwslt_op = std::make_shared( - num_workers_, num_samples_, worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_, - std::move(schema), IWSLTOp::IWSLTType::kIWSLT2016, dataset_dir_, usage_, language_pair_, valid_set_, test_set_); - RETURN_IF_NOT_OK(iwslt_op->Init()); - - // If a global shuffle is used for IWSLT, it will inject a shuffle op over the IWSLT. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be - // built. This is achieved in the cache transform pass where we call MakeSimpleProducer to reset IWSLT's - // shuffle option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(IWSLTOp::CountTotalRows(IWSLTOp::IWSLTType::kIWSLT2016, dataset_dir_, usage_, language_pair_, - valid_set_, test_set_, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(iwslt_op->FileNames().size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - iwslt_op->SetTotalRepeats(GetTotalRepeats()); - iwslt_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(iwslt_op); - return Status::OK(); -} - -Status IWSLT2016Node::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -Status IWSLT2016Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - - RETURN_IF_NOT_OK(IWSLTOp::CountTotalRows(IWSLTOp::IWSLTType::kIWSLT2016, dataset_dir_, usage_, language_pair_, - valid_set_, test_set_, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status IWSLT2016Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["language_pair"] = language_pair_; - args["valid_set"] = valid_set_; - args["test_set"] = test_set_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status IWSLT2016Node::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -Status IWSLT2016Node::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h deleted file mode 100644 index 9d6ec94ae..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2016_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2016_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class IWSLT2016Node. -/// \brief A Node derived class to represent IWSLT2016Node. -class IWSLT2016Node : public NonMappableSourceNode { - public: - /// \brief Constructor of IWSLT2016Node. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of IWSLT2016, can be "train", "test", "valid" or "all" data. - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] valid_set A string to identify validation set. - /// \param[in] test_set A string to identify test set. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - IWSLT2016Node(const std::string &dataset_dir, const std::string &usage, const std::vector &language_pair, - const std::string &valid_set, const std::string &test_set, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~IWSLT2016Node() override = default; - - /// \brief Node name getter. - /// \return std::string Name of the current node. - std::string Name() const override { return kIWSLT2016Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return std::shared_ptr A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status The status code returned. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - const std::vector &LanguagePair() const { return language_pair_; } - const std::string &ValidSet() const { return valid_set_; } - const std::string &TestSet() const { return test_set_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status The status code returned. - Status to_json(nlohmann::json *out_json) override; - - /// \brief IWSLT2016 by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status The status code returned. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this clue node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so - /// that this clue node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status The status code returned. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - std::vector language_pair_; - std::string valid_set_; - std::string test_set_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::map> support_language_pair_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2016_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.cc deleted file mode 100644 index f23b5c156..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.cc +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h" - -#include -#include - -#include "include/utils/common.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/iwslt_op.h" - -namespace mindspore { -namespace dataset { -// Constructor for IWSLT2017Node. -IWSLT2017Node::IWSLT2017Node(const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - language_pair_(std::move(language_pair)), - valid_set_("dev2010"), - test_set_("tst2010"), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. - // User discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work - // if the num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to - // return num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); - support_language_pair_map_["en"] = {"nl", "de", "it", "ro"}; - support_language_pair_map_["ro"] = {"de", "en", "nl", "it"}; - support_language_pair_map_["de"] = {"ro", "en", "nl", "it"}; - support_language_pair_map_["it"] = {"en", "nl", "de", "ro"}; - support_language_pair_map_["nl"] = {"de", "en", "it", "ro"}; -} - -std::shared_ptr IWSLT2017Node::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, language_pair_, num_samples_, shuffle_, num_shards_, - shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void IWSLT2017Node::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status IWSLT2017Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("IWSLT2017Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("IWSLT2017Node", usage_, {"train", "valid", "test", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("IWSLT2017Node", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - const int kLanguagePairSize = 2; - if (language_pair_.size() != kLanguagePairSize) { - std::string err_msg = - "IWSLT2017Node: language_pair expecting size 2, but got: " + std::to_string(language_pair_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateMapKey("IWSLT2017Node", language_pair_[0], support_language_pair_map_)); - RETURN_IF_NOT_OK(ValidateMapValue("IWSLT2017Node", language_pair_[1], support_language_pair_map_[language_pair_[0]])); - - if (num_samples_ < 0) { - std::string err_msg = "IWSLT2017Node: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("IWSLT2017Node", num_shards_, shard_id_)); - return Status::OK(); -} - -Status IWSLT2017Node::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("translation", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - - std::shared_ptr iwslt_op = std::make_shared( - num_workers_, num_samples_, worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_, - std::move(schema), IWSLTOp::IWSLTType::kIWSLT2017, dataset_dir_, usage_, language_pair_, valid_set_, test_set_); - RETURN_IF_NOT_OK(iwslt_op->Init()); - - // If a global shuffle is used for IWSLT, it will inject a shuffle op over the IWSLT. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be - // built.This is achieved in the cache transform pass where we call MakeSimpleProducer to reset IWSLT's - // shuffle option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(IWSLTOp::CountTotalRows(IWSLTOp::IWSLTType::kIWSLT2017, dataset_dir_, usage_, language_pair_, - valid_set_, test_set_, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(iwslt_op->FileNames().size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - iwslt_op->SetTotalRepeats(GetTotalRepeats()); - iwslt_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(iwslt_op); - return Status::OK(); -} - -Status IWSLT2017Node::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -Status IWSLT2017Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(IWSLTOp::CountTotalRows(IWSLTOp::IWSLTType::kIWSLT2017, dataset_dir_, usage_, language_pair_, - valid_set_, test_set_, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status IWSLT2017Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["language_pair"] = language_pair_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status IWSLT2017Node::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -Status IWSLT2017Node::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h deleted file mode 100644 index a9000bdb1..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.h +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2017_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2017_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class IWSLT2017Node. -/// \brief A Node derived class to represent IWSLT2017Node. -class IWSLT2017Node : public NonMappableSourceNode { - public: - /// \brief Constructor of IWSLT2017Node. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of IWSLT2017, can be "train", "test", "valid" or "all" data. - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - IWSLT2017Node(const std::string &dataset_dir, const std::string &usage, const std::vector &language_pair, - int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - std::shared_ptr cache); - - /// \brief Destructor. - ~IWSLT2017Node() override = default; - - /// \brief Node name getter. - /// \return std::string Name of the current node. - std::string Name() const override { return kIWSLT2017Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return std::shared_ptr A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status The status code returned. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - const std::vector &LanguagePair() const { return language_pair_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status The status code returned. - Status to_json(nlohmann::json *out_json) override; - - /// \brief IWSLT by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status The status code returned. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this clue node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so - /// that this clue node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status The status code returned. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - std::vector language_pair_; - std::string valid_set_; - std::string test_set_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::map> support_language_pair_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_IWSLT2017_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.cc deleted file mode 100644 index d7f60f509..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.cc +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/kitti_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for KITTINode -KITTINode::KITTINode(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache = nullptr) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr KITTINode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, decode_, sampler, cache_); - return node; -} - -void KITTINode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status KITTINode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - Path dir(dataset_dir_); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("KITTIDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("KITTIDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("KITTIDataset", usage_, {"train", "test"})); - return Status::OK(); -} - -// Function to build KITTINode -Status KITTINode::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - auto schema = std::make_unique(); - - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("image"), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - if (usage_ == "train") { - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("label"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("truncated"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("occluded"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("alpha"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("bbox"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("dimensions"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("location"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("rotation_y"), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - } - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - std::shared_ptr kitti_op; - kitti_op = std::make_shared(dataset_dir_, usage_, num_workers_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt)); - kitti_op->SetTotalRepeats(GetTotalRepeats()); - kitti_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(kitti_op); - return Status::OK(); -} - -// Get the shard id of node -Status KITTINode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status KITTINode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build KITTIOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status KITTINode::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h deleted file mode 100644 index cef9dfeb4..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kitti_node.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KITTI_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KITTI_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class KITTINode : public MappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Dataset directory of KITTI. - /// \param[in] usage Usage of this dataset, can be `train` or `test`. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Tells KITTIOp what to read. - /// \param[in] cache Tensor cache to use. - KITTINode(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Destructor. - ~KITTINode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kKITTINode; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Tells KITTIOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KITTI_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.cc deleted file mode 100644 index d0bb88acb..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.cc +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/kmnist_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -KMnistNode::KMnistNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr KMnistNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void KMnistNode::Print(std::ostream &out) const { out << Name(); } - -Status KMnistNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("KMnistNode", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("KMnistNode", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("KMnistNode", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -Status KMnistNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(usage_, num_workers_, dataset_dir_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status KMnistNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status KMnistNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(KMnistOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status KMnistNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h deleted file mode 100644 index aa5ff5650..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/kmnist_node.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KMNIST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KMNIST_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class KMnistNode : public MappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Dataset directory of kmnist. - /// \param[in] usage Usage of this dataset, can be 'train', 'test' or 'all'. - /// \param[in] sampler Tells KMnistOp what to read. - /// \param[in] cache Tensor cache to use. - KMnistNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~KMnistNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kKMnistNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[out] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_KMNIST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.cc deleted file mode 100644 index 2c012bb54..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.cc +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lfw_op.h" -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -LFWNode::LFWNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::string &image_set, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache = nullptr) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - task_(task), - usage_(usage), - image_set_(image_set), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr LFWNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, task_, usage_, image_set_, decode_, sampler, cache_); - return node; -} - -void LFWNode::Print(std::ostream &out) const { out << Name(); } - -Status LFWNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("LFWDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("LFWDataset", task_, {"people", "pairs"})); - RETURN_IF_NOT_OK(ValidateStringValue("LFWDataset", usage_, {"10fold", "train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateStringValue("LFWDataset", image_set_, {"original", "funneled", "deepfunneled"})); - RETURN_IF_NOT_OK(ValidateDatasetSampler("LFWDataset", sampler_)); - return Status::OK(); -} - -Status LFWNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg is exist in LFWOp, but not externalized (in Python API). - RETURN_UNEXPECTED_IF_NULL(node_ops); - std::unique_ptr schema = std::make_unique(); - if (task_ == "people") { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - } else if (task_ == "pairs") { - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("image1", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("image2", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - } - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("label"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_workers_, dataset_dir_, task_, usage_, image_set_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node -Status LFWNode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status LFWNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "[Internal ERROR] Unable to build LFWOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status LFWNode::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["dataset_dir"] = dataset_dir_; - args["task"] = task_; - args["usage"] = usage_; - args["image_set"] = image_set_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status LFWNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_UNEXPECTED_IF_NULL(ds); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "task", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "image_set", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kTFRecordNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string task = json_obj["task"]; - std::string usage = json_obj["usage"]; - std::string image_set = json_obj["image_set"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, task, usage, image_set, decode, sampler, cache); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h deleted file mode 100644 index 1de38a68c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lfw_node.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LFW_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LFW_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class LFWNode -/// \brief A Dataset derived class to represent LFW dataset. -class LFWNode : public MappableSourceNode { - public: - /// \brief Constructor - /// \param[in] dataset_dir Dataset directory of LFW. - /// \param[in] task Set the task type of reading LFW, can be `people` or `pairs`. - /// \param[in] usage The image split to use, can be `train`, `test', `all' or `10fold`. - /// \param[in] image_set Image set of image funneling to use, can be `original`, `funneled' or `deepfunneled`. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Tells KMnistOp what to read. - /// \param[in] cache Tensor cache to use. - LFWNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::string &image_set, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor. - ~LFWNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kLFWNode; } - - /// \brief Print the description - /// \param out The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Task() const { return task_; } - const std::string &Usage() const { return usage_; } - const std::string &ImageSet() const { return image_set_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json. - /// \param[in] json_obj The JSON object to be deserialized. - /// \param[out] ds Deserialized dataset. - /// \return Status The status code returned. - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - /// \param[in] sampler The Sampler setter of the current node. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string task_; - std::string usage_; - std::string image_set_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LFW_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.cc deleted file mode 100644 index 2b97bfb8d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.cc +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/libri_tts_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -LibriTTSNode::LibriTTSNode(const std::string &dataset_dir, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -void LibriTTSNode::Print(std::ostream &out) const { out << Name(); } - -std::shared_ptr LibriTTSNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -Status LibriTTSNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("LibriTTSDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("LibriTTSDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("LibriTTSDataset", usage_, - {"dev-clean", "dev-other", "test-clean", "test-other", "train-clean-100", - "train-clean-360", "train-other-500", "all"})); - return Status::OK(); -} - -Status LibriTTSNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status LibriTTSNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(LibriTTSOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status LibriTTSNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kCv, 1))); - TensorShape scalar_rate = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_rate))); - TensorShape scalar_original_text = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("original_text", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_original_text))); - TensorShape scalar_normalized_text = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("normalized_text", DataType(DataType::DE_STRING), - TensorImpl::kFlexible, 0, &scalar_normalized_text))); - TensorShape scalar_speaker_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("speaker_id", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_speaker_id))); - TensorShape scalar_chapter_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("chapter_id", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar_chapter_id))); - TensorShape scalar_utterance_id = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("utterance_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar_utterance_id))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - auto op = std::make_shared(dataset_dir_, usage_, num_workers_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -Status LibriTTSNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h deleted file mode 100644 index 28f8fcb2d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LIBRI_TTS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LIBRI_TTS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class LibriTTSNode : public MappableSourceNode { - public: - /// \brief Constructor. - LibriTTSNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~LibriTTSNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kLibriTTSNode; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Tells LibriTTSOp what to read. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LIBRI_TTS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.cc deleted file mode 100644 index 63d17ad33..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.cc +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lj_speech_op.h" - -namespace mindspore { -namespace dataset { -// Constructor for LJSpeechNode. -LJSpeechNode::LJSpeechNode(const std::string &dataset_dir, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), sampler_(sampler) {} - -std::shared_ptr LJSpeechNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void LJSpeechNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status LJSpeechNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("LJSpeechNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("LJSpeechNode", sampler_)); - return Status::OK(); -} - -// Function to build LJSpeechOp for LJSpeech. -Status LJSpeechNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - TensorShape sample_rate_scalar = TensorShape::CreateScalar(); - TensorShape trans_scalar = TensorShape::CreateScalar(); - TensorShape nom_trans_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &sample_rate_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("transcription", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &trans_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("normalized_transcription", DataType(DataType::DE_STRING), - TensorImpl::kFlexible, 0, &nom_trans_scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto lj_speech_op = std::make_shared(dataset_dir_, num_workers_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - lj_speech_op->SetTotalRepeats(GetTotalRepeats()); - lj_speech_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(lj_speech_op); - - return Status::OK(); -} - -// Get the shard id of node. -Status LJSpeechNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status LJSpeechNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows = 0, sample_size = 0; - RETURN_IF_NOT_OK(LJSpeechOp::CountTotalRows(dataset_dir_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status LJSpeechNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h deleted file mode 100644 index ab5504f95..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LJ_SPEECH_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LJ_SPEECH_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \brief Read LJSpeech dataset. -class LJSpeechNode : public MappableSourceNode { - public: - /// \brief Constructor. - LJSpeechNode(const std::string &dataset_dir, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~LJSpeechNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kLJSpeechNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - /// \return Path string of the dataset. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LJ_SPEECH_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.cc deleted file mode 100644 index ae296bfc0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.cc +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/lsun_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -LSUNNode::LSUNNode(const std::string &dataset_dir, const std::string &usage, const std::vector &classes, - bool decode, std::shared_ptr sampler, std::shared_ptr cache = nullptr) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - classes_(classes), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr LSUNNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, classes_, decode_, sampler, cache_); - return node; -} - -void LSUNNode::Print(std::ostream &out) const { - out << (Name() + "(path: " + dataset_dir_ + ", decode: " + (decode_ ? "true" : "false") + ")"); -} - -Status LSUNNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("LSUNDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("LSUNDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("LSUNDataset", usage_, {"train", "test", "valid", "all"})); - for (auto class_name : classes_) { - RETURN_IF_NOT_OK(ValidateStringValue("LSUNDataset", class_name, - {"bedroom", "bridge", "church_outdoor", "classroom", "conference_room", - "dining_room", "kitchen", "living_room", "restaurant", "tower"})); - } - - return Status::OK(); -} - -Status LSUNNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg is exist in LSUNOp, but not externalized (in Python API). - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, usage_, classes_, decode_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - RETURN_UNEXPECTED_IF_NULL(node_ops); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node -Status LSUNNode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status LSUNNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - RETURN_IF_NOT_OK(LSUNOp::CountRowsAndClasses(dataset_dir_, usage_, classes_, &num_rows, nullptr)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status LSUNNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["classes"] = classes_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h deleted file mode 100644 index f6424d4ae..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/lsun_node.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LSUN_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LSUN_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class LSUNNode -/// \brief A Dataset derived class to represent LSUN dataset -class LSUNNode : public MappableSourceNode { - public: - /// \brief Constructor - /// \param[in] dataset_dir Dataset directory of LSUNDataset. - /// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] classes Choose specified lsun classes to load. - /// \param[in] do_decode Decode the images after reading. - /// \param[in] sampler Tells LSUNOp what to read. - /// \param[in] cache Tensor cache to use. - LSUNNode(const std::string &dataset_dir, const std::string &usage, const std::vector &classes, - bool decode, std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor - ~LSUNNode() = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kLSUNNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \param[in] share_id num of the shards of the Dataset - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - const std::vector &Classes() const { return classes_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::vector classes_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_LSUN_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.cc deleted file mode 100644 index 360ae9b58..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.cc +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/manifest_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -ManifestNode::ManifestNode(const std::string &dataset_file, const std::string &usage, - const std::shared_ptr &sampler, - const std::map &class_indexing, bool decode, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_file_(dataset_file), - usage_(usage), - decode_(decode), - class_index_(class_indexing), - sampler_(sampler) {} - -std::shared_ptr ManifestNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_file_, usage_, sampler, class_index_, decode_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void ManifestNode::Print(std::ostream &out) const { - out << (Name() + "(file:" + dataset_file_); - if (sampler_ != nullptr) { - out << ",sampler"; - } - if (cache_ != nullptr) { - out << ",cache"; - } - out << ")"; -} - -Status ManifestNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - std::vector forbidden_symbols = {':', '*', '?', '"', '<', '>', '|', '`', '&', '\'', ';'}; - std::string dataset_file_base = Path(dataset_file_).Basename(); - for (char c : dataset_file_base) { - auto p = std::find(forbidden_symbols.begin(), forbidden_symbols.end(), c); - if (p != forbidden_symbols.end()) { - std::string err_msg = - "ManifestDataset: filename of 'dataset_file' should not contain :*?\"<>|`&;\', check dataset_file: " + - dataset_file_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("ManifestDataset", {dataset_file_}, "annotation file")); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("ManifestDataset", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("ManifestDataset", usage_, {"train", "eval", "inference"})); - - return Status::OK(); -} - -Status ManifestNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - std::shared_ptr manifest_op; - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - manifest_op = std::make_shared(num_workers_, dataset_file_, connector_que_size_, decode_, class_index_, - std::move(schema), std::move(sampler_rt), usage_); - manifest_op->SetTotalRepeats(GetTotalRepeats()); - manifest_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(manifest_op); - - return Status::OK(); -} - -// Get the shard id of node -Status ManifestNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status ManifestNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "[Internal ERROR] Unable to build op."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status ManifestNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_file"] = dataset_file_; - args["usage"] = usage_; - args["class_indexing"] = class_index_; - args["decode"] = decode_; - - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ManifestNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_file", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "class_indexing", kManifestNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kManifestNode)); - std::string dataset_file = json_obj["dataset_file"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::map class_indexing; - nlohmann::json class_map = json_obj["class_indexing"]; - for (const auto &class_map_child : class_map) { - std::string class_ = class_map_child[0]; - int32_t indexing = class_map_child[1]; - class_indexing.insert({class_, indexing}); - } - bool decode = json_obj["decode"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_file, usage, sampler, class_indexing, decode, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h deleted file mode 100644 index 38e84f825..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MANIFEST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MANIFEST_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class ManifestNode : public MappableSourceNode { - public: - /// \brief Constructor - ManifestNode(const std::string &dataset_file, const std::string &usage, const std::shared_ptr &sampler, - const std::map &class_indexing, bool decode, std::shared_ptr cache); - - /// \brief Destructor - ~ManifestNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kManifestNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetFile() const { return dataset_file_; } - const std::string &Usage() const { return usage_; } - bool Decode() const { return decode_; } - const std::map &ClassIndex() const { return class_index_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \param[in] cache Dataset cache for constructor input - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_file_; - std::string usage_; - bool decode_; - std::map class_index_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MANIFEST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.cc deleted file mode 100644 index b754f4302..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.cc +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/mind_record_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/mindrecord_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -MindDataNode::MindDataNode(const std::vector &dataset_files, const std::vector &columns_list, - const std::shared_ptr &sampler, nlohmann::json padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_file_(std::string()), - dataset_files_(dataset_files), - search_for_pattern_(false), - columns_list_(columns_list), - input_sampler_(sampler), - sampler_(std::make_shared()), - padded_sample_(padded_sample), - sample_bytes_({}), - num_padded_(num_padded), - shuffle_mode_(shuffle_mode) {} - -MindDataNode::MindDataNode(const std::string &dataset_file, const std::vector &columns_list, - const std::shared_ptr &sampler, nlohmann::json padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_file_(dataset_file), - dataset_files_({}), - search_for_pattern_(true), - columns_list_(columns_list), - input_sampler_(sampler), - sampler_(std::make_shared()), - padded_sample_(padded_sample), - sample_bytes_({}), - num_padded_(num_padded), - shuffle_mode_(shuffle_mode) {} - -std::shared_ptr MindDataNode::Copy() { - std::shared_ptr node; - std::shared_ptr sampler = (input_sampler_ == nullptr) ? nullptr : input_sampler_->SamplerCopy(); - if (dataset_files_.empty()) { - node = std::make_shared(dataset_file_, columns_list_, sampler, padded_sample_, num_padded_, - shuffle_mode_, cache_); - } else { - node = std::make_shared(dataset_files_, columns_list_, sampler, padded_sample_, num_padded_, - shuffle_mode_, cache_); - } - node->SetSampleBytes(&sample_bytes_); - return node; -} - -void MindDataNode::Print(std::ostream &out) const { out << (Name() + "(file:" + dataset_file_ + ",...)"); } - -Status MindDataNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - - RETURN_IF_NOT_OK(ValidateEnum("MindDataset", "ShuffleMode", shuffle_mode_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal, ShuffleMode::kInfile, - ShuffleMode::kAdaptive, ShuffleMode::kPartial})); - - std::vector dataset_file_vec = - search_for_pattern_ ? std::vector{dataset_file_} : dataset_files_; - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("MindDataset", dataset_file_vec)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("MindDataset", input_sampler_)); - - if (!columns_list_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("MindDataset", "columns_list", columns_list_)); - } - - if (padded_sample_ != nullptr) { - if (num_padded_ < 0 || num_padded_ > INT_MAX) { - std::string err_msg = - "MindDataset: 'num_padded' must to be between 0 and INT32_MAX, but got: " + std::to_string(num_padded_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (columns_list_.empty()) { - std::string err_msg = "MindDataset: 'padded_sample' is specified and requires 'columns_list' as well"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (std::string &column : columns_list_) { - if (padded_sample_.find(column) == padded_sample_.end()) { - std::string err_msg = - "MindDataset: " + column + " in 'columns_list' does not match any column in 'padded_sample'"; - MS_LOG(ERROR) << err_msg << ", padded_sample: " << padded_sample_; - return Status(StatusCode::kMDSyntaxError, err_msg); - } - } - } - if (num_padded_ > 0) { - if (padded_sample_ == nullptr) { - std::string err_msg = "MindDataset: num_padded is specified but padded_sample is not"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - return Status::OK(); -} - -// Helper function to create runtime sampler for minddata dataset -Status MindDataNode::BuildMindDatasetSamplerChain(const std::shared_ptr &sampler, - std::vector> *operators_, - int64_t num_padded, ShuffleMode shuffle_mode) { - std::shared_ptr op = sampler->BuildForMindDataset(); - if (op == nullptr) { - std::string err_msg = - "MindDataset: Unsupported sampler is supplied for MindDataset. Supported sampler list: " - "SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler and DistributedSampler"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - std::stack> stack_ops; - while (op != nullptr) { - // update the shuffle mode for sampler op or shuffle op - if (op->GetNumSamples() != 0 && - (op->GetShuffleMode() == ShuffleMode::kFiles || op->GetShuffleMode() == ShuffleMode::kInfile)) { - std::string err_msg = - "MindDataset: Shuffle.kFiles or Shuffle.kInfile and num_samples cannot be specified at the same time."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - auto distributed_sampler_op = std::dynamic_pointer_cast(op); - if (distributed_sampler_op && num_padded > 0) { - distributed_sampler_op->SetNumPaddedSamples(num_padded); - stack_ops.push(distributed_sampler_op); - } else { - stack_ops.push(op); - } - op = op->GetChildOp(); - } - while (!stack_ops.empty()) { - operators_->push_back(stack_ops.top()); - stack_ops.pop(); - } - return Status::OK(); -} - -// Helper function to set sample_bytes from py::byte type -void MindDataNode::SetSampleBytes(std::map *sample_bytes) { sample_bytes_ = *sample_bytes; } - -Status MindDataNode::Build(std::vector> *const node_ops) { - RETURN_IF_NOT_OK(BuildMindDatasetSamplerChain(input_sampler_, &operators_, num_padded_, shuffle_mode_)); - - std::shared_ptr sampler_rt = nullptr; - // Build the sampler IR into a runtime sampler. - // This will also create a shard reader object, saved in this node's sampler_. - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - // Now we need to acquire the newly created shard reader from this node's sampler_. - // There are two cases: - // 1. If this node is cached, now after cache transform pass, its sampler_ has already been replaced by cache lookup - // node, and we should find the shard reader from cache lookup node's sampler_. - // 2. If this node is not cached, just acquire the shard reader from this node's sampler_. - std::unique_ptr shard_reader; - if (IsDescendantOfCache()) { - auto cache_lookup_sampler = std::dynamic_pointer_cast(sampler_); - CHECK_FAIL_RETURN_UNEXPECTED(cache_lookup_sampler != nullptr, - "Internal error. MindDataNode is cached, its sampler should be cache lookup node"); - auto mr_sampler = std::dynamic_pointer_cast(cache_lookup_sampler->Sampler()); - CHECK_FAIL_RETURN_UNEXPECTED(mr_sampler != nullptr, - "Internal error. CacheLookupNode's sampler should be a MindRecordSamplerObj object"); - RETURN_IF_NOT_OK(mr_sampler->GetShardReader(&shard_reader)); - } else { - auto mindrecord_sampler = sampler_; - // In checkpoint recovery, a SkipFirstEpochSampler will be inserted, so MindRecordSampler will be its child - if (std::dynamic_pointer_cast(mindrecord_sampler) != nullptr) { - mindrecord_sampler = mindrecord_sampler->GetChild()[0]; - } - auto mr_sampler = std::dynamic_pointer_cast(mindrecord_sampler); - CHECK_FAIL_RETURN_UNEXPECTED(mr_sampler != nullptr, - "Internal error. MindDataNode's sampler should be a MindRecordSamplerObj object"); - RETURN_IF_NOT_OK(mr_sampler->GetShardReader(&shard_reader)); - } - - std::shared_ptr mindrecord_op; - // If pass a string to MindData(), it will be treated as a pattern to search for matched files, - // else if pass a vector to MindData(), it will be treated as specified files to be read - if (search_for_pattern_) { - std::vector dataset_file_vec_ = {dataset_file_}; - mindrecord_op = std::make_shared( - num_workers_, dataset_file_vec_, search_for_pattern_, connector_que_size_, columns_list_, operators_, num_padded_, - padded_sample_, sample_bytes_, shuffle_mode_, std::move(shard_reader), std::move(sampler_rt)); - } else { - mindrecord_op = std::make_shared( - num_workers_, dataset_files_, search_for_pattern_, connector_que_size_, columns_list_, operators_, num_padded_, - padded_sample_, sample_bytes_, shuffle_mode_, std::move(shard_reader), std::move(sampler_rt)); - } - - RETURN_IF_NOT_OK(mindrecord_op->Init()); - mindrecord_op->SetTotalRepeats(GetTotalRepeats()); - mindrecord_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(mindrecord_op); - - return Status::OK(); -} - -// Get the shard id of node -Status MindDataNode::GetShardId(int32_t *shard_id) { - *shard_id = input_sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status MindDataNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = -1; - std::vector> operators; - RETURN_IF_NOT_OK(BuildMindDatasetSamplerChain(input_sampler_, &operators, num_padded_, shuffle_mode_)); - - if (search_for_pattern_) { - dataset_files_ = {dataset_file_}; - } - - // The last operator is parent sampler - std::shared_ptr op = operators.back(); - RETURN_IF_NOT_OK(MindRecordOp::CountTotalRows(dataset_files_, search_for_pattern_, op, &num_rows, num_padded_)); - *dataset_size = num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status MindDataNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status MindDataNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h deleted file mode 100644 index 5b99471f7..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MINDDATA_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MINDDATA_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mindrecord_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class MindDataNode : public MappableSourceNode { - public: - /// \brief Constructor - MindDataNode(const std::vector &dataset_files, const std::vector &columns_list, - const std::shared_ptr &sampler, nlohmann::json padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, std::shared_ptr cache = nullptr); - - /// \brief Constructor - MindDataNode(const std::string &dataset_file, const std::vector &columns_list, - const std::shared_ptr &sampler, nlohmann::json padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, std::shared_ptr cache = nullptr); - - /// \brief Destructor - ~MindDataNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kMindDataNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Build sampler chain for minddata dataset - /// \return Status Status::OK() if input sampler is valid - Status BuildMindDatasetSamplerChain(const std::shared_ptr &sampler, - std::vector> *operators_, - int64_t num_padded, ShuffleMode shuffle_mode); - - /// \brief Set sample_bytes when padded_sample has py::byte value - /// \note Pybind will use this function to set sample_bytes into MindDataNode - void SetSampleBytes(std::map *sample_bytes); - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - std::string dataset_file_; // search_for_pattern_ will be true in this mode - std::vector dataset_files_; // search_for_pattern_ will be false in this mode - bool search_for_pattern_; - std::vector columns_list_; - std::shared_ptr input_sampler_; // The sampler from users input, will be used to create a set of shard - // operators. - std::shared_ptr sampler_; // An auto-created sampler, IR of runtime MindRecordSamplerRT sampler - nlohmann::json padded_sample_; - std::map sample_bytes_; // enable in python - int64_t num_padded_; - std::vector> operators_; - ShuffleMode shuffle_mode_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MINDDATA_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc deleted file mode 100644 index cc7e9f6eb..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/mnist_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -MnistNode::MnistNode(std::string dataset_dir, std::string usage, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr MnistNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void MnistNode::Print(std::ostream &out) const { out << Name(); } - -Status MnistNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("MnistDataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("MnistDataset", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("MnistDataset", usage_, {"train", "test", "all"})); - - return Status::OK(); -} - -Status MnistNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - auto op = std::make_shared(usage_, num_workers_, dataset_dir_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status MnistNode::GetShardId(int32_t *const shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status MnistNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(MnistOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status MnistNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status MnistNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kMnistNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, sampler, cache); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h deleted file mode 100644 index ed1fec94e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MNIST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MNIST_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class MnistNode : public MappableSourceNode { - public: - /// \brief Constructor - MnistNode(std::string dataset_dir, std::string usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~MnistNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kMnistNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *const shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MNIST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.cc deleted file mode 100644 index ed0e11db7..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.cc +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/multi30k_op.h" - -namespace mindspore { -namespace dataset { -Multi30kNode::Multi30kNode(const std::string &dataset_dir, const std::string &usage, - const std::vector &language_pair, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - language_pair_(language_pair), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - multi30k_files_list_(WalkAllFiles(usage, dataset_dir)) { - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -void Multi30kNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -std::shared_ptr Multi30kNode::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, language_pair_, num_samples_, shuffle_, num_shards_, - shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -// Function to build Multi30kNode -Status Multi30kNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - std::vector sorted_dataset_files = multi30k_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("translation", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - - std::shared_ptr multi30k_op = - std::make_shared(num_workers_, num_samples_, language_pair_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(multi30k_op->Init()); - - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(Multi30kOp::CountAllFileRows(sorted_dataset_files, &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - multi30k_op->SetTotalRepeats(GetTotalRepeats()); - multi30k_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add Multi30kOp - node_ops->push_back(multi30k_op); - - return Status::OK(); -} - -Status Multi30kNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Multi30kDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("Multi30kDataset", multi30k_files_list_)); - RETURN_IF_NOT_OK(ValidateStringValue("Multi30kDataset", usage_, {"train", "valid", "test", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("Multi30kDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - const int kLanguagePairSize = 2; - if (language_pair_.size() != kLanguagePairSize) { - std::string err_msg = - "Multi30kDataset: language_pair expecting size 2, but got: " + std::to_string(language_pair_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - const std::vector> support_language_pair = {{"en", "de"}, {"de", "en"}}; - if (language_pair_ != support_language_pair[0] && language_pair_ != support_language_pair[1]) { - std::string err_msg = R"(Multi30kDataset: language_pair must be {"en", "de"} or {"de", "en"}.)"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateScalar("Multi30kDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("Multi30kDataset", num_shards_, shard_id_)); - return Status::OK(); -} - -Status Multi30kNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -Status Multi30kNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(Multi30kOp::CountAllFileRows(multi30k_files_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Multi30kNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - args["language_pair"] = language_pair_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status Multi30kNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -Status Multi30kNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector Multi30kNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector multi30k_files_list; - Path train_en("training/train.en"); - Path test_en("mmt16_task1_test/test.en"); - Path valid_en("validation/val.en"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_en; - multi30k_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_en; - multi30k_files_list.push_back(temp_path.ToString()); - } else if (usage == "valid") { - Path temp_path = dir / valid_en; - multi30k_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_en; - multi30k_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_en; - multi30k_files_list.push_back(temp_path1.ToString()); - Path temp_path2 = dir / valid_en; - multi30k_files_list.push_back(temp_path2.ToString()); - } - return multi30k_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h deleted file mode 100644 index 42217dfc5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/multi30k_node.h +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MULTI30K_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MULTI30K_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class Multi30kNode : public NonMappableSourceNode { - public: - /// \brief Constructor of Multi30kNode. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MULTI30K, can be "train", "test", "valid" or "all". - /// \param[in] language_pair List containing text and translation language. - /// \param[in] num_samples The number of samples to be included in the dataset - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shared_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - Multi30kNode(const std::string &dataset_dir, const std::string &usage, const std::vector &language_pair, - int32_t num_samples, ShuffleMode shuffle, int32_t num_shards, int32_t shared_id, - std::shared_ptr cache); - - /// \brief Destructor of Multi30kNode. - ~Multi30kNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kMulti30kNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops); - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting. - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Multi30k by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this Multi30k node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the Multi30k node need to be reset to its defaults - /// so that this Multi30k node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function - Status MakeSimpleProducer() override; - - /// \brief Getter functions - int32_t NumSamples() const { return num_samples_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - ShuffleMode Shuffle() const { return shuffle_; } - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - const std::vector &LanguagePair() const { return language_pair_; } - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of Multi30k. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - std::vector language_pair_; - int32_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::vector multi30k_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_MULTI30K_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.cc deleted file mode 100644 index 7af9e38f3..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.cc +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/omniglot_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -OmniglotNode::OmniglotNode(const std::string &dataset_dir, bool background, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - background_(background), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr OmniglotNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, background_, decode_, sampler, cache_); - return node; -} - -void OmniglotNode::Print(std::ostream &out) const { - out << (Name() + "(path: " + dataset_dir_ + ", background: " + (background_ ? "true" : "false") + - ", decode: " + (decode_ ? "true" : "false") + ")"); -} - -Status OmniglotNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("OmniglotDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("OmniglotDataset", sampler_)); - return Status::OK(); -} - -Status OmniglotNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg is exist in OmniglotOp, but not externalized (in Python API). - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(num_workers_, dataset_dir_, connector_que_size_, background_, decode_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node -Status OmniglotNode::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size -Status OmniglotNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - Path dataset_path(dataset_dir_); - if (background_) { - dataset_path = dataset_path / "images_background"; - } else { - dataset_path = dataset_path / "images_evaluation"; - } - std::string path_str = dataset_path.ToString(); - - RETURN_IF_NOT_OK(OmniglotOp::CountRowsAndClasses(path_str, &num_rows, nullptr)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status OmniglotNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_UNEXPECTED_IF_NULL(out_json); - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["num_parallel_workers"] = num_workers_; - args["dataset_dir"] = dataset_dir_; - args["background"] = background_; - args["decode"] = decode_; - args["sampler"] = sampler_args; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h deleted file mode 100644 index ab87cee8f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/omniglot_node.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_OMNIGLOT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_OMNIGLOT_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class OmniglotNode -/// \brief A Dataset derived class to represent Omniglot dataset. -class OmniglotNode : public MappableSourceNode { - public: - /// \brief Constructor - OmniglotNode(const std::string &dataset_dir, bool background, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor - ~OmniglotNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kOmniglotNode; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - bool Background() const { return background_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - bool background_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_OMNIGLOT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.cc deleted file mode 100644 index 790180920..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.cc +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/penn_treebank_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for PennTreebankNode. -PennTreebankNode::PennTreebankNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - penn_treebank_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr PennTreebankNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void PennTreebankNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status PennTreebankNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("PennTreebankNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("PennTreebankNode", usage_, {"train", "test", "valid", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("PennTreebankNode", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - if (num_samples_ < 0) { - std::string err_msg = "PennTreebankNode: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateDatasetShardParams("PennTreebankNode", num_shards_, shard_id_)); - return Status::OK(); -} - -// Function to build PennTreebankNode. -Status PennTreebankNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = penn_treebank_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - // Create and initialize PennTreebankNode. - std::shared_ptr penn_treebank_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(penn_treebank_op->Init()); - // If a global shuffle is used for PennTreebank, it will inject a shuffle op over the PennTreebank. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset PennTreebank's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(PennTreebankOp::CountAllFileRows(penn_treebank_files_list_, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - penn_treebank_op->SetTotalRepeats(GetTotalRepeats()); - penn_treebank_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add PennTreebankNode. - node_ops->push_back(penn_treebank_op); - return Status::OK(); -} - -// Get the shard id of node. -Status PennTreebankNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size. -Status PennTreebankNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(PennTreebankOp::CountAllFileRows(penn_treebank_files_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status PennTreebankNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// PennTreebank by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status PennTreebankNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this PennTreebank node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the PennTreebank node need to be reset to its defaults so -// that this PennTreebank node will produce the full set of data into the cache. -Status PennTreebankNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector PennTreebankNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector penn_treebank_files_list; - Path train_prefix("ptb.train.txt"); - Path test_prefix("ptb.test.txt"); - Path valid_prefix("ptb.valid.txt"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - penn_treebank_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - penn_treebank_files_list.push_back(temp_path.ToString()); - } else if (usage == "valid") { - Path temp_path = dir / valid_prefix; - penn_treebank_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - penn_treebank_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - penn_treebank_files_list.push_back(temp_path1.ToString()); - Path temp_path2 = dir / valid_prefix; - penn_treebank_files_list.push_back(temp_path2.ToString()); - } - return penn_treebank_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h deleted file mode 100644 index c37df7690..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PENN_TREEBANK_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PENN_TREEBANK_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \brief class PennTreebankNode. -/// \brief Dataset derived class to represent PennTreebank dataset. -class PennTreebankNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - PennTreebankNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~PennTreebankNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kPennTreebankNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - int32_t NumSamples() const { return num_samples_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - ShuffleMode Shuffle() const { return shuffle_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief PennTreebank by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in - /// the tree, that cache can inherit this sampler from the leaf, providing - /// sampling support from the caching layer. That is why we setup the - /// sampler for a leaf node that does not use sampling. Note: This - /// function is common among NonMappableSourceNode and should be promoted - /// to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this PennTreebank node, - /// then the cache will be executing a sampler for fetching the data. - /// As such, any options in the PennTreebank node need to be reset to its defaults - /// so that this PennTreebank node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its - /// parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of PennTreebank. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - int32_t num_shards_; - int32_t shard_id_; - ShuffleMode shuffle_; - std::vector penn_treebank_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PENN_TREEBANK_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.cc deleted file mode 100644 index 809fc96b0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.cc +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/photo_tour_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -PhotoTourNode::PhotoTourNode(const std::string &dataset_dir, const std::string &name, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), name_(name), usage_(usage), sampler_(sampler) {} - -std::shared_ptr PhotoTourNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, name_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void PhotoTourNode::Print(std::ostream &out) const { - out << (Name() + "(name: " + name_ + ", usage: " + usage_); - if (sampler_ != nullptr) { - out << ", sampler"; - } - if (cache_ != nullptr) { - out << ", cache"; - } - out << ")"; -} - -Status PhotoTourNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("PhotoTourNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("PhotoTourNode", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("PhotoTourNode", usage_, {"train", "test"})); - RETURN_IF_NOT_OK( - ValidateStringValue("PhotoTourNode", name_, - {"notredame", "yosemite", "liberty", "notredame_harris", "yosemite_harris", "liberty_harris"})); - - return Status::OK(); -} - -Status PhotoTourNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - if (usage_ == "train") { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - } else { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image1", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image2", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("matches", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - } - - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, name_, usage_, num_workers_, connector_que_size_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status PhotoTourNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status PhotoTourNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(PhotoTourOp::CountTotalRows(dataset_dir_, name_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status PhotoTourNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["name"] = name_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h deleted file mode 100644 index b69c9efb8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PHOTO_TOUR_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PHOTO_TOUR_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class PhotoTourNode : public MappableSourceNode { - public: - /// \brief Constructor. - PhotoTourNode(const std::string &dataset_dir, const std::string &name, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~PhotoTourNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kPhotoTourNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id - The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed - /// up the process of getting dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &GetName() const { return name_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler - Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string name_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PHOTO_TOUR_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.cc deleted file mode 100644 index 9700e8c42..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.cc +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/places365_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Places365Node::Places365Node(const std::string &dataset_dir, const std::string &usage, bool small, bool decode, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - small_(small), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr Places365Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, small_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void Places365Node::Print(std::ostream &out) const { out << Name(); } - -Status Places365Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("Places365Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("Places365Node", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("Places365Node", usage_, {"train-standard", "train-challenge", "val"})); - - return Status::OK(); -} - -Status Places365Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, usage_, small_, decode_, num_workers_, connector_que_size_, - std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status Places365Node::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size. -Status Places365Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(Places365Op::CountTotalRows(dataset_dir_, usage_, small_, decode_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status Places365Node::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["small"] = small_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h deleted file mode 100644 index d34fc7656..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/places365_node.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PLACES365_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PLACES365_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class Places365Node : public MappableSourceNode { - public: - /// \brief Constructor. - Places365Node(const std::string &dataset_dir, const std::string &usage, bool small, bool decode, - std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~Places365Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kPlaces365Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed - /// up the process of getting dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - const bool Small() const { return small_; } - const bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler - Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool small_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_PLACES365_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.cc deleted file mode 100644 index fb3775613..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.cc +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/qmnist_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -QMnistNode::QMnistNode(const std::string &dataset_dir, const std::string &usage, bool compat, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - compat_(compat), - sampler_(sampler) {} - -std::shared_ptr QMnistNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, compat_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void QMnistNode::Print(std::ostream &out) const { - out << (Name() + "(dataset dir: " + dataset_dir_ + ", usage: " + usage_ + - ", compat: " + (compat_ ? "true" : "false") + ", cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status QMnistNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("QMnistDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("QMnistDataset", sampler_)); - RETURN_IF_NOT_OK( - ValidateStringValue("QMnistDataset", usage_, {"train", "test", "test10k", "test50k", "nist", "all"})); - return Status::OK(); -} - -Status QMnistNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - if (compat_) { - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - } else { - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - } - - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, usage_, compat_, std::move(schema), std::move(sampler_rt), - num_workers_, connector_que_size_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status QMnistNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status QMnistNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(QMnistOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status QMnistNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["compat"] = compat_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status QMnistNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kQMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kQMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kQMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kQMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "compat", kQMnistNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kQMnistNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - bool compat = json_obj["compat"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, compat, sampler, cache); - (void)(*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - (void)(*ds)->SetConnectorQueueSize(json_obj["connector_queue_size"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h deleted file mode 100644 index f291b17ee..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/qmnist_node.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_QMNIST_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_QMNIST_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class QMnistNode : public MappableSourceNode { - public: - /// \brief Constructor. - QMnistNode(const std::string &dataset_dir, const std::string &usage, bool compat, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~QMnistNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kQMnistNode; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - const bool Compat() const { return compat_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool compat_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_QMNIST_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.cc deleted file mode 100644 index 94120588d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.cc +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/random_data_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -std::shared_ptr RandomNode::Copy() { - std::shared_ptr node; - if (schema_ != nullptr) { - node = std::make_shared(total_rows_, schema_, columns_list_, cache_); - } else { - node = std::make_shared(total_rows_, schema_path_, columns_list_, cache_); - } - return node; -} - -void RandomNode::Print(std::ostream &out) const { - out << (Name() + "(num_row:" + std::to_string(total_rows_) + ",...)"); -} - -// ValidateParams for RandomNode -Status RandomNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateScalar("RandomDataset", "total_rows", total_rows_, {0}, false)); - - if (!columns_list_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetColumnParam("RandomDataset", "columns_list", columns_list_)); - } - - // allow total_rows == 0 for now because RandomOp would generate a random row when it gets a 0 - CHECK_FAIL_RETURN_UNEXPECTED(total_rows_ == 0 || total_rows_ >= num_workers_, - "RandomDataset needs 'total_rows' >= 'num_workers', total_rows=" + - std::to_string(total_rows_) + ", num_workers=" + std::to_string(num_workers_) + "."); - - return Status::OK(); -} - -int32_t RandomNode::GenRandomInt(int32_t min, int32_t max) { - std::uniform_int_distribution uniDist(min, max); - return uniDist(rand_gen_); -} - -// Build for RandomNode -Status RandomNode::Build(std::vector> *const node_ops) { - rand_gen_.seed(GetSeed()); // seed the random generator - // If total rows was not given, then randomly pick a number - std::shared_ptr schema_obj; - if (!schema_path_.empty()) { - schema_obj = Schema(schema_path_); - if (schema_obj == nullptr) { - std::string err_msg = "RandomDataset: Invalid schema path, check schema path:" + schema_path_; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - std::string schema_json_string, schema_file_path; - if (schema_ != nullptr) { - schema_->set_dataset_type("Random"); - if (total_rows_ != 0) { - schema_->set_num_rows(total_rows_); - } - schema_json_string = schema_->to_json(); - } else { - schema_file_path = schema_path_; - } - - std::vector columns_to_load; - if (columns_list_.size() > 0) { - columns_to_load = columns_list_; - } - if (!schema_file_path.empty() || !schema_json_string.empty()) { - data_schema_ = std::make_unique(); - if (!schema_file_path.empty()) { - RETURN_IF_NOT_OK(data_schema_->LoadSchemaFile(schema_file_path, columns_to_load)); - } else if (!schema_json_string.empty()) { - RETURN_IF_NOT_OK(data_schema_->LoadSchemaString(schema_json_string, columns_to_load)); - } - } - - std::shared_ptr op; - op = std::make_shared(num_workers_, connector_que_size_, total_rows_, std::move(data_schema_)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status RandomNode::GetShardId(int32_t *const shard_id) { - // RandomDataset doesn't support multiple shards - *shard_id = 0; - return Status::OK(); -} - -// Get Dataset size -Status RandomNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = total_rows_ != 0 ? total_rows_ : data_schema_->NumRows(); - *dataset_size = num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -// RandomDataset by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status RandomNode::SetupSamplerForCache(std::shared_ptr *sampler) { - // RandomOp doesn't support sampler, should not support sharding, select sampler should just be sequential. - *sampler = SelectSampler(total_rows_, dataset::ShuffleMode::kFalse, 1, 0); - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status RandomNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status RandomNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h deleted file mode 100644 index 7ccdec94e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RANDOM_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RANDOM_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/include/dataset/samplers.h" - -namespace mindspore { -namespace dataset { -class RandomNode : public NonMappableSourceNode { - public: - // Some constants to provide limits to random generation. - static constexpr int32_t kMaxNumColumns = 4; - static constexpr int32_t kMaxRank = 4; - static constexpr int32_t kMaxDimValue = 32; - - /// \brief Constructor - RandomNode(int32_t total_rows, const std::shared_ptr &schema, const std::vector &columns_list, - const std::shared_ptr &cache) - : NonMappableSourceNode(cache), - total_rows_(total_rows), - schema_path_(""), - schema_(schema), - columns_list_(columns_list) {} - - /// \brief Constructor - RandomNode(int32_t total_rows, const std::string &schema_path, const std::vector &columns_list, - const std::shared_ptr &cache) - : NonMappableSourceNode(cache), - total_rows_(total_rows), - schema_path_(schema_path), - schema_(nullptr), - columns_list_(columns_list) {} - - /// \brief Destructor - ~RandomNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kRandomNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *const shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - int32_t TotalRows() const { return total_rows_; } - const std::string &SchemaPath() const { return schema_path_; } - const std::shared_ptr &GetSchema() const { return schema_; } - const std::vector &ColumnsList() const { return columns_list_; } - const std::mt19937 &RandGen() const { return rand_gen_; } - const std::unique_ptr &GetDataSchema() const { return data_schema_; } - - /// \brief RandomDataset by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief Random node will always produce the full set of data into the cache - /// \return Status of the function - Status MakeSimpleProducer() override { return Status::OK(); } - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - /// \brief A quick inline for producing a random number between (and including) min/max - /// \param[in] min minimum number that can be generated. - /// \param[in] max maximum number that can be generated. - /// \return The generated random number - int32_t GenRandomInt(int32_t min, int32_t max); - - int32_t total_rows_; - std::string schema_path_; - std::shared_ptr schema_; - std::vector columns_list_; - std::mt19937 rand_gen_; - std::unique_ptr data_schema_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RANDOM_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.cc deleted file mode 100644 index 8fb04d3e5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.cc +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h" - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/rendered_sst2_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const std::set kExts = {".png"}; - -RenderedSST2Node::RenderedSST2Node(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - decode_(decode), - sampler_(sampler) {} - -std::shared_ptr RenderedSST2Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, decode_, sampler, cache_); - return node; -} - -void RenderedSST2Node::Print(std::ostream &out) const { - out << (Name() + "(path: " + dataset_dir_ + ", decode: " + (decode_ ? "true" : "false") + ")"); -} - -Status RenderedSST2Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("RenderedSST2Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("RenderedSST2Node", usage_, {"val", "train", "all", "test"})); - RETURN_IF_NOT_OK(ValidateDatasetSampler("RenderedSST2Node", sampler_)); - return Status::OK(); -} - -Status RenderedSST2Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - // This arg exists in RenderedSST2Op, but is not externalized (in Python API). - RETURN_UNEXPECTED_IF_NULL(node_ops); - std::unique_ptr schema = std::make_unique(); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - const std::map kClassIndex = {}; - auto op = std::make_shared(num_workers_, dataset_dir_, usage_, connector_que_size_, decode_, kExts, - kClassIndex, std::move(schema), std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node. -Status RenderedSST2Node::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status RenderedSST2Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size; - int64_t num_rows; - RETURN_IF_NOT_OK(RenderedSST2Op::CountRowsAndClasses(dataset_dir_, usage_, kExts, &num_rows, nullptr)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - RETURN_UNEXPECTED_IF_NULL(size_getter); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status RenderedSST2Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - nlohmann::json sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RenderedSST2Node::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kRenderedSST2Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kRenderedSST2Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kRenderedSST2Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kRenderedSST2Node)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kRenderedSST2Node)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string usage = json_obj["usage"]; - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, usage, decode, sampler, cache); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h deleted file mode 100644 index d4c22c4c5..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/rendered_sst2_node.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RENDERED_SST2_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RENDERED_SST2_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/cache/dataset_cache.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class RenderedSST2Node. -/// \brief A Dataset derived class to represent RenderedSST2 dataset. -class RenderedSST2Node : public MappableSourceNode { - public: - /// \brief Constructor. - RenderedSST2Node(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::shared_ptr &sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~RenderedSST2Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kRenderedSST2Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[out] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - /// \return The dataset_dir - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - /// \return The usage - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - /// \return The Decode. - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json. - /// \param[in] json_obj The JSON object to be deserialized. - /// \param[out] ds Deserialized dataset. - /// \return Status The status code returned. - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_RENDERED_SST2_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc deleted file mode 100644 index 9db6e5a5e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -DistributedSamplerObj::DistributedSamplerObj(int64_t num_shards, int64_t shard_id, dataset::ShuffleMode shuffle_mode, - int64_t num_samples, uint32_t seed, int64_t offset, bool even_dist) - : num_shards_(num_shards), - shard_id_(shard_id), - shuffle_mode_(shuffle_mode), - num_samples_(num_samples), - seed_(seed), - offset_(offset), - even_dist_(even_dist) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -// Destructor -DistributedSamplerObj::~DistributedSamplerObj() = default; - -Status DistributedSamplerObj::ValidateParams() { - if (num_shards_ <= 0) { - RETURN_STATUS_UNEXPECTED("DistributedSampler: num_shards must be greater than 0, but got: " + - std::to_string(num_shards_)); - } - - if (shard_id_ < 0 || shard_id_ >= num_shards_) { - RETURN_STATUS_UNEXPECTED("DistributedSampler: shard_id must be in range [0, " + std::to_string(num_shards_) + - "), but got: " + std::to_string(shard_id_)); - } - - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("DistributedSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - - if (offset_ > num_shards_) { - RETURN_STATUS_UNEXPECTED("DistributedSampler: offset must be no more than num_shards(" + - std::to_string(num_shards_) + "), but got: " + std::to_string(offset_)); - } - - return Status::OK(); -} - -Status DistributedSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - if (shuffle_mode_ != dataset::ShuffleMode::kFalse && shuffle_mode_ != dataset::ShuffleMode::kGlobal) { - RETURN_STATUS_UNEXPECTED( - "The current dataset's shuffle mode should be Shuffle.FALSE or Shuffle.GLOBAL. Other " - "shuffle modes are not supported yet."); - } - bool shuffle = false; - if (shuffle_mode_ == dataset::ShuffleMode::kGlobal) { - shuffle = true; - } - - *sampler = std::make_shared(num_shards_, shard_id_, shuffle, num_samples_, seed_, - offset_, even_dist_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr DistributedSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - auto mind_sampler = std::make_shared(num_shards_, shard_id_, shuffle_mode_, seed_, - num_samples_, offset_); - return mind_sampler; -} -#endif - -Status DistributedSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "DistributedSampler"; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - args["shuffle_mode"] = shuffle_mode_; - args["seed"] = seed_; - args["offset"] = offset_; - args["num_samples"] = num_samples_; - args["even_dist"] = even_dist_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status DistributedSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, - std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_shards", "DistributedSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_id", "DistributedSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle_mode", "DistributedSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "seed", "DistributedSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "offset", "DistributedSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "even_dist", "DistributedSampler")); - int64_t num_shards = json_obj["num_shards"]; - int64_t shard_id = json_obj["shard_id"]; - dataset::ShuffleMode shuffle_mode = json_obj["shuffle_mode"]; - uint32_t seed = json_obj["seed"]; - int64_t offset = json_obj["offset"]; - bool even_dist = json_obj["even_dist"]; - *sampler = - std::make_shared(num_shards, shard_id, shuffle_mode, num_samples, seed, offset, even_dist); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -std::shared_ptr DistributedSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(num_shards_, shard_id_, shuffle_mode_, num_samples_, seed_, - offset_, even_dist_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} - -int64_t DistributedSamplerObj::ShardId() { return shard_id_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h deleted file mode 100644 index 4242acc85..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_DISTRIBUTED_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_DISTRIBUTED_SAMPLER_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class DistributedSamplerObj : public SamplerObj { - public: - DistributedSamplerObj(int64_t num_shards, int64_t shard_id, dataset::ShuffleMode shuffle_mode, int64_t num_samples, - uint32_t seed, int64_t offset, bool even_dist); - - ~DistributedSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - /// \brief Function to get the shard id of sampler - /// \return The shard id of sampler - int64_t ShardId() override; - - private: - int64_t num_shards_; - int64_t shard_id_; - dataset::ShuffleMode shuffle_mode_; - int64_t num_samples_; - uint32_t seed_; - int64_t offset_; - bool even_dist_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_DISTRIBUTED_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc deleted file mode 100644 index 35b4f518a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/pk_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -PKSamplerObj::PKSamplerObj(int64_t num_val, bool shuffle, int64_t num_samples) - : num_val_(num_val), shuffle_(shuffle), num_samples_(num_samples) {} - -// Destructor -PKSamplerObj::~PKSamplerObj() = default; - -Status PKSamplerObj::ValidateParams() { - if (num_val_ <= 0) { - RETURN_STATUS_UNEXPECTED("PKSampler: num_val must be greater than 0, but got: " + std::to_string(num_val_)); - } - - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("PKSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - return Status::OK(); -} - -Status PKSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "PKSampler"; - args["num_val"] = num_val_; - args["shuffle"] = shuffle_; - args["num_samples"] = num_samples_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status PKSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_val", "PKSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle", "PKSampler")); - int64_t num_val = json_obj["num_val"]; - bool shuffle = json_obj["shuffle"]; - *sampler = std::make_shared(num_val, shuffle, num_samples); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -Status PKSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(num_val_, shuffle_, num_samples_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr PKSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - std::shared_ptr mind_sampler; - if (shuffle_ == true) { - mind_sampler = std::make_shared("label", num_val_, std::numeric_limits::max(), - GetSeed(), num_samples_); - } else { - mind_sampler = std::make_shared("label", num_val_, num_samples_); - } - - return mind_sampler; -} -#endif - -std::shared_ptr PKSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(num_val_, shuffle_, num_samples_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h deleted file mode 100644 index 9f2ac5f7a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PK_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PK_SAMPLER_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class PKSamplerObj : public SamplerObj { - public: - PKSamplerObj(int64_t num_val, bool shuffle, int64_t num_samples); - - ~PKSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - private: - int64_t num_val_; - bool shuffle_; - int64_t num_samples_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PK_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc deleted file mode 100644 index cfe8b66b0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -PreBuiltSamplerObj::PreBuiltSamplerObj(std::shared_ptr sampler) : sp_(std::move(sampler)) {} - -// Destructor -PreBuiltSamplerObj::~PreBuiltSamplerObj() = default; - -#ifndef ENABLE_ANDROID -PreBuiltSamplerObj::PreBuiltSamplerObj(std::shared_ptr sampler) - : sp_minddataset_(std::move(sampler)) {} -#endif - -Status PreBuiltSamplerObj::ValidateParams() { return Status::OK(); } - -Status PreBuiltSamplerObj::SamplerBuild(std::shared_ptr *const sampler) { - Status s = BuildChildren(&sp_); - if (s.IsOk()) { - *sampler = sp_; - } else { - *sampler = nullptr; - } - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr PreBuiltSamplerObj::BuildForMindDataset() { return sp_minddataset_; } -#endif - -std::shared_ptr PreBuiltSamplerObj::SamplerCopy() { -#ifndef ENABLE_ANDROID - if (sp_minddataset_ != nullptr) { - auto sampler = std::make_shared(sp_minddataset_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; - } -#endif - auto sampler = std::make_shared(sp_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} - -Status PreBuiltSamplerObj::to_json(nlohmann::json *const out_json) { - RETURN_IF_NOT_OK(sp_->to_json(out_json)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h deleted file mode 100644 index 6d1ddce7e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PREBUILT_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PREBUILT_SAMPLER_IR_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class PreBuiltSamplerObj : public SamplerObj { - public: - explicit PreBuiltSamplerObj(std::shared_ptr sampler); - -#ifndef ENABLE_ANDROID - explicit PreBuiltSamplerObj(std::shared_ptr sampler); -#endif - - ~PreBuiltSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *const sampler) override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - std::shared_ptr SamplerCopy() override; - - Status ValidateParams() override; - - Status to_json(nlohmann::json *const out_json) override; - - private: - std::shared_ptr sp_; -#ifndef ENABLE_ANDROID - std::shared_ptr sp_minddataset_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_PREBUILT_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc deleted file mode 100644 index 84905d190..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/random_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -RandomSamplerObj::RandomSamplerObj(bool replacement, int64_t num_samples, bool reshuffle_each_epoch, - dataset::ShuffleMode shuffle_mode) - : replacement_(replacement), - num_samples_(num_samples), - reshuffle_each_epoch_(reshuffle_each_epoch), - shuffle_mode_(shuffle_mode) {} - -// Destructor -RandomSamplerObj::~RandomSamplerObj() = default; - -Status RandomSamplerObj::ValidateParams() { - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("RandomSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - return Status::OK(); -} - -Status RandomSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "RandomSampler"; - args["replacement"] = replacement_; - args["reshuffle_each_epoch"] = reshuffle_each_epoch_; - args["num_samples"] = num_samples_; - args["shuffle_mode"] = shuffle_mode_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RandomSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "replacement", "RandomSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "reshuffle_each_epoch", "RandomSampler")); - bool replacement = json_obj["replacement"]; - bool reshuffle_each_epoch = json_obj["reshuffle_each_epoch"]; - *sampler = std::make_shared(replacement, num_samples, reshuffle_each_epoch); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -Status RandomSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(replacement_, num_samples_, reshuffle_each_epoch_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr RandomSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - auto mind_sampler = - std::make_shared(GetSeed(), num_samples_, replacement_, reshuffle_each_epoch_); - - return mind_sampler; -} -#endif - -std::shared_ptr RandomSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(replacement_, num_samples_, reshuffle_each_epoch_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h deleted file mode 100644 index b7b9b65f0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_RANDOM_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_RANDOM_SAMPLER_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class RandomSamplerObj : public SamplerObj { - public: - RandomSamplerObj(bool replacement, int64_t num_samples, bool reshuffle_each_epoch = true, - dataset::ShuffleMode shuffle_mode = dataset::ShuffleMode::kGlobal); - - ~RandomSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - private: - bool replacement_; - int64_t num_samples_; - bool reshuffle_each_epoch_; - dataset::ShuffleMode shuffle_mode_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_RANDOM_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc deleted file mode 100644 index 18b34e03e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sampler.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { - -// Constructor -SamplerObj::SamplerObj() {} - -// Destructor -SamplerObj::~SamplerObj() = default; - -Status SamplerObj::BuildChildren(std::shared_ptr *const sampler) { - for (auto child : children_) { - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(child->SamplerBuild(&sampler_rt)); - RETURN_IF_NOT_OK((*sampler)->AddChild(sampler_rt)); - } - return Status::OK(); -} - -Status SamplerObj::AddChildSampler(std::shared_ptr child) { - if (child == nullptr) { - return Status::OK(); - } - - // Only samplers can be added, not any other DatasetOp. - std::shared_ptr sampler = std::dynamic_pointer_cast(child); - if (!sampler) { - RETURN_STATUS_UNEXPECTED("Cannot add child, child is not a sampler object."); - } - - // Samplers can have at most 1 child. - if (!children_.empty()) { - RETURN_STATUS_UNEXPECTED("Cannot add child sampler, this sampler already has a child."); - } - - children_.push_back(child); - - return Status::OK(); -} - -Status SamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - if (!children_.empty()) { - std::vector children_args; - for (auto child : children_) { - nlohmann::json child_arg; - RETURN_IF_NOT_OK(child->to_json(&child_arg)); - children_args.push_back(child_arg); - } - args["child_sampler"] = children_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status SamplerObj::from_json(nlohmann::json json_obj, std::shared_ptr *parent_sampler) { - for (nlohmann::json child : json_obj["child_sampler"]) { - std::shared_ptr child_sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(child, &child_sampler)); - (*parent_sampler)->AddChildSampler(child_sampler); - } - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h deleted file mode 100644 index b00f397d7..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SAMPLERS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SAMPLERS_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class SamplerObj { - public: - /// \brief Constructor - SamplerObj(); - - /// \brief Destructor - virtual ~SamplerObj(); - - /// \brief Pure virtual function for derived class to implement parameters validation - /// \return The Status code of the function. It returns OK status if parameters are valid. - virtual Status ValidateParams() = 0; - - /// \brief Pure virtual function to convert a SamplerObj class into a runtime sampler object - /// \param[out] sampler Shared pointers to the newly created Sampler - /// \return The Status code of the function. It returns OK status if sampler is created successfully. - virtual Status SamplerBuild(std::shared_ptr *sampler) = 0; - - /// \brief Pure virtual function to copy a SamplerObj class - /// \return Shared pointers to the newly copied SamplerObj - virtual std::shared_ptr SamplerCopy() = 0; - - /// \brief Function for derived class to get the shard id of sampler - /// \return The shard id of the derived sampler - virtual int64_t ShardId() { return 0; } - - /// \brief Adds a child to the sampler - /// \param[in] child The sampler to be added as child - /// \return the Status code returned - Status AddChildSampler(std::shared_ptr child); - - virtual Status to_json(nlohmann::json *const out_json); - -#ifndef ENABLE_ANDROID - /// \brief Function to construct children samplers - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] parent_sampler given parent sampler, output constructed parent sampler with children samplers added - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *parent_sampler); -#endif - - std::vector> GetChild() { return children_; } - -#ifndef ENABLE_ANDROID - /// \brief Virtual function to convert a SamplerObj class into a runtime mindrecord sampler object, - /// only override by SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler - /// \return Shared pointers to the newly created Sampler - virtual std::shared_ptr BuildForMindDataset() { return nullptr; } -#endif - - protected: - /// \brief A function that calls build on the children of this sampler - /// \param[in] sampler The samplerRT object built from this sampler - /// \return the Status code returned - Status BuildChildren(std::shared_ptr *const sampler); - - std::vector> children_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SAMPLERS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc deleted file mode 100644 index fd5a03117..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -SequentialSamplerObj::SequentialSamplerObj(int64_t start_index, int64_t num_samples) - : start_index_(start_index), num_samples_(num_samples) {} - -// Destructor -SequentialSamplerObj::~SequentialSamplerObj() = default; - -Status SequentialSamplerObj::ValidateParams() { - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("SequentialSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - - if (start_index_ < 0) { - RETURN_STATUS_UNEXPECTED("SequentialSampler: start_index_ must be greater than or equal to 0, but got: " + - std::to_string(start_index_)); - } - - return Status::OK(); -} - -Status SequentialSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "SequentialSampler"; - args["start_index"] = start_index_; - args["num_samples"] = num_samples_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status SequentialSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, - std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "start_index", "SequentialSampler")); - int64_t start_index = json_obj["start_index"]; - *sampler = std::make_shared(start_index, num_samples); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -Status SequentialSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(start_index_, num_samples_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr SequentialSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - auto mind_sampler = std::make_shared(num_samples_, start_index_); - - return mind_sampler; -} -#endif - -std::shared_ptr SequentialSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(start_index_, num_samples_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h deleted file mode 100644 index 00928f04b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SEQUENTIAL_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SEQUENTIAL_SAMPLER_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class SequentialSamplerObj : public SamplerObj { - public: - SequentialSamplerObj(int64_t start_index, int64_t num_samples); - - ~SequentialSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - protected: - int64_t start_index_; - int64_t num_samples_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SEQUENTIAL_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc deleted file mode 100644 index af3535f7c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -// Constructor -SkipFirstEpochSamplerObj::SkipFirstEpochSamplerObj(int64_t start_index) : SequentialSamplerObj(start_index, 0) {} - -// Destructor -SkipFirstEpochSamplerObj::~SkipFirstEpochSamplerObj() = default; - -Status SkipFirstEpochSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "SkipFirstEpochSampler"; - args["start_index"] = start_index_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status SkipFirstEpochSamplerObj::from_json(nlohmann::json json_obj, std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "start_index", "SkipFirstEpochSampler")); - int64_t start_index = json_obj["start_index"]; - *sampler = std::make_shared(start_index); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -Status SkipFirstEpochSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(start_index_, 0); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -std::shared_ptr SkipFirstEpochSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(start_index_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h deleted file mode 100644 index ddc4949ea..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SKIP_FIRST_EPOCH_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SKIP_FIRST_EPOCH_SAMPLER_IR_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h" -#include "include/api/status.h" - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class SkipFirstEpochSamplerObj : public SequentialSamplerObj { - public: - explicit SkipFirstEpochSamplerObj(int64_t start_index); - - ~SkipFirstEpochSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, std::shared_ptr *sampler); -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SKIP_FIRST_EPOCH_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc deleted file mode 100644 index 88b4e1c1c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_random_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -SubsetRandomSamplerObj::SubsetRandomSamplerObj(std::vector indices, int64_t num_samples) - : SubsetSamplerObj(std::move(indices), num_samples) {} - -// Destructor -SubsetRandomSamplerObj::~SubsetRandomSamplerObj() = default; - -Status SubsetRandomSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(indices_, num_samples_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr SubsetRandomSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - auto mind_sampler = std::make_shared(indices_, GetSeed()); - - return mind_sampler; -} -#endif - -Status SubsetRandomSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "SubsetRandomSampler"; - args["indices"] = indices_; - args["num_samples"] = num_samples_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status SubsetRandomSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, - std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "indices", "SubsetRandomSampler")); - std::vector indices = json_obj["indices"]; - *sampler = std::make_shared(indices, num_samples); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -std::shared_ptr SubsetRandomSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(indices_, num_samples_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h deleted file mode 100644 index 87605dc9d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_RANDOM_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_RANDOM_SAMPLER_IR_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class SubsetRandomSamplerObj : public SubsetSamplerObj { - public: - SubsetRandomSamplerObj(std::vector indices, int64_t num_samples); - - ~SubsetRandomSamplerObj() override; - - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - private: -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_RANDOM_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc deleted file mode 100644 index c35a93836..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/subset_sampler.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "minddata/mindrecord/include/shard_distributed_sample.h" -#include "minddata/mindrecord/include/shard_operator.h" -#include "minddata/mindrecord/include/shard_pk_sample.h" -#include "minddata/mindrecord/include/shard_sample.h" -#include "minddata/mindrecord/include/shard_sequential_sample.h" -#include "minddata/mindrecord/include/shard_shuffle.h" -#endif - -namespace mindspore { -namespace dataset { -// Constructor -SubsetSamplerObj::SubsetSamplerObj(std::vector indices, int64_t num_samples) - : indices_(std::move(indices)), num_samples_(num_samples) {} - -// Destructor -SubsetSamplerObj::~SubsetSamplerObj() = default; - -Status SubsetSamplerObj::ValidateParams() { - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("SubsetRandomSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - - return Status::OK(); -} - -Status SubsetSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - // runtime sampler object - *sampler = std::make_shared(indices_, num_samples_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -#ifndef ENABLE_ANDROID -std::shared_ptr SubsetSamplerObj::BuildForMindDataset() { - // runtime mindrecord sampler object - auto mind_sampler = std::make_shared(indices_); - - return mind_sampler; -} -#endif -Status SubsetSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "SubsetSampler"; - args["indices"] = indices_; - args["num_samples"] = num_samples_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status SubsetSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "indices", "SubsetSampler")); - std::vector indices = json_obj["indices"]; - *sampler = std::make_shared(indices, num_samples); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -std::shared_ptr SubsetSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(indices_, num_samples_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h deleted file mode 100644 index b3dfd18ec..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_SAMPLER_IR_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class SubsetSamplerObj : public SamplerObj { - public: - SubsetSamplerObj(std::vector indices, int64_t num_samples); - - ~SubsetSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - -#ifndef ENABLE_ANDROID - std::shared_ptr BuildForMindDataset() override; -#endif - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - protected: - const std::vector indices_; - int64_t num_samples_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_SUBSET_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc deleted file mode 100644 index 8e9fe9b4f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sampler/weighted_random_sampler.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" - -namespace mindspore { -namespace dataset { -// Constructor -WeightedRandomSamplerObj::WeightedRandomSamplerObj(std::vector weights, int64_t num_samples, bool replacement) - : weights_(std::move(weights)), num_samples_(num_samples), replacement_(replacement) {} - -// Destructor -WeightedRandomSamplerObj::~WeightedRandomSamplerObj() = default; - -Status WeightedRandomSamplerObj::ValidateParams() { - if (weights_.empty()) { - RETURN_STATUS_UNEXPECTED("WeightedRandomSampler: weights vector must not be empty"); - } - int32_t zero_elem = 0; - for (int32_t i = 0; i < weights_.size(); ++i) { - if (weights_[i] < 0) { - RETURN_STATUS_UNEXPECTED( - "WeightedRandomSampler: weights vector must not contain negative numbers, got: " - "weights[" + - std::to_string(i) + "] = " + std::to_string(weights_[i])); - } - if (weights_[i] == 0) { - zero_elem++; - } - } - if (zero_elem == weights_.size()) { - RETURN_STATUS_UNEXPECTED("WeightedRandomSampler: elements of weights vector must not be all zero"); - } - if (num_samples_ < 0) { - RETURN_STATUS_UNEXPECTED("WeightedRandomSampler: num_samples must be greater than or equal to 0, but got: " + - std::to_string(num_samples_)); - } - return Status::OK(); -} - -Status WeightedRandomSamplerObj::to_json(nlohmann::json *const out_json) { - nlohmann::json args; - RETURN_IF_NOT_OK(SamplerObj::to_json(&args)); - args["sampler_name"] = "WeightedRandomSampler"; - args["weights"] = weights_; - args["replacement"] = replacement_; - args["num_samples"] = num_samples_; - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status WeightedRandomSamplerObj::from_json(nlohmann::json json_obj, int64_t num_samples, - std::shared_ptr *sampler) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "weights", "WeightedRandomSampler")); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "replacement", "WeightedRandomSampler")); - std::vector weights = json_obj["weights"]; - bool replacement = json_obj["replacement"]; - *sampler = std::make_shared(weights, num_samples, replacement); - // Run common code in super class to add children samplers - RETURN_IF_NOT_OK(SamplerObj::from_json(json_obj, sampler)); - return Status::OK(); -} -#endif - -Status WeightedRandomSamplerObj::SamplerBuild(std::shared_ptr *sampler) { - *sampler = std::make_shared(weights_, num_samples_, replacement_); - Status s = BuildChildren(sampler); - sampler = s.IsOk() ? sampler : nullptr; - return s; -} - -std::shared_ptr WeightedRandomSamplerObj::SamplerCopy() { - auto sampler = std::make_shared(weights_, num_samples_, replacement_); - for (const auto &child : children_) { - Status rc = sampler->AddChildSampler(child); - if (rc.IsError()) { - MS_LOG(ERROR) << "[Internal ERROR] Error in copying the sampler. Message: " << rc; - } - } - return sampler; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h deleted file mode 100644 index 837e069a3..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_WEIGHTED_RANDOM_SAMPLER_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_WEIGHTED_RANDOM_SAMPLER_IR_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "include/api/status.h" -#ifndef ENABLE_ANDROID -#include "minddata/mindrecord/include/shard_operator.h" -#endif - -namespace mindspore { -namespace dataset { -// Internal Sampler class forward declaration -class SamplerRT; - -class WeightedRandomSamplerObj : public SamplerObj { - public: - explicit WeightedRandomSamplerObj(std::vector weights, int64_t num_samples = 0, bool replacement = true); - - ~WeightedRandomSamplerObj() override; - - Status SamplerBuild(std::shared_ptr *sampler) override; - - std::shared_ptr SamplerCopy() override; - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *const out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function for read sampler from JSON object - /// \param[in] json_obj JSON object to be read - /// \param[in] num_samples number of sample in the sampler - /// \param[out] sampler Sampler constructed from parameters in JSON object - /// \return Status of the function - static Status from_json(nlohmann::json json_obj, int64_t num_samples, std::shared_ptr *sampler); -#endif - - Status ValidateParams() override; - - private: - const std::vector weights_; - int64_t num_samples_; - bool replacement_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SAMPLERS_WEIGHTED_RANDOM_SAMPLER_IR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.cc deleted file mode 100644 index 48e53c882..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.cc +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h" - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sbu_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -SBUNode::SBUNode(const std::string &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), decode_(decode), sampler_(sampler) {} - -std::shared_ptr SBUNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SBUNode::Print(std::ostream &out) const { - out << (Name() + "(dataset dir: " + dataset_dir_ + ", decode: " + (decode_ ? "true" : "false") + - ", cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status SBUNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SBUDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("SBUDataset", sampler_)); - - Path root_dir(dataset_dir_); - - Path url_path = root_dir / Path("SBU_captioned_photo_dataset_urls.txt"); - Path caption_path = root_dir / Path("SBU_captioned_photo_dataset_captions.txt"); - Path image_path = root_dir / Path("sbu_images"); - - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("SBUDataset", {url_path.ToString()}, "url file")); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("SBUDataset", {caption_path.ToString()}, "caption file")); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SBUDataset", {image_path.ToString()})); - - return Status::OK(); -} - -Status SBUNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("caption", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, decode_, std::move(schema), std::move(sampler_rt), num_workers_, - connector_que_size_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node -Status SBUNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status SBUNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(SBUOp::CountTotalRows(dataset_dir_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SBUNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h deleted file mode 100644 index 68e9fcdd6..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sbu_node.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SBU_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SBU_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class SBUNode : public MappableSourceNode { - public: - /// \brief Constructor. - SBUNode(const std::string &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor. - ~SBUNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSBUNode; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SBU_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.cc deleted file mode 100644 index d5c1ffd53..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.cc +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/semeion_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for SemeionNode. -SemeionNode::SemeionNode(const std::string &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), sampler_(sampler) {} - -std::shared_ptr SemeionNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SemeionNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status SemeionNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SemeionNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("SemeionNode", sampler_)); - return Status::OK(); -} - -Status SemeionNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape label_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &label_scalar))); - - // Argument that is not exposed to user in the API. - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto semeion_op = std::make_shared(dataset_dir_, num_workers_, std::move(schema), std::move(sampler_rt), - connector_que_size_); - semeion_op->SetTotalRepeats(GetTotalRepeats()); - semeion_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(semeion_op); - return Status::OK(); -} - -Status SemeionNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status SemeionNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size = -1; - int64_t num_rows = 0; - - RETURN_IF_NOT_OK(SemeionOp::CountTotalRows(dataset_dir_, &num_rows)); - - // give sampler the total number of files and check if num_samples is smaller. - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - // We cache dataset size so as to not duplicated run. - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SemeionNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h deleted file mode 100644 index 1a7de56f0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/semeion_node.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SEMEION_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SEMEION_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class SemeionNode : public MappableSourceNode { - public: - /// \brief Constructor. - SemeionNode(const std::string &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor. - ~SemeionNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSemeionNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[out] shard_id Shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - /// \brief DatasetDir getter. - /// \return DatasetDir of the current node. - const std::string &DatasetDir() const { return dataset_dir_; } - - private: - std::string dataset_dir_; - std::shared_ptr sampler_; -}; // class SemeionNode -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SEMEION_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.cc deleted file mode 100644 index 5f7eb7ba0..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.cc +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -SogouNewsNode::SogouNewsNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - usage_(usage), - sogou_news_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. - // User discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work - // if the num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to - // return num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr SogouNewsNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SogouNewsNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status SogouNewsNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SogouNewsNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("SogouNewsNode", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("SogouNewsNode", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - if (num_samples_ < 0) { - std::string err_msg = "SogouNewsNode: Invalid number of samples: " + std::to_string(num_samples_); - MS_LOG(ERROR) << err_msg; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("SogouNewsNode", num_shards_, shard_id_)); - return Status::OK(); -} - -Status SogouNewsNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = sogou_news_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - std::vector> column_default; - column_default.push_back(std::make_shared>(SogouNewsOp::STRING, "")); - column_default.push_back(std::make_shared>(SogouNewsOp::STRING, "")); - column_default.push_back(std::make_shared>(SogouNewsOp::STRING, "")); - - std::vector column_name = {"index", "title", "content"}; - char field_delim = ','; - auto sogou_news_op = std::make_shared(num_workers_, num_samples_, worker_connector_size_, - connector_que_size_, shuffle_files, num_shards_, shard_id_, - field_delim, column_default, column_name, sogou_news_files_list_); - - RETURN_IF_NOT_OK(sogou_news_op->Init()); - - // If a global shuffle is used for SogouNews, it will inject a shuffle op over the SogouNews. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be - // built.This is achieved in the cache transform pass where we call MakeSimpleProducer to reset SogouNews - // shuffle option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(SogouNewsOp::CountAllFileRows(sogou_news_files_list_, false, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - sogou_news_op->SetTotalRepeats(GetTotalRepeats()); - sogou_news_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(sogou_news_op); - return Status::OK(); -} - -Status SogouNewsNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -Status SogouNewsNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(SogouNewsOp::CountAllFileRows(sogou_news_files_list_, false, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SogouNewsNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status SogouNewsNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -Status SogouNewsNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector SogouNewsNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector sogou_news_files_list; - Path train_prefix("train.csv"); - Path test_prefix("test.csv"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - sogou_news_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - sogou_news_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - if (temp_path.Exists()) { - sogou_news_files_list.push_back(temp_path.ToString()); - } - Path temp_path1 = dir / test_prefix; - if (temp_path1.Exists()) { - sogou_news_files_list.push_back(temp_path1.ToString()); - } - } - return sogou_news_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h deleted file mode 100644 index 99abfcd6f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SOGOU_NEWS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SOGOU_NEWS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sogou_news_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class SogouNewsNode -/// \brief A Node derived class to represent SogouNews Node. -class SogouNewsNode : public NonMappableSourceNode { - public: - /// \brief Constructor of SogouNewsNode. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SogouNews, can be "train", "test" or "all" data. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - SogouNewsNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~SogouNewsNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSogouNewsNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting. - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief SogouNews by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this clue node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so. - /// that this clue node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of SogouNews. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - std::vector> column_defaults_; - std::vector column_names_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::vector sogou_news_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SOGOU_NEWS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.cc deleted file mode 100644 index bec0ea591..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.cc +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/speech_commands_op.h" - -namespace mindspore { -namespace dataset { -SpeechCommandsNode::SpeechCommandsNode(const std::string &dataset_dir, const std::string &usage, - std::shared_ptr sampler, std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr SpeechCommandsNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SpeechCommandsNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status SpeechCommandsNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SpeechCommandsNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("SpeechCommandsNode", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("SpeechCommandsNode", usage_, {"train", "valid", "test", "all"})); - return Status::OK(); -} - -Status SpeechCommandsNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - TensorShape sample_rate_scalar = TensorShape::CreateScalar(); - TensorShape label_scalar = TensorShape::CreateScalar(); - TensorShape speaker_id_scalar = TensorShape::CreateScalar(); - TensorShape utterance_number_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &sample_rate_scalar))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &label_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("speaker_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &speaker_id_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("utterance_number", DataType(DataType::DE_INT32), - TensorImpl::kFlexible, 0, &utterance_number_scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto speech_commands_op = std::make_shared(dataset_dir_, usage_, num_workers_, connector_que_size_, - std::move(schema), std::move(sampler_rt)); - speech_commands_op->SetTotalRepeats(GetTotalRepeats()); - speech_commands_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(speech_commands_op); - return Status::OK(); -} - -Status SpeechCommandsNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status SpeechCommandsNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t sample_size, num_rows; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build SpeechCommandsOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SpeechCommandsNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["usage"] = usage_; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h deleted file mode 100644 index ac40447a4..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SPEECH_COMMANDS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SPEECH_COMMANDS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class SpeechCommandsNode : public MappableSourceNode { - public: - /// \brief Constructor. - SpeechCommandsNode(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~SpeechCommandsNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSpeechCommandsNode; } - - /// \brief Print the description. - /// \param[out] out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler - Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; // class SpeechCommandsNode -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SPEECH_COMMANDS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.cc deleted file mode 100644 index 12ea58bd8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.cc +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/squad_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for SQuADNode. -SQuADNode::SQuADNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) {} - -void SQuADNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -std::shared_ptr SQuADNode::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -Status SQuADNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SQuADDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("SQuADDataset", usage_, {"train", "dev", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("SQuADDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateScalar("SQuADDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("SQuADDataset", num_shards_, shard_id_)); - - return Status::OK(); -} - -// Function to build SQuADNode. -Status SQuADNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("context"), DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("question"), DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("text"), DataType(DataType::DE_STRING), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("answer_start"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - - // Create and initialize SQuADOp. - std::shared_ptr squad_op = - std::make_shared(dataset_dir_, usage_, num_workers_, num_samples_, worker_connector_size_, - std::move(schema), connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(squad_op->Init()); - - // If a global shuffle is used for SQuAD, it will inject a shuffle op over the SQuAD. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset SQuAD's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - // Get the number of rows in the dataset. - RETURN_IF_NOT_OK(SQuADOp::CountAllFileRows(dataset_dir_, usage_, &num_rows)); - // Add the shuffle op after this op. - if (usage_ == "train" || usage_ == "dev") { - int64_t num_files = 1; - RETURN_IF_NOT_OK(AddShuffleOp(num_files, num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - } else { - int64_t num_files = 2; - RETURN_IF_NOT_OK(AddShuffleOp(num_files, num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - } - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - squad_op->SetTotalRepeats(GetTotalRepeats()); - squad_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(squad_op); - - return Status::OK(); -} - -// Get the shard id of node. -Status SQuADNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size. -Status SQuADNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(SQuADOp::CountAllFileRows(dataset_dir_, usage_, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SQuADNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent -// class. SQuAD by itself is a non-mappable dataset that does not support sampling. However, if a cache operator is -// injected at some other place higher in the tree, that cache can inherit this sampler from the leaf, providing -// sampling support from the caching layer. That is why we setup the sampler for a leaf node that does not use -// sampling. -Status SQuADNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this SQuAD node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the SQuAD node need to be reset to its defaults so -// that this SQuAD node will produce the full set of data into the cache. -Status SQuADNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h deleted file mode 100644 index 0c4f5cc60..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/squad_node.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SQUAD_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SQUAD_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class SQuADNode. -/// \brief A Dataset derived class to represent SQuAD dataset. -class SQuADNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - SQuADNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~SQuADNode() = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSQuADNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief SQuAD by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this SQuAD node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the SQuAD node need to be reset to its defaults so - /// that this SQuAD node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SQUAD_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.cc deleted file mode 100644 index 55ac8ae3d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.cc +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -SST2Node::SST2Node(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr SST2Node::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SST2Node::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status SST2Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SST2Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("SST2Node", usage_, {"dev", "train", "test"})); - RETURN_IF_NOT_OK(ValidateScalar("SST2Node", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("SST2Node", num_shards_, shard_id_)); - - return Status::OK(); -} - -// Function to build SST2Node -Status SST2Node::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order - std::vector sorted_dataset_files; - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &sorted_dataset_files)); - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - char field_delim = '\t'; - - std::vector column_names; - - std::vector> column_default_list; - - std::shared_ptr sst2_op = std::make_shared( - sorted_dataset_files, usage_, field_delim, column_default_list, column_names, num_workers_, num_samples_, - worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(sst2_op->Init()); - - // If a global shuffle is used for SST2, it will inject a shuffle op over the SST2. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset SST2's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(SST2Op::CountAllFileRows(sorted_dataset_files, column_names.empty(), &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - sst2_op->SetTotalRepeats(GetTotalRepeats()); - sst2_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(sst2_op); - - return Status::OK(); -} - -Status SST2Node::WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files) { - RETURN_UNEXPECTED_IF_NULL(dataset_files); - Path train_file_name("train.tsv"); - Path test_file_name("test.tsv"); - Path dev_file_name("dev.tsv"); - Path dir(dataset_dir); - if (usage == "train") { - Path file_path = dir / train_file_name; - dataset_files->push_back(file_path.ToString()); - } else if (usage == "test") { - Path file_path = dir / test_file_name; - dataset_files->push_back(file_path.ToString()); - } else if (usage == "dev") { - Path file_path = dir / dev_file_name; - dataset_files->push_back(file_path.ToString()); - } - return Status::OK(); -} - -// Get the shard id of node -Status SST2Node::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size -Status SST2Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows; - int64_t sample_size; - std::vector dataset_files; - - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &dataset_files)); - RETURN_IF_NOT_OK(SST2Op::CountAllFileRows(dataset_files, true, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SST2Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// SST2 by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status SST2Node::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this SST2 node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the SST2 node need to be reset to its defaults so -// that this SST2 node will produce the full set of data into the cache. -Status SST2Node::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h deleted file mode 100644 index ea24d9f31..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sst2_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SST2_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SST2_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sst2_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class SST2Node : public NonMappableSourceNode { - public: - /// \brief Constructor. - SST2Node(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~SST2Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSST2Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SST2. - /// \param[in] dataset_files List of filepaths for the dataset files - /// \return std::vector A list of read file names. - Status WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files); - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief SST2 by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this SST2 node, then the cache will be - /// executing a sampler for fetching the data. As such, any options in the SST2 node need to be reset - /// to its defaults so that this SST2 node will produce the full set of data into the cache. Note: - /// This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SST2_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.cc deleted file mode 100644 index 8ad8bd7bc..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.cc +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/stl10_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -STL10Node::STL10Node(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), usage_(usage), sampler_(sampler) {} - -std::shared_ptr STL10Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void STL10Node::Print(std::ostream &out) const { - out << (Name() + "(cache:" + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status STL10Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("STL10Node", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("STL10Node", sampler_)); - - RETURN_IF_NOT_OK(ValidateStringValue("STL10Node", usage_, {"train", "test", "unlabeled", "train+unlabeled", "all"})); - - return Status::OK(); -} - -// Function to build STL10Op for STL10 -Status STL10Node::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto stl10_op = std::make_shared(usage_, num_workers_, dataset_dir_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - stl10_op->SetTotalRepeats(GetTotalRepeats()); - stl10_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(stl10_op); - - return Status::OK(); -} - -// Get the shard id of node -Status STL10Node::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status STL10Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(STL10Op::CountTotalRows(dataset_dir_, usage_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - - return Status::OK(); -} - -Status STL10Node::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h deleted file mode 100644 index b0aaf8c9f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/stl10_node.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_STL10_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_STL10_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class STL10Node : public MappableSourceNode { - public: - /// \brief Constructor - STL10Node(const std::string &dataset_dir, const std::string &usage, std::shared_ptr sampler, - std::shared_ptr cache); - - /// \brief Destructor - ~STL10Node() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kSTL10Node; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \param[in] shard_id - The shard id. - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - /// \param[in] sampler The Sampler setter of the current node - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_STL10_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.cc deleted file mode 100644 index fc3e2a813..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.cc +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -SUN397Node::SUN397Node(const std::string &dataset_dir, bool decode, const std::shared_ptr &sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), decode_(decode), sampler_(sampler) {} - -std::shared_ptr SUN397Node::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void SUN397Node::Print(std::ostream &out) const { out << Name(); } - -Status SUN397Node::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("SUN397Node", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("SUN397Node", sampler_)); - return Status::OK(); -} - -Status SUN397Node::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, decode_, num_workers_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -// Get the shard id of node. -Status SUN397Node::GetShardId(int32_t *shard_id) { - RETURN_UNEXPECTED_IF_NULL(shard_id); - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status SUN397Node::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(dataset_size); - - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows; - int64_t sample_size; - RETURN_IF_NOT_OK(SUN397Op::CountTotalRows(dataset_dir_, decode_, &num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status SUN397Node::to_json(nlohmann::json *out_json) { - nlohmann::json args; - nlohmann::json sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h deleted file mode 100644 index 76d80660a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/sun397_node.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SUN397_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SUN397_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/sun397_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class SUN397Node. -/// \brief A Dataset derived class to represent SUN397 dataset. -class SUN397Node : public MappableSourceNode { - public: - /// \brief Constructor. - /// \param[in] dataset_dir Dataset directory of SUN397Dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Tells SUN397Op what to read. - /// \param[in] cache Tensor cache to use. - SUN397Node(const std::string &dataset_dir, bool decode, const std::shared_ptr &sampler, - std::shared_ptr cache); - - /// \brief Destructor. - ~SUN397Node() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kSUN397Node; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[out] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard ID within num_shards. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed - /// up the process of getting dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Specify sampler. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_SUN397_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.cc deleted file mode 100644 index 94775a7ca..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.cc +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/tedlium_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for TedliumNode. -TedliumNode::TedliumNode(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, const std::shared_ptr &sampler, - const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - release_(release), - extensions_(extensions), - usage_(usage), - sampler_(sampler) {} - -std::shared_ptr TedliumNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, release_, usage_, extensions_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void TedliumNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status ValidateExtensionsParam(const std::string &dataset_name, const std::string &extensions) { - if (extensions != ".sph") { - std::string err_msg = dataset_name + ": extension " + extensions + " is not supported."; - MS_LOG(ERROR) << err_msg; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status TedliumNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - - RETURN_IF_NOT_OK(ValidateDatasetDirParam("TedliumNode", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateStringValue("TedliumNode", release_, {"release1", "release2", "release3"})); - - RETURN_IF_NOT_OK(ValidateExtensionsParam("TedliumNode", extensions_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("TedliumNode", sampler_)); - - if (release_ == "release1" || release_ == "release2") { - RETURN_IF_NOT_OK(ValidateStringValue("TedliumNode", usage_, {"dev", "train", "test", "all"})); - } else if (release_ == "release3") { - RETURN_IF_NOT_OK(ValidateStringValue("TedliumNode", usage_, {"all"})); - } - return Status::OK(); -} - -Status TedliumNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - TensorShape sample_rate_scalar = TensorShape::CreateScalar(); - TensorShape trans_scalar = TensorShape::CreateScalar(); - TensorShape talk_id_scalar = TensorShape::CreateScalar(); - TensorShape speaker_id_scalar = TensorShape::CreateScalar(); - TensorShape identi_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &sample_rate_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("transcript", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &trans_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("talk_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &talk_id_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("speaker_id", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &speaker_id_scalar))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("identifier", DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &identi_scalar))); - - // Argument that is not exposed to user in the API. - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto tedlium_op = std::make_shared(dataset_dir_, release_, usage_, extensions_, num_workers_, - std::move(schema), std::move(sampler_rt), connector_que_size_); - tedlium_op->SetTotalRepeats(GetTotalRepeats()); - tedlium_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(tedlium_op); - return Status::OK(); -} - -Status TedliumNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status TedliumNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0, sample_size = 0; - RETURN_IF_NOT_OK(TedliumOp::CountTotalRows(dataset_dir_, release_, usage_, extensions_, &num_rows)); - - // give sampler the total number of files and check if num_samples is smaller. - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - // We cache dataset size so as to not duplicated run. - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status TedliumNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["release"] = release_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["extensions"] = extensions_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h deleted file mode 100644 index 2d4622655..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tedlium_node.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEDLIUM_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEDLIUM_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class TedliumNode : public MappableSourceNode { - public: - /// \brief Constructor. - TedliumNode(const std::string &dataset_dir, const std::string &release, const std::string &usage, - const std::string &extensions, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor. - ~TedliumNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kTedliumNode; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id Shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - /// \brief Release getter. - /// \return Release of the current node. - const std::string &Release() const { return release_; } - - /// \brief DatasetDir getter. - /// \return DatasetDir of the current node. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Usage getter. - /// \return Usage of the current node. - const std::string &Usage() const { return usage_; } - - /// \brief Extensions getter. - /// \return Extensions of the current node. - const std::string &Extensions() const { return extensions_; } - - private: - std::string dataset_dir_; - std::string release_; - std::string usage_; - std::string extensions_; - std::shared_ptr sampler_; -}; // class TedliumNode -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEDLIUM_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.cc deleted file mode 100644 index 6dc87a797..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.cc +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/text_file_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -// Constructor for TextFileNode -TextFileNode::TextFileNode(std::vector dataset_files, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_files_(dataset_files), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr TextFileNode::Copy() { - auto node = std::make_shared(dataset_files_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void TextFileNode::Print(std::ostream &out) const { - out << (Name() + "(file:..." + ",num_shards:" + std::to_string(num_shards_) + - ",shard_id:" + std::to_string(shard_id_) + ",cache:" + ((cache_ != nullptr) ? "true" : "false") + ",...)"); -} - -Status TextFileNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("TextFileDataset", dataset_files_)); - RETURN_IF_NOT_OK(ValidateEnum("TextFileDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateScalar("TextFileDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("TextFileDataset", num_shards_, shard_id_)); - - return Status::OK(); -} - -// Function to build TextFileNode -Status TextFileNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order - std::vector sorted_dataset_files = dataset_files_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - - // Create and initialize TextFileOp - std::shared_ptr text_file_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(text_file_op->Init()); - - // If a global shuffle is used for TextFile, it will inject a shuffle op over the TextFile. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset TextFile's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(TextFileOp::CountAllFileRows(sorted_dataset_files, &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - text_file_op->SetTotalRepeats(GetTotalRepeats()); - text_file_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add TextFileOp - node_ops->push_back(text_file_op); - - return Status::OK(); -} - -// Get the shard id of node -Status TextFileNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size -Status TextFileNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(TextFileOp::CountAllFileRows(dataset_files_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status TextFileNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_files"] = dataset_files_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status TextFileNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_files", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_samples", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_shards", kTextFileNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_id", kTextFileNode)); - std::vector dataset_files = json_obj["dataset_files"]; - int64_t num_samples = json_obj["num_samples"]; - ShuffleMode shuffle = static_cast(json_obj["shuffle"]); - int32_t num_shards = json_obj["num_shards"]; - int32_t shard_id = json_obj["shard_id"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_files, num_samples, shuffle, num_shards, shard_id, cache); - (void)((*ds)->SetNumWorkers(json_obj["num_parallel_workers"])); - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// TextFile by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status TextFileNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this TextFile node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the TextFile node need to be reset to its defaults so -// that this TextFile node will produce the full set of data into the cache. -Status TextFileNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h deleted file mode 100644 index 09ce9de18..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEXT_FILE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEXT_FILE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class TextFileNode -/// \brief A Dataset derived class to represent TextFile dataset -class TextFileNode : public NonMappableSourceNode { - public: - /// \brief Constructor - TextFileNode(std::vector dataset_files, int32_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor - ~TextFileNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kTextFileNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::vector &DatasetFiles() const { return dataset_files_; } - int32_t NumSamples() const { return num_samples_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief TextFile by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this TextFile node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the TextFile node need to be reset to its defaults - /// so that this TextFile node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function - Status MakeSimpleProducer() override; - - private: - std::vector dataset_files_; - int32_t num_samples_; - int32_t num_shards_; - int32_t shard_id_; - ShuffleMode shuffle_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TEXT_FILE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc deleted file mode 100644 index f1d1c028e..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/jagged_connector.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "utils/file_utils.h" -#include "utils/system/crc32c.h" - -namespace mindspore { -namespace dataset { -std::unordered_set TFRecordNode::large_files_ = {}; -const int64_t kTFRecordFileLimit = 0x140000000; - -std::shared_ptr TFRecordNode::Copy() { - std::shared_ptr node; - if (schema_obj_ != nullptr) { - node = std::make_shared(dataset_files_, schema_obj_, columns_list_, num_samples_, shuffle_, - num_shards_, shard_id_, shard_equal_rows_, cache_, compression_type_); - } else { - node = std::make_shared(dataset_files_, schema_path_, columns_list_, num_samples_, shuffle_, - num_shards_, shard_id_, shard_equal_rows_, cache_, compression_type_); - } - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void TFRecordNode::Print(std::ostream &out) const { - out << (Name() + "(num_samples:" + std::to_string(num_samples_) + ",num_shards:" + std::to_string(num_shards_) + - ",shard_id:" + std::to_string(shard_id_) + ",...)"); -} - -Status TFRecordNode::ValidateTFRecordFiles(const std::vector &filenames) { - std::vector invalid_files; - - for (const std::string &filename : filenames) { - // invalid path - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - invalid_files.push_back(filename); - continue; - } - - std::ifstream reader(realpath.value(), std::ios::in | std::ios::binary); - // failed to open - if (!reader) { - invalid_files.push_back(filename); - reader.close(); - continue; - } - - // delay inside checking for compressed files - if (compression_type_.empty()) { - // read data - int64_t record_length = 0; - (void)reader.read(reinterpret_cast(&record_length), static_cast(sizeof(int64_t))); - - // read crc from file - uint32_t masked_crc = 0; - (void)reader.read(reinterpret_cast(&masked_crc), static_cast(sizeof(uint32_t))); - - // generate crc from data - uint32_t generated_crc = - system::Crc32c::GetMaskCrc32cValue(reinterpret_cast(&record_length), sizeof(int64_t)); - - // invalid tfrecord file - if (masked_crc != generated_crc) { - invalid_files.push_back(filename); - reader.close(); - continue; - } - } - - // check and log large files - CheckLargeFile(filename, &reader); - reader.close(); - } - - if (!invalid_files.empty()) { - std::string err_msg; - err_msg += "Invalid file. The following files either cannot be opened, or are not valid TFRecordDataset files:\n"; - - std::string accumulated_filenames = std::accumulate( - invalid_files.begin(), invalid_files.end(), std::string(""), - [](const std::string &accumulated, const std::string &next) { return accumulated + " " + next + "\n"; }); - err_msg += accumulated_filenames; - RETURN_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status TFRecordNode::ValidateTFRecordCompressionType(const std::string &compression_type, - const std::vector &dataset_files, - int32_t num_shards) const { - if (!compression_type.empty() && compression_type != "ZLIB" && compression_type != "GZIP") { - RETURN_STATUS_UNEXPECTED( - "Input compression_type can only be either '' (no compression), 'ZLIB', or 'GZIP', but got '" + compression_type + - "'."); - } - - if (!compression_type.empty()) { -#if defined(_WIN32) || defined(_WIN64) - RETURN_STATUS_UNEXPECTED("Compressed TFRecord files are not supported in Windows OS."); -#endif - } - - if (!compression_type.empty() && static_cast(dataset_files.size()) < num_shards) { - RETURN_STATUS_UNEXPECTED( - "When compression_type is provided, number of dataset files cannot be less than num_shards but got " + - std::to_string(dataset_files.size()) + " number of files with " + std::to_string(num_shards) + " num_shards"); - } - - return Status::OK(); -} - -void TFRecordNode::CheckLargeFile(const std::string &filename, std::ifstream *reader) { - if (large_files_.find(filename) == large_files_.end()) { - int64_t file_len = reader->seekg(0, std::ios::end).tellg(); - if (file_len > kTFRecordFileLimit) { - MS_LOG(WARNING) - << "The size of following TFRecord file is larger than 5G. There may be performance problems in " - << "distributed scenarios. The file can be split into sub-files smaller than 5G to obtain better performance. " - << "Large TFRecord file: " << filename; - (void)large_files_.insert(filename); - } - } -} - -// Validator for TFRecordNode -Status TFRecordNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateEnum("TFRecordDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("TFRecordDataset", dataset_files_)); - RETURN_IF_NOT_OK(ValidateScalar("TFRecordDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("TFRecordDataset", num_shards_, shard_id_)); - - RETURN_IF_NOT_OK(ValidateTFRecordCompressionType(compression_type_, dataset_files_, num_shards_)); - RETURN_IF_NOT_OK(ValidateTFRecordFiles(dataset_files_)); - - if (!shard_equal_rows_ && dataset_files_.size() < static_cast(num_shards_)) { - RETURN_STATUS_UNEXPECTED( - "Invalid file, numbers of tfrecord file should not less than num_shards when shard_equal_rows is false, " - "but got numbers of tfrecord file: " + - std::to_string(dataset_files_.size()) + ", num_shards: " + std::to_string(num_shards_)); - } - - return Status::OK(); -} - -Status TFRecordNode::CreateDataSchema(DataSchema *data_schema) { - RETURN_UNEXPECTED_IF_NULL(data_schema); - if (!schema_path_.empty()) { - RETURN_IF_NOT_OK(ValidateDatasetFilesParam("TFRecordDataset", {schema_path_})); - RETURN_IF_NOT_OK(data_schema->LoadSchemaFile(schema_path_, columns_list_)); - } else if (schema_obj_ != nullptr) { - std::string schema_json_string = schema_obj_->to_json(); - RETURN_IF_NOT_OK(data_schema->LoadSchemaString(schema_json_string, columns_list_)); - } - return Status::OK(); -} - -// Function to build TFRecordNode -Status TFRecordNode::Build(std::vector> *const node_ops) { - RETURN_UNEXPECTED_IF_NULL(node_ops); - // Sort the datasets file in a lexicographical order - std::vector sorted_dir_files = dataset_files_; - std::sort(sorted_dir_files.begin(), sorted_dir_files.end()); - - DataSchema data_schema; - RETURN_IF_NOT_OK(CreateDataSchema(&data_schema)); - - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - NonMappableLeafOp::CompressionType compression_type; - RETURN_IF_NOT_OK(HelperGetCompressType(&compression_type)); - - // Create and initialize TFReaderOp - std::shared_ptr tf_reader_op = - std::make_shared(num_workers_, worker_connector_size_, num_samples_, sorted_dir_files, - std::make_unique(data_schema), connector_que_size_, columns_list_, - shuffle_files, num_shards_, shard_id_, shard_equal_rows_, compression_type, decode_); - - RETURN_IF_NOT_OK(tf_reader_op->Init()); - - // If a global shuffle is used for TFRecord, it will inject a shuffle op over the TFRecord. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset TFRecord's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - if (compression_type_.empty() || num_samples_ == 0) { - RETURN_IF_NOT_OK(TFReaderOp::CountTotalRows(&num_rows, sorted_dir_files, 1, false, compression_type)); - } else { - // For compressed, actual total rows is unable to be counted, thus max number of rows that will be read is - // provided instead - num_rows = num_samples_ * num_shards_; - } - // Add the shuffle op after this op - RETURN_IF_NOT_OK(AddShuffleOp(sorted_dir_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - tf_reader_op->SetTotalRepeats(GetTotalRepeats()); - tf_reader_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add TFReaderOp - node_ops->push_back(tf_reader_op); - return Status::OK(); -} - -// Get the shard id of node -Status TFRecordNode::GetShardId(int32_t *const shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size -Status TFRecordNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - RETURN_UNEXPECTED_IF_NULL(size_getter); - RETURN_UNEXPECTED_IF_NULL(dataset_size); - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - if (compression_type_.empty() || num_samples_ == 0) { - int64_t num_rows; - constexpr int64_t kThreadCount = 8; - NonMappableLeafOp::CompressionType compression_type; - RETURN_IF_NOT_OK(HelperGetCompressType(&compression_type)); - // By default, TFRecord will do file-based sharding. But when cache is injected, it will be row-based sharding. - if (!shard_equal_rows_ && !IsCached()) { - // Data will be sharded by file - std::vector shard_file_list; - RETURN_IF_NOT_OK(GetShardFileList(&shard_file_list)); - RETURN_IF_NOT_OK( - TFReaderOp::CountTotalRows(&num_rows, shard_file_list, kThreadCount, estimate, compression_type)); - } else { - // Data will be sharded by row - RETURN_IF_NOT_OK(TFReaderOp::CountTotalRows(&num_rows, dataset_files_, kThreadCount, estimate, compression_type)); - num_rows = static_cast(ceil(num_rows / (num_shards_ * 1.0))); - } - *dataset_size = num_samples_ > 0 ? std::min(num_rows, num_samples_) : num_rows; - dataset_size_ = *dataset_size; - } else { - *dataset_size = num_samples_; - dataset_size_ = *dataset_size; - } - return Status::OK(); -} - -// Get the file list of the specific shard ID -Status TFRecordNode::GetShardFileList(std::vector *shard_filenames) { - RETURN_UNEXPECTED_IF_NULL(shard_filenames); - if (!shard_filenames->empty()) { - RETURN_STATUS_UNEXPECTED("The initial file list must be empty."); - } - int cut_off_file = static_cast(dataset_files_.size()); - if (!compression_type_.empty() && num_samples_ > 0) { - cut_off_file = static_cast(cut_off_file / num_shards_) * num_shards_; - } - for (int index = shard_id_; index < cut_off_file; index += num_shards_) { - shard_filenames->push_back(dataset_files_.at(index)); - } - return Status::OK(); -} - -// Helper get compression type -Status TFRecordNode::HelperGetCompressType(NonMappableLeafOp::CompressionType *compression_type) { - if (compression_type_.empty()) { - *compression_type = NonMappableLeafOp::CompressionType::NONE; - } else if (compression_type_ == "GZIP" && num_samples_ > 0) { - *compression_type = NonMappableLeafOp::CompressionType::GZIP; - } else if (compression_type_ == "GZIP" && num_samples_ == 0) { - *compression_type = NonMappableLeafOp::CompressionType::GZIP_WITH_COUNT; - } else if (compression_type_ == "ZLIB" && num_samples_ > 0) { - *compression_type = NonMappableLeafOp::CompressionType::ZLIB; - } else if (compression_type_ == "ZLIB" && num_samples_ == 0) { - *compression_type = NonMappableLeafOp::CompressionType::ZLIB_WITH_COUNT; - } else { - RETURN_STATUS_UNEXPECTED("Input compression_type can only be either '' (no compression), 'ZLIB', or 'GZIP'"); - } - return Status::OK(); -} - -Status TFRecordNode::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_files"] = dataset_files_; - args["columns_list"] = columns_list_; - args["num_samples"] = num_samples_; - args["shuffle_global"] = (shuffle_ == ShuffleMode::kGlobal); - args["shuffle_files"] = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - args["shard_equal_rows"] = shard_equal_rows_; - args["compression_type"] = compression_type_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - if (schema_obj_ != nullptr) { - schema_obj_->set_dataset_type("TF"); - schema_obj_->set_num_rows(num_samples_); - nlohmann::json schema_json_string; - schema_obj_->schema_to_json(&schema_json_string); - args["schema_json_string"] = schema_json_string; - } else { - args["schema_file_path"] = schema_path_; - } - *out_json = args; - return Status::OK(); -} - -Status TFRecordNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_files", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "columns_list", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_samples", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shuffle", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_shards", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_id", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "shard_equal_rows", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "compression_type", kTFRecordNode)); - std::vector dataset_files = json_obj["dataset_files"]; - std::vector columns_list = json_obj["columns_list"]; - int64_t num_samples = json_obj["num_samples"]; - ShuffleMode shuffle = static_cast(json_obj["shuffle"]); - int32_t num_shards = json_obj["num_shards"]; - int32_t shard_id = json_obj["shard_id"]; - bool shard_equal_rows = json_obj["shard_equal_rows"]; - std::string compression_type = json_obj["compression_type"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - if (json_obj.find("schema_file_path") != json_obj.end()) { - std::string schema_file_path = json_obj["schema_file_path"]; - *ds = std::make_shared(dataset_files, schema_file_path, columns_list, num_samples, shuffle, - num_shards, shard_id, shard_equal_rows, cache, compression_type); - } else { - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("schema_json_string") != json_obj.end(), - "Failed to find either schema_file_path or schema_json_string"); - std::shared_ptr schema_obj = Schema(); - RETURN_IF_NOT_OK(schema_obj->from_json(json_obj["schema_json_string"])); - *ds = std::make_shared(dataset_files, schema_obj, columns_list, num_samples, shuffle, num_shards, - shard_id, shard_equal_rows, cache, compression_type); - } - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// TFRecord by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status TFRecordNode::SetupSamplerForCache(std::shared_ptr *sampler) { - RETURN_UNEXPECTED_IF_NULL(sampler); - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this TFRecord node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the TFRecord node need to be reset to its defaults so -// that this TFRecord node will produce the full set of data into the cache. -Status TFRecordNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - shard_equal_rows_ = false; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status TFRecordNode::Accept(IRNodePass *p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status TFRecordNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(p); - RETURN_UNEXPECTED_IF_NULL(modified); - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h deleted file mode 100644 index 68008d8db..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TF_RECORD_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TF_RECORD_NODE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/tf_reader_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class TFRecordNode -/// \brief A Dataset derived class to represent TFRecord dataset -class TFRecordNode : public NonMappableSourceNode { - friend class CacheValidationPass; - - public: - /// \brief Constructor - /// \note Parameter 'schema' is the path to the schema file - TFRecordNode(const std::vector &dataset_files, const std::string &schema, - const std::vector &columns_list, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, bool shard_equal_rows, const std::shared_ptr &cache, - const std::string &compression_type) - : NonMappableSourceNode(cache), - dataset_files_(dataset_files), - schema_path_(schema), - columns_list_(columns_list), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - shard_equal_rows_(shard_equal_rows), - compression_type_(compression_type), - decode_(true) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User - // discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the - // num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return - // num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); - } - - /// \brief Constructor - /// \note Parameter 'schema' is shared pointer to Schema object - TFRecordNode(const std::vector &dataset_files, const std::shared_ptr &schema, - const std::vector &columns_list, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, bool shard_equal_rows, const std::shared_ptr &cache, - const std::string &compression_type) - : NonMappableSourceNode(cache), - dataset_files_(dataset_files), - schema_obj_(schema), - columns_list_(columns_list), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - shard_equal_rows_(shard_equal_rows), - compression_type_(compression_type), - decode_(true) {} - - /// \brief Destructor - ~TFRecordNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kTFRecordNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *const shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Set whether to parse the protobuf in TFRecordOp - /// \param[in] decode Whether to decode. - void SetDecode(bool decode) { decode_ = decode; } - - /// \brief Create DataSchema object with the input. - /// \param[out] data_schema The output data schema. - Status CreateDataSchema(DataSchema *data_schema); - - /// \brief Get the file list of the specific shard ID - /// \param[out] shard_filenames the list of filenames for that specific shard ID - /// \return Status of the function - Status GetShardFileList(std::vector *shard_filenames); - - /// \brief Getter functions - const std::vector &DatasetFiles() const { return dataset_files_; } - const std::string &SchemaPath() const { return schema_path_; } - const std::shared_ptr &GetSchemaObj() const { return schema_obj_; } - const std::vector &ColumnsList() const { return columns_list_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - bool ShardEqualRows() const { return shard_equal_rows_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief TFRecord by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup - /// \return Status of the function - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this TFRecord node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the TFRecord node need to be reset to its defaults - /// so that this TFRecord node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function - Status MakeSimpleProducer() override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - private: - /// Check and return if there exists invalid tfrecord files in the file list - Status ValidateTFRecordFiles(const std::vector &filenames); - - /// Check and return if compression type is invalid or number of files in dataset_files is less than num_shards - Status ValidateTFRecordCompressionType(const std::string &compression_type, - const std::vector &dataset_files, int32_t num_shards) const; - - /// Record large tf file and log a warning. - void CheckLargeFile(const std::string &filename, std::ifstream *reader); - - /// Helper function to get NonMappableLeafOp::CompressionType based on compression_type_ and num_samples_ - Status HelperGetCompressType(NonMappableLeafOp::CompressionType *compression_type); - - std::vector dataset_files_; - std::string schema_path_; // schema_path_ path to schema file. It is set when type of schema parameter is string - std::shared_ptr schema_obj_; // schema_obj_ schema object. - std::vector columns_list_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - bool shard_equal_rows_; - std::string compression_type_; - bool decode_; // whether to parse the proto - - static std::unordered_set large_files_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_TF_RECORD_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.cc deleted file mode 100644 index 6fe42a84f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.cc +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/udpos_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for UDPOSNode. -UDPOSNode::UDPOSNode(const std::string &dataset_dir, const std::string &usage, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - udpos_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr UDPOSNode::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void UDPOSNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status UDPOSNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("UDPOSNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("UDPOSNode", usage_, {"train", "test", "valid", "all"})); - RETURN_IF_NOT_OK(ValidateScalar("UDPOSNode", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("UDPOSNode", num_shards_, shard_id_)); - RETURN_IF_NOT_OK(ValidateEnum("UDPOSNode", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - return Status::OK(); -} - -// Function to build UDPOSNode. -Status UDPOSNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = udpos_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("word", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("universal", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("stanford", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - // Create and initialize UDPOSOp. - std::shared_ptr udpos_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(udpos_op->Init()); - - // If a global shuffle is used for UDPOS, it will inject a shuffle op over the UDPOS. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset UDPOS's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(UDPOSOp::CountAllFileRows(sorted_dataset_files, &num_rows)); - - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - udpos_op->SetTotalRepeats(GetTotalRepeats()); - udpos_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add UDPOSOp. - node_ops->push_back(udpos_op); - - return Status::OK(); -} - -// Get the shard id of node. -Status UDPOSNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size. -Status UDPOSNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(UDPOSOp::CountAllFileRows(udpos_files_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status UDPOSNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// UDPOS by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status UDPOSNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this UDPOS node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the UDPOS node need to be reset to its defaults so -// that this UDPOS node will produce the full set of data into the cache. -Status UDPOSNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector UDPOSNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector udpos_files_list; - Path train_prefix("en-ud-tag.v2.train.txt"); - Path test_prefix("en-ud-tag.v2.test.txt"); - Path valid_prefix("en-ud-tag.v2.dev.txt"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - udpos_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - udpos_files_list.push_back(temp_path.ToString()); - } else if (usage == "valid") { - Path temp_path = dir / valid_prefix; - udpos_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - udpos_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - udpos_files_list.push_back(temp_path1.ToString()); - Path temp_path2 = dir / valid_prefix; - udpos_files_list.push_back(temp_path2.ToString()); - } - return udpos_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h deleted file mode 100644 index 217790dad..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/udpos_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_UDPOS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_UDPOS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class UDPOSNode. -/// \brief A Dataset derived class to represent UDPOS dataset. -class UDPOSNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - UDPOSNode(const std::string &dataset_dir, const std::string &usage, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~UDPOSNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "UDPOSNode"; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int32_t NumSamples() const { return num_samples_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief UDPOS by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this UDPOS node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the UDPOS node need to be reset to its defaults - /// so that this UDPOS node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \Read all files in the directory. - /// \param[in] usage Part of dataset of UDPOS. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return Status The status code returned. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - int32_t num_samples_; - int32_t num_shards_; - int32_t shard_id_; - ShuffleMode shuffle_; - std::vector udpos_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_UDPOS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.cc deleted file mode 100644 index 5691fca52..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/usps_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -USPSNode::USPSNode(const std::string &dataset_dir, const std::string &usage, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr USPSNode::Copy() { - auto node = std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void USPSNode::Print(std::ostream &out) const { - out << (Name() + "(dataset dir:" + dataset_dir_ + ", usage:" + usage_ + - ", num_shards:" + std::to_string(num_shards_) + ", shard_id:" + std::to_string(shard_id_) + - ", num_samples:" + std::to_string(num_samples_) + ")"); -} - -Status USPSNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("USPSDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("USPSDataset", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateScalar("USPSDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("USPSDataset", num_shards_, shard_id_)); - RETURN_IF_NOT_OK(ValidateEnum("USPSDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - return Status::OK(); -} - -Status USPSNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kCv, 1))); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 0, &scalar))); - - auto op = std::make_shared(dataset_dir_, usage_, std::move(schema), num_workers_, worker_connector_size_, - num_samples_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(op->Init()); - - // If a global shuffle is used for USPS, it will inject a shuffle op over the USPS. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset USPS's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(USPSOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK(AddShuffleOp(op->FileNames().size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get the shard id of node -Status USPSNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size -Status USPSNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(USPSOp::CountTotalRows(dataset_dir_, usage_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status USPSNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// USPS by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status USPSNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this USPS node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the USPS node need to be reset to its defaults so -// that this USPS node will produce the full set of data into the cache. -Status USPSNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h deleted file mode 100644 index 56e1152c9..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/usps_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_USPS_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_USPS_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class USPSNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - USPSNode(const std::string &dataset_dir, const std::string &usage, int32_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~USPSNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kUSPSNode; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter functions. - const std::string &Usage() const { return usage_; } - - /// \brief Getter functions. - int32_t NumSamples() const { return num_samples_; } - - /// \brief Getter functions. - int32_t NumShards() const { return num_shards_; } - - /// \brief Getter functions. - int32_t ShardId() const { return shard_id_; } - - /// \brief Getter functions. - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief USPS by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this USPS node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the USPS node need to be reset to its defaults - /// so that this USPS node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - int32_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_USPS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.cc deleted file mode 100644 index 241cdccb8..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.cc +++ /dev/null @@ -1,249 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/voc_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { - -#ifdef ENABLE_PYTHON -// Constructor for VOCNode -VOCNode::VOCNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::map &class_indexing, bool decode, std::shared_ptr sampler, - std::shared_ptr cache, bool extra_metadata, py::function decrypt) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - task_(task), - usage_(usage), - class_index_(class_indexing), - decode_(decode), - sampler_(sampler), - extra_metadata_(extra_metadata), - decrypt_(decrypt) {} -#else -// Constructor for VOCNode -VOCNode::VOCNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::map &class_indexing, bool decode, std::shared_ptr sampler, - std::shared_ptr cache, bool extra_metadata) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - task_(task), - usage_(usage), - class_index_(class_indexing), - decode_(decode), - sampler_(sampler), - extra_metadata_(extra_metadata) {} -#endif - -std::shared_ptr VOCNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); -#ifdef ENABLE_PYTHON - auto node = std::make_shared(dataset_dir_, task_, usage_, class_index_, decode_, sampler, cache_, - extra_metadata_, decrypt_); -#else - auto node = - std::make_shared(dataset_dir_, task_, usage_, class_index_, decode_, sampler, cache_, extra_metadata_); -#endif - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void VOCNode::Print(std::ostream &out) const { out << Name(); } - -Status VOCNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - Path dir(dataset_dir_); - - RETURN_IF_NOT_OK(ValidateDatasetDirParam("VOCDataset", dataset_dir_)); - - RETURN_IF_NOT_OK(ValidateDatasetSampler("VOCDataset", sampler_)); - - if (task_ == "Segmentation") { - if (!class_index_.empty()) { - std::string err_msg = "VOCDataset: 'class_indexing' is invalid in Segmentation task."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - Path imagesets_file = dir / "ImageSets" / "Segmentation" / usage_ + ".txt"; - if (!imagesets_file.Exists()) { - std::string err_msg = "VOCDataset: Invalid 'usage': " + usage_ + ", file does not exist"; - MS_LOG(ERROR) << "VOCDataset: Invalid 'usage': " << usage_ << ", file \"" << imagesets_file - << "\" does not exist!"; - return Status(StatusCode::kMDSyntaxError, err_msg); - } - } else if (task_ == "Detection") { - Path imagesets_file = dir / "ImageSets" / "Main" / usage_ + ".txt"; - if (!imagesets_file.Exists()) { - std::string err_msg = "VOCDataset: Invalid 'usage': " + usage_ + ", file does not exist"; - MS_LOG(ERROR) << "VOCDataset: Invalid 'usage': " << usage_ << ", file \"" << imagesets_file - << "\" does not exist!"; - return Status(StatusCode::kMDSyntaxError, err_msg); - } - } else { - std::string err_msg = "VOCDataset: Invalid 'task': " + task_ + ", expected Segmentation or Detection."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -// Function to build VOCNode -Status VOCNode::Build(std::vector> *const node_ops) { - auto schema = std::make_unique(); - VOCOp::TaskType task_type_; - - if (task_ == "Segmentation") { - task_type_ = VOCOp::TaskType::Segmentation; - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnImage), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnTarget), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - } else if (task_ == "Detection") { - task_type_ = VOCOp::TaskType::Detection; - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnImage), DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnBbox), DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnLabel), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnDifficult), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string(kColumnTruncate), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - } - if (extra_metadata_) { - std::string meta_file_name = std::string(kDftMetaColumnPrefix) + std::string(kColumnFileName); - TensorShape scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(meta_file_name, DataType(DataType::DE_STRING), TensorImpl::kFlexible, 0, &scalar))); - } - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - std::shared_ptr voc_op; -#ifdef ENABLE_PYTHON - voc_op = std::make_shared(task_type_, usage_, dataset_dir_, class_index_, num_workers_, connector_que_size_, - decode_, std::move(schema), std::move(sampler_rt), extra_metadata_, decrypt_); - -#else - voc_op = std::make_shared(task_type_, usage_, dataset_dir_, class_index_, num_workers_, connector_que_size_, - decode_, std::move(schema), std::move(sampler_rt), extra_metadata_); -#endif - voc_op->SetTotalRepeats(GetTotalRepeats()); - voc_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(voc_op); - - return Status::OK(); -} - -// Get the shard id of node -Status VOCNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - - return Status::OK(); -} - -// Get Dataset size -Status VOCNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "[Internal ERROR] Unable to build VocOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status VOCNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["task"] = task_; - args["usage"] = usage_; - args["class_indexing"] = class_index_; - args["decode"] = decode_; - args["extra_metadata"] = extra_metadata_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status VOCNode::from_json(nlohmann::json json_obj, std::shared_ptr *ds) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "num_parallel_workers", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "connector_queue_size", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "dataset_dir", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "task", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "usage", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "class_indexing", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "decode", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "sampler", kTFRecordNode)); - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "extra_metadata", kTFRecordNode)); - std::string dataset_dir = json_obj["dataset_dir"]; - std::string task = json_obj["task"]; - std::string usage = json_obj["usage"]; - std::map class_indexing; - nlohmann::json class_map = json_obj["class_indexing"]; - for (const auto &class_map_child : class_map) { - std::string class_ = class_map_child[0]; - int32_t indexing = class_map_child[1]; - class_indexing.insert({class_, indexing}); - } - bool decode = json_obj["decode"]; - std::shared_ptr sampler; - RETURN_IF_NOT_OK(Serdes::ConstructSampler(json_obj["sampler"], &sampler)); - bool extra_metadata = json_obj["extra_metadata"]; - std::shared_ptr cache = nullptr; - RETURN_IF_NOT_OK(DatasetCache::from_json(json_obj, &cache)); - *ds = std::make_shared(dataset_dir, task, usage, class_indexing, decode, sampler, cache, extra_metadata); - (*ds)->SetNumWorkers(json_obj["num_parallel_workers"]); - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h deleted file mode 100644 index b911700e2..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_VOC_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_VOC_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class VOCNode : public MappableSourceNode { - public: -#ifdef ENABLE_PYTHON - /// \brief Constructor - VOCNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::map &class_indexing, bool decode, std::shared_ptr sampler, - std::shared_ptr cache, bool extra_metadata = false, py::function decrypt = py::none()); -#else - /// \brief Constructor - VOCNode(const std::string &dataset_dir, const std::string &task, const std::string &usage, - const std::map &class_indexing, bool decode, std::shared_ptr sampler, - std::shared_ptr cache, bool extra_metadata = false); -#endif - - /// \brief Destructor - ~VOCNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kVOCNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Get the shard id of node - /// \return Status Status::OK() if get shard id successfully - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Task() const { return task_; } - const std::string &Usage() const { return usage_; } - const std::map &ClassIndex() const { return class_index_; } - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - -#ifndef ENABLE_ANDROID - /// \brief Function to read dataset in json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Deserialized dataset - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr *ds); -#endif - - /// \brief Sampler getter - /// \return SamplerObj of the current node - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - const std::string kColumnImage = "image"; - const std::string kColumnTarget = "target"; - const std::string kColumnBbox = "bbox"; - const std::string kColumnLabel = "label"; - const std::string kColumnDifficult = "difficult"; - const std::string kColumnTruncate = "truncate"; - const std::string kColumnFileName = "filename"; - std::string dataset_dir_; - std::string task_; - std::string usage_; - std::map class_index_; - bool decode_; - std::shared_ptr sampler_; - bool extra_metadata_; -#ifdef ENABLE_PYTHON - py::function decrypt_; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_VOC_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.cc deleted file mode 100644 index 4ec0d221c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.cc +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/wider_face_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for WIDERFaceNode. -WIDERFaceNode::WIDERFaceNode(const std::string &dataset_dir, const std::string &usage, const bool &decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache) - : MappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - sampler_(sampler), - decode_(decode) {} - -std::shared_ptr WIDERFaceNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, usage_, decode_, sampler, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void WIDERFaceNode::Print(std::ostream &out) const { out << Name(); } - -Status WIDERFaceNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("WIDERFaceDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("WIDERFaceDataset", sampler_)); - RETURN_IF_NOT_OK(ValidateStringValue("WIDERFaceDataset", usage_, {"all", "train", "valid", "test"})); - return Status::OK(); -} - -// Function to build WIDERFaceNode. -Status WIDERFaceNode::Build(std::vector> *const node_ops) { - std::unique_ptr schema = std::make_unique(); - if (usage_ == "all" || usage_ == "train" || usage_ == "valid") { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("bbox"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("blur"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("expression"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("illumination"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("occlusion"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor(std::string("pose"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor(std::string("invalid"), DataType(DataType::DE_UINT32), TensorImpl::kFlexible, 1))); - } else if (usage_ == "test") { - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("image", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - } - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - auto wider_face_op = std::make_shared(dataset_dir_, usage_, num_workers_, connector_que_size_, decode_, - std::move(schema), std::move(sampler_rt)); - wider_face_op->SetTotalRepeats(GetTotalRepeats()); - wider_face_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(wider_face_op); - return Status::OK(); -} - -// Get the shard id of node. -Status WIDERFaceNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -// Get Dataset size. -Status WIDERFaceNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows = 0, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build WIDERFaceOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status WIDERFaceNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["decode"] = decode_; - args["usage"] = usage_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h deleted file mode 100644 index 97e1d5271..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wider_face_node.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIDER_FACE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIDER_FACE_NODE_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class WIDERFaceNode : public MappableSourceNode { - public: - /// \brief Constructor. - WIDERFaceNode(const std::string &dataset_dir, const std::string &usage, const bool &decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Destructor. - ~WIDERFaceNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kWIDERFaceNode; } - - /// \brief Print the description. - /// \param out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter function. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Getter function. - const std::string &Usage() const { return usage_; } - - /// \brief Getter function. - bool Decode() const { return decode_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getters. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::string usage_; - bool decode_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIDER_FACE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.cc deleted file mode 100644 index fc352855f..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.cc +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/wiki_text_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for WikiTextNode. -WikiTextNode::WikiTextNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - wikitext_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr WikiTextNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void WikiTextNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status WikiTextNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("WikiTextDataset", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("WikiTextDataset", usage_, {"train", "test", "valid", "all"})); - RETURN_IF_NOT_OK(ValidateScalar("WikiTextDataset", "num_samples", num_samples_, {0}, false)); - RETURN_IF_NOT_OK(ValidateEnum("WikiTextDataset", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - RETURN_IF_NOT_OK(ValidateDatasetShardParams("WikiTextDataset", num_shards_, shard_id_)); - return Status::OK(); -} - -// Function to build WikiTextNode. -Status WikiTextNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = wikitext_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK(schema->AddColumn(ColDescriptor("text", DataType(DataType::DE_UINT8), TensorImpl::kFlexible, 1))); - // Create and initialize WikiTextNode. - std::shared_ptr wikitext_op = - std::make_shared(num_workers_, num_samples_, worker_connector_size_, std::move(schema), - sorted_dataset_files, connector_que_size_, shuffle_files, num_shards_, shard_id_); - RETURN_IF_NOT_OK(wikitext_op->Init()); - // If a global shuffle is used for WikiText, it will inject a shuffle op over the WikiText. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset WikiText's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(WikiTextOp::CountAllFileRows(wikitext_files_list_, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - wikitext_op->SetTotalRepeats(GetTotalRepeats()); - wikitext_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - // Add WikiTextNode. - node_ops->push_back(wikitext_op); - return Status::OK(); -} - -// Get the shard id of node. -Status WikiTextNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -// Get Dataset size. -Status WikiTextNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size = num_samples_; - RETURN_IF_NOT_OK(WikiTextOp::CountAllFileRows(wikitext_files_list_, &num_rows)); - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status WikiTextNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// WikiText by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status WikiTextNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this WikiText node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the WikiText node need to be reset to its defaults so -// that this WikiText node will produce the full set of data into the cache. -Status WikiTextNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector WikiTextNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector wikitext_files_list; - Path train_prefix("wiki.train.tokens"); - Path test_prefix("wiki.test.tokens"); - Path valid_prefix("wiki.valid.tokens"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - wikitext_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - wikitext_files_list.push_back(temp_path.ToString()); - } else if (usage == "valid") { - Path temp_path = dir / valid_prefix; - wikitext_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - wikitext_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - wikitext_files_list.push_back(temp_path1.ToString()); - Path temp_path2 = dir / valid_prefix; - wikitext_files_list.push_back(temp_path2.ToString()); - } - return wikitext_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h deleted file mode 100644 index 4688ab52b..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIKI_TEXT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIKI_TEXT_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \brief class WikiTextNode. -/// \brief Dataset derived class to represent WikiText dataset. -class WikiTextNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - WikiTextNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~WikiTextNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kWikiTextNode; } - - /// \brief Print the description. - /// \param[in] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Get the num samples id of node. - /// \return NumSamples of the node. - int32_t NumSamples() const { return num_samples_; } - - /// \brief Get the num shards id of node. - /// \return NumSamples of the node. - int32_t NumShards() const { return num_shards_; } - - /// \brief Get the shard id of node. - /// \return Shard_id of the node. - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the shuffle mode of node. - /// \return Shuffle of the node. - ShuffleMode Shuffle() const { return shuffle_; } - - /// \brief Get the usage node. - /// \return Usage of the node. - const std::string &Usage() const { return usage_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief WikiText by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in - /// the tree, that cache can inherit this sampler from the leaf, providing - /// sampling support from the caching layer. That is why we setup the - /// sampler for a leaf node that does not use sampling. Note: This - /// function is common among NonMappableSourceNode and should be promoted - /// to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this WikiText node, - /// then the cache will be executing a sampler for fetching the data. - /// As such, any options in the WikiText node need to be reset to its defaults - /// so that this WikiText node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its - /// parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of WikiText. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - int32_t num_shards_; - int32_t shard_id_; - ShuffleMode shuffle_; - std::vector wikitext_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_WIKI_TEXT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.cc deleted file mode 100644 index 2aa70e6ec..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.cc +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -YahooAnswersNode::YahooAnswersNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - std::shared_ptr cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - usage_(usage), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. User discretion - // is advised. Auto_num_worker_pass is currently an experimental feature which can still work if the num_shards_ isn't - // 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to return num_shards. Once - // PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr YahooAnswersNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void YahooAnswersNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status YahooAnswersNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("YahooAnswersNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("YahooAnswersNode", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("YahooAnswersNode", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - - if (num_samples_ < 0) { - std::string err_msg = "YahooAnswersNode: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("YahooAnswersNode", num_shards_, shard_id_)); - - return Status::OK(); -} - -// Function to build YahooAnswersNode -Status YahooAnswersNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order - std::vector sorted_dataset_files; - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &sorted_dataset_files)); - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - char field_delim = ','; - - std::vector column_names = {"class", "title", "content", "answer"}; - - std::vector> column_default_list; - for (auto c : column_names) { - column_default_list.push_back(std::make_shared>(YahooAnswersOp::STRING, "")); - } - - std::shared_ptr yahoo_answers_op = std::make_shared( - sorted_dataset_files, field_delim, column_default_list, column_names, num_workers_, num_samples_, - worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_); - - RETURN_IF_NOT_OK(yahoo_answers_op->Init()); - - // If a global shuffle is used for YahooAnswers, it will inject a shuffle op over the YahooAnswers. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be built. - // This is achieved in the cache transform pass where we call MakeSimpleProducer to reset YahooAnswers's shuffle - // option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset - RETURN_IF_NOT_OK(YahooAnswersOp::CountAllFileRows(sorted_dataset_files, column_names.empty(), &num_rows)); - - // Add the shuffle op after this op - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - yahoo_answers_op->SetTotalRepeats(GetTotalRepeats()); - yahoo_answers_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(yahoo_answers_op); - - return Status::OK(); -} - -Status YahooAnswersNode::WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files) { - Path train_file_name("train.csv"); - Path test_file_name("test.csv"); - Path dir(dataset_dir); - if (usage == "train") { - Path file_path = dir / train_file_name; - dataset_files->push_back(file_path.ToString()); - } else if (usage == "test") { - Path file_path = dir / test_file_name; - dataset_files->push_back(file_path.ToString()); - } else { - Path file_path_1 = dir / train_file_name; - if (file_path_1.Exists()) { - dataset_files->push_back(file_path_1.ToString()); - } - Path file_path_2 = dir / test_file_name; - if (file_path_2.Exists()) { - dataset_files->push_back(file_path_2.ToString()); - } - } - return Status::OK(); -} - -// Get the shard id of node -Status YahooAnswersNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - - return Status::OK(); -} - -// Get Dataset size -Status YahooAnswersNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows, sample_size; - std::vector column_names = {"class", "title", "content", "answer"}; - std::vector dataset_files; - - RETURN_IF_NOT_OK(WalkAllFiles(dataset_dir_, usage_, &dataset_files)); - RETURN_IF_NOT_OK(YahooAnswersOp::CountAllFileRows(dataset_files, column_names.empty(), &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status YahooAnswersNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -// Note: The following two functions are common among NonMappableSourceNode and should be promoted to its parent class. -// YahooAnswers by itself is a non-mappable dataset that does not support sampling. -// However, if a cache operator is injected at some other place higher in the tree, that cache can -// inherit this sampler from the leaf, providing sampling support from the caching layer. -// That is why we setup the sampler for a leaf node that does not use sampling. -Status YahooAnswersNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -// If a cache has been added into the ascendant tree over this YahooAnswers node, then the cache will be executing -// a sampler for fetching the data. As such, any options in the YahooAnswers node need to be reset to its defaults so -// that this YahooAnswers node will produce the full set of data into the cache. -Status YahooAnswersNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h deleted file mode 100644 index e25f59fcb..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YAHOO_ANSWERS_NODE_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YAHOO_ANSWERS_NODE_H - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yahoo_answers_op.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class YahooAnswersNode : public NonMappableSourceNode { - public: - /// \brief Constructor. - YahooAnswersNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, std::shared_ptr cache); - - /// \brief Destructor. - ~YahooAnswersNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kYahooAnswersNode; } - - /// \brief Print the description. - /// \param[out] out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param[in] node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of YahooAnswers. - /// \param[in] dataset_files List of filepaths for the dataset files - /// \return std::vector A list of read file names. - Status WalkAllFiles(const std::string &dataset_dir, const std::string &usage, - std::vector *dataset_files); - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief YahooAnswers by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this YahooAnswers node, then the cache will be - /// executing a sampler for fetching the data. As such, any options in the YahooAnswers node need to be reset - /// to its defaults so that this YahooAnswers node will produce the full set of data into the cache. Note: - /// This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - private: - std::string dataset_dir_; - std::string usage_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YAHOO_ANSWERS_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.cc deleted file mode 100644 index 7414e805c..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.cc +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -YelpReviewNode::YelpReviewNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache) - : NonMappableSourceNode(std::move(cache)), - dataset_dir_(dataset_dir), - num_samples_(num_samples), - shuffle_(shuffle), - num_shards_(num_shards), - shard_id_(shard_id), - usage_(usage), - yelp_review_files_list_(WalkAllFiles(usage, dataset_dir)) { - // Update the num_shards_ in global context. this number is only used for now by auto_num_worker_pass. - // User discretion is advised. Auto_num_worker_pass is currently an experimental feature which can still work - // if the num_shards_ isn't 100% correct. The reason behind is for now, PreBuildSampler doesn't offer a way to - // return num_shards. Once PreBuildSampler is phased out, this can be cleaned up. - GlobalContext::config_manager()->set_num_shards_for_auto_num_workers(num_shards_); -} - -std::shared_ptr YelpReviewNode::Copy() { - auto node = - std::make_shared(dataset_dir_, usage_, num_samples_, shuffle_, num_shards_, shard_id_, cache_); - (void)node->SetNumWorkers(num_workers_); - (void)node->SetConnectorQueueSize(connector_que_size_); - return node; -} - -void YelpReviewNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + - ", num_shards: " + std::to_string(num_shards_) + ", shard_id: " + std::to_string(shard_id_) + ")"); -} - -Status YelpReviewNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("YelpReviewNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateStringValue("YelpReviewNode", usage_, {"train", "test", "all"})); - RETURN_IF_NOT_OK(ValidateEnum("YelpReviewNode", "ShuffleMode", shuffle_, - {ShuffleMode::kFalse, ShuffleMode::kFiles, ShuffleMode::kGlobal})); - if (num_samples_ < 0) { - std::string err_msg = "YelpReviewNode: Invalid number of samples: " + std::to_string(num_samples_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - RETURN_IF_NOT_OK(ValidateDatasetShardParams("YelpReviewNode", num_shards_, shard_id_)); - return Status::OK(); -} - -Status YelpReviewNode::Build(std::vector> *const node_ops) { - bool shuffle_files = (shuffle_ == ShuffleMode::kGlobal || shuffle_ == ShuffleMode::kFiles); - - // Sort the dataset files in a lexicographical order. - std::vector sorted_dataset_files = yelp_review_files_list_; - std::sort(sorted_dataset_files.begin(), sorted_dataset_files.end()); - - std::vector> column_default; - column_default.push_back(std::make_shared>(YelpReviewOp::STRING, "")); - column_default.push_back(std::make_shared>(YelpReviewOp::STRING, "")); - - std::vector column_name = {"label", "text"}; - char field_delim = ','; - std::shared_ptr yelp_review_op = std::make_shared( - num_workers_, num_samples_, worker_connector_size_, connector_que_size_, shuffle_files, num_shards_, shard_id_, - field_delim, column_default, column_name, yelp_review_files_list_); - RETURN_IF_NOT_OK(yelp_review_op->Init()); - - // If a global shuffle is used for YelpReview, it will inject a shuffle op over the YelpReview. - // But, if there is a cache in the tree, we do not need the global shuffle and the shuffle op should not be - // built.This is achieved in the cache transform pass where we call MakeSimpleProducer to reset YelpReview's - // shuffle option to false. - if (shuffle_ == ShuffleMode::kGlobal) { - // Inject ShuffleOp. - std::shared_ptr shuffle_op = nullptr; - int64_t num_rows = 0; - - // First, get the number of rows in the dataset. - RETURN_IF_NOT_OK(YelpReviewOp::CountAllFileRows(yelp_review_files_list_, false, &num_rows)); - // Add the shuffle op after this op. - RETURN_IF_NOT_OK( - AddShuffleOp(sorted_dataset_files.size(), num_shards_, num_rows, 0, connector_que_size_, &shuffle_op)); - shuffle_op->SetTotalRepeats(GetTotalRepeats()); - shuffle_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - shuffle_op->Skip(skip_steps_); - node_ops->push_back(shuffle_op); - } - yelp_review_op->SetTotalRepeats(GetTotalRepeats()); - yelp_review_op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(yelp_review_op); - return Status::OK(); -} - -Status YelpReviewNode::GetShardId(int32_t *shard_id) { - *shard_id = shard_id_; - return Status::OK(); -} - -Status YelpReviewNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - RETURN_IF_NOT_OK(YelpReviewOp::CountAllFileRows(yelp_review_files_list_, false, &num_rows)); - sample_size = num_samples_; - num_rows = static_cast(ceil(num_rows / (1.0 * num_shards_))); - *dataset_size = sample_size > 0 ? std::min(num_rows, sample_size) : num_rows; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status YelpReviewNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - args["usage"] = usage_; - args["num_samples"] = num_samples_; - args["shuffle"] = shuffle_; - args["num_shards"] = num_shards_; - args["shard_id"] = shard_id_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} - -Status YelpReviewNode::SetupSamplerForCache(std::shared_ptr *sampler) { - *sampler = SelectSampler(num_samples_, shuffle_, num_shards_, shard_id_); - return Status::OK(); -} - -Status YelpReviewNode::MakeSimpleProducer() { - shard_id_ = 0; - num_shards_ = 1; - shuffle_ = ShuffleMode::kFalse; - num_samples_ = 0; - return Status::OK(); -} - -std::vector YelpReviewNode::WalkAllFiles(const std::string &usage, const std::string &dataset_dir) { - std::vector yelp_review_files_list; - Path train_prefix("train.csv"); - Path test_prefix("test.csv"); - Path dir(dataset_dir); - - if (usage == "train") { - Path temp_path = dir / train_prefix; - yelp_review_files_list.push_back(temp_path.ToString()); - } else if (usage == "test") { - Path temp_path = dir / test_prefix; - yelp_review_files_list.push_back(temp_path.ToString()); - } else { - Path temp_path = dir / train_prefix; - yelp_review_files_list.push_back(temp_path.ToString()); - Path temp_path1 = dir / test_prefix; - yelp_review_files_list.push_back(temp_path1.ToString()); - } - return yelp_review_files_list; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h deleted file mode 100644 index cff0e8988..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YELP_REVIEW_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YELP_REVIEW_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yelp_review_op.h" - -namespace mindspore { -namespace dataset { -/// \class YelpReviewNode -/// \brief A Node derived class to represent YelpReview Node. -class YelpReviewNode : public NonMappableSourceNode { - public: - /// \brief Constructor of YelpReviewNode. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of YelpReview, can be "train", "test" or "all" data. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - YelpReviewNode(const std::string &dataset_dir, const std::string &usage, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor. - ~YelpReviewNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return kYelpReviewNode; } - - /// \brief Print the description. - /// \param[out] out The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief A base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id The shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size The size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - const std::string &Usage() const { return usage_; } - int64_t NumSamples() const { return num_samples_; } - ShuffleMode Shuffle() const { return shuffle_; } - int32_t NumShards() const { return num_shards_; } - int32_t ShardId() const { return shard_id_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief YelpReview by itself is a non-mappable dataset that does not support sampling. - /// However, if a cache operator is injected at some other place higher in the tree, that cache can - /// inherit this sampler from the leaf, providing sampling support from the caching layer. - /// That is why we setup the sampler for a leaf node that does not use sampling. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \param[in] sampler The sampler to setup. - /// \return Status of the function. - Status SetupSamplerForCache(std::shared_ptr *sampler) override; - - /// \brief If a cache has been added into the ascendant tree over this clue node, then the cache will be executing - /// a sampler for fetching the data. As such, any options in the clue node need to be reset to its defaults so. - /// that this clue node will produce the full set of data into the cache. - /// Note: This function is common among NonMappableSourceNode and should be promoted to its parent class. - /// \return Status of the function. - Status MakeSimpleProducer() override; - - /// \brief Generate a list of read file names according to usage. - /// \param[in] usage Part of dataset of YelpReview. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \return std::vector A list of read file names. - std::vector WalkAllFiles(const std::string &usage, const std::string &dataset_dir); - - private: - std::string dataset_dir_; - std::string usage_; - std::vector> column_defaults_; - std::vector column_names_; - int64_t num_samples_; - ShuffleMode shuffle_; - int32_t num_shards_; - int32_t shard_id_; - std::vector yelp_review_files_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YELP_REVIEW_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.cc deleted file mode 100644 index 697792bde..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.cc +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/yes_no_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for YesNoNode. -YesNoNode::YesNoNode(const std::string &dataset_dir, std::shared_ptr sampler, - std::shared_ptr cache) - : MappableSourceNode(std::move(cache)), dataset_dir_(dataset_dir), sampler_(sampler) {} - -std::shared_ptr YesNoNode::Copy() { - std::shared_ptr sampler = (sampler_ == nullptr) ? nullptr : sampler_->SamplerCopy(); - auto node = std::make_shared(dataset_dir_, sampler, cache_); - return node; -} - -void YesNoNode::Print(std::ostream &out) const { - out << (Name() + "(cache: " + ((cache_ != nullptr) ? "true" : "false") + ")"); -} - -Status YesNoNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - RETURN_IF_NOT_OK(ValidateDatasetDirParam("YesNoNode", dataset_dir_)); - RETURN_IF_NOT_OK(ValidateDatasetSampler("YesNoNode", sampler_)); - - return Status::OK(); -} - -Status YesNoNode::Build(std::vector> *const node_ops) { - // Do internal Schema generation. - auto schema = std::make_unique(); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("waveform", DataType(DataType::DE_FLOAT32), TensorImpl::kFlexible, 1))); - TensorShape sample_rate_scalar = TensorShape::CreateScalar(); - TensorShape lable_scalar = TensorShape::CreateScalar(); - RETURN_IF_NOT_OK(schema->AddColumn( - ColDescriptor("sample_rate", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &sample_rate_scalar))); - RETURN_IF_NOT_OK( - schema->AddColumn(ColDescriptor("label", DataType(DataType::DE_INT32), TensorImpl::kFlexible, 0, &lable_scalar))); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - - auto op = std::make_shared(dataset_dir_, num_workers_, connector_que_size_, std::move(schema), - std::move(sampler_rt)); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - - return Status::OK(); -} - -Status YesNoNode::GetShardId(int32_t *shard_id) { - *shard_id = sampler_->ShardId(); - return Status::OK(); -} - -Status YesNoNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - - int64_t num_rows, sample_size; - std::vector> ops; - RETURN_IF_NOT_OK(Build(&ops)); - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build YesNoOp."); - auto op = std::dynamic_pointer_cast(ops.front()); - RETURN_IF_NOT_OK(op->CountTotalRows(&num_rows)); - std::shared_ptr sampler_rt = nullptr; - RETURN_IF_NOT_OK(sampler_->SamplerBuild(&sampler_rt)); - sample_size = sampler_rt->CalculateNumSamples(num_rows); - if (sample_size == -1) { - RETURN_IF_NOT_OK(size_getter->DryRun(shared_from_this(), &sample_size)); - } - *dataset_size = sample_size; - dataset_size_ = *dataset_size; - return Status::OK(); -} - -Status YesNoNode::to_json(nlohmann::json *out_json) { - nlohmann::json args, sampler_args; - RETURN_IF_NOT_OK(sampler_->to_json(&sampler_args)); - args["sampler"] = sampler_args; - args["num_parallel_workers"] = num_workers_; - args["connector_queue_size"] = connector_que_size_; - args["dataset_dir"] = dataset_dir_; - if (cache_ != nullptr) { - nlohmann::json cache_args; - RETURN_IF_NOT_OK(cache_->to_json(&cache_args)); - args["cache"] = cache_args; - } - *out_json = args; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h deleted file mode 100644 index 970b62343..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/source/yes_no_node.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YES_NO_NODE_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YES_NO_NODE_H - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class YesNoNode : public MappableSourceNode { - public: - /// \brief Constructor. - YesNoNode(const std::string &dataset_dir, std::shared_ptr sampler, std::shared_ptr cache); - - /// \brief Destructor. - ~YesNoNode() override = default; - - /// \brief Node name getter. - /// \return Name of the current node. - std::string Name() const override { return "YesNoNode"; } - - /// \brief Print the description. - /// \param out - The output stream to write output to. - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object. - /// \return A shared pointer to the new copy. - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class. - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create. - /// \return Status Status::OK() if build successfully. - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation. - /// \return Status Status::OK() if all the parameters are valid. - Status ValidateParams() override; - - /// \brief Get the shard id of node. - /// \param[in] shard_id Shard id. - /// \return Status Status::OK() if get shard id successfully. - Status GetShardId(int32_t *shard_id) override; - - /// \brief Base-class override for GetDatasetSize. - /// \param[in] size_getter Shared pointer to DatasetSizeGetter. - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset. - /// \return Status of the function. - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Getter functions. - const std::string &DatasetDir() const { return dataset_dir_; } - - /// \brief Get the arguments of node. - /// \param[out] out_json JSON string of all attributes. - /// \return Status of the function. - Status to_json(nlohmann::json *out_json) override; - - /// \brief Sampler getter. - /// \return SamplerObj of the current node. - std::shared_ptr Sampler() override { return sampler_; } - - /// \brief Sampler setter. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - void SetSampler(std::shared_ptr sampler) override { sampler_ = sampler; } - - private: - std::string dataset_dir_; - std::shared_ptr sampler_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SOURCE_YES_NO_NODE_H diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.cc deleted file mode 100644 index 71240808a..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.cc +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h" - -#include "mindspore-lite/minddata/dataset/engine/datasetops/barrier_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for SyncWaitNode -SyncWaitNode::SyncWaitNode(std::shared_ptr child, const std::string &condition_name, py::function callback) - : condition_name_(condition_name), callback_(callback) { - this->AddChild(child); -} - -std::shared_ptr SyncWaitNode::Copy() { - auto node = std::make_shared(nullptr, condition_name_, callback_); - return node; -} - -void SyncWaitNode::Print(std::ostream &out) const { - out << (Name() + "(cond_name:" + condition_name_ + "" + ")"); -} - -// Function to build the BarrierOp -Status SyncWaitNode::Build(std::vector> *const node_ops) { - // The reason for this is because having it otherwise can lead to blocking issues - // See barrier_op.h for more details - auto op = std::make_shared(connector_que_size_, condition_name_, callback_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Function to validate the parameters for SyncWaitNode -Status SyncWaitNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h deleted file mode 100644 index fa2be5d6d..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SYNC_WAIT_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SYNC_WAIT_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -/// \class SyncWaitNode -/// \brief A Dataset derived class to represent SyncWaitNode dataset -class SyncWaitNode : public DatasetNode { - public: - /// \brief Constructor - SyncWaitNode(std::shared_ptr child, const std::string &condition_name, py::function callback); - - /// \brief Destructor - ~SyncWaitNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kSyncWaitNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter functions - const std::string &ConditionName() const { return condition_name_; } - const py::function &Callback() const { return callback_; } - - private: - std::string condition_name_; - py::function callback_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_SYNC_WAIT_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.cc deleted file mode 100644 index 9bda5b9bd..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.cc +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/take_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Constructor for TakeNode -TakeNode::TakeNode(std::shared_ptr child, int32_t count) : take_count_(count) { this->AddChild(child); } - -std::shared_ptr TakeNode::Copy() { - auto node = std::make_shared(nullptr, take_count_); - return node; -} - -void TakeNode::Print(std::ostream &out) const { out << (Name() + "(num_rows:" + std::to_string(take_count_) + ")"); } - -// Function to build the TakeOp -Status TakeNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(take_count_); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Function to validate the parameters for TakeNode -Status TakeNode::ValidateParams() { - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (take_count_ <= 0 && take_count_ != -1) { - std::string err_msg = - "TakeNode: 'take_count' should be either -1 or positive integer, but got: " + std::to_string(take_count_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -// Get Dataset size -Status TakeNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - int64_t num_rows; - RETURN_IF_NOT_OK(children_[0]->GetDatasetSize(size_getter, estimate, &num_rows)); - *dataset_size = take_count_ == -1 ? num_rows : std::min(static_cast(take_count_), num_rows); - dataset_size_ = *dataset_size; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status TakeNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status TakeNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status TakeNode::to_json(nlohmann::json *out_json) { - nlohmann::json args; - args["count"] = take_count_; - *out_json = args; - return Status::OK(); -} - -Status TakeNode::from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result) { - RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "count", kTakeNode)); - int32_t count = json_obj["count"]; - *result = std::make_shared(ds, count); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h deleted file mode 100644 index e7686fd49..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_TAKE_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_TAKE_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class TakeNode : public DatasetNode { - public: - /// \brief Constructor - explicit TakeNode(std::shared_ptr child, int32_t count); - - /// \brief Destructor - ~TakeNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kTakeNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Getter - /// \return Number of rows to output - int32_t Count() const { return take_count_; } - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Getter functions - int32_t TakeCount() const { return take_count_; } - - /// \brief Get the arguments of node - /// \param[out] out_json JSON string of all attributes - /// \return Status of the function - Status to_json(nlohmann::json *out_json) override; - - /// \brief Function for read dataset operation from json - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] ds dataset node constructed - /// \param[out] result Deserialized dataset after the operation - /// \return Status The status code returned - static Status from_json(nlohmann::json json_obj, std::shared_ptr ds, - std::shared_ptr *result); - - private: - int32_t take_count_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_TAKE_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.cc b/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.cc deleted file mode 100644 index 80b19d9fa..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.cc +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/datasetops/zip_op.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { - -ZipNode::ZipNode(const std::vector> &datasets) { - nary_op_ = true; - for (auto const &child : datasets) { - AddChild(child); - } -} - -std::shared_ptr ZipNode::Copy() { - std::vector> empty_vector; - empty_vector.clear(); - auto node = std::make_shared(empty_vector); - return node; -} - -void ZipNode::Print(std::ostream &out) const { out << Name(); } - -Status ZipNode::ValidateParams() { - constexpr size_t kMinChildrenSize = 2; - RETURN_IF_NOT_OK(DatasetNode::ValidateParams()); - if (children_.size() < kMinChildrenSize) { - std::string err_msg = "ZipNode: input datasets are not specified."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (find(children_.begin(), children_.end(), nullptr) != children_.end()) { - std::string err_msg = "ZipNode: input datasets should not be null."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -Status ZipNode::Build(std::vector> *const node_ops) { - auto op = std::make_shared(); - op->SetTotalRepeats(GetTotalRepeats()); - op->SetNumRepeatsPerEpoch(GetNumRepeatsPerEpoch()); - node_ops->push_back(op); - return Status::OK(); -} - -// Get Dataset size -Status ZipNode::GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) { - if (dataset_size_ > 0) { - *dataset_size = dataset_size_; - return Status::OK(); - } - std::vector dataset_sizes; - int64_t child_dataset_size; - for (auto child : children_) { - RETURN_IF_NOT_OK(child->GetDatasetSize(size_getter, estimate, &child_dataset_size)); - dataset_sizes.push_back(child_dataset_size); - } - - *dataset_size = *std::min_element(dataset_sizes.begin(), dataset_sizes.end()); - dataset_size_ = *dataset_size; - return Status::OK(); -} - -// Visitor accepting method for IRNodePass -Status ZipNode::Accept(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->Visit(shared_from_base(), modified); -} - -// Visitor accepting method for IRNodePass -Status ZipNode::AcceptAfter(IRNodePass *const p, bool *const modified) { - // Downcast shared pointer then call visitor - return p->VisitAfter(shared_from_base(), modified); -} - -Status ZipNode::from_json(std::vector> datasets, std::shared_ptr *result) { - *result = std::make_shared(datasets); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h b/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h deleted file mode 100644 index d7b03f0cb..000000000 --- a/mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ZIP_NODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ZIP_NODE_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { -class ZipNode : public DatasetNode { - public: - /// \brief Constructor - explicit ZipNode(const std::vector> &datasets); - - /// \brief Destructor - ~ZipNode() override = default; - - /// \brief Node name getter - /// \return Name of the current node - std::string Name() const override { return kZipNode; } - - /// \brief Print the description - /// \param out - The output stream to write output to - void Print(std::ostream &out) const override; - - /// \brief Copy the node to a new object - /// \return A shared pointer to the new copy - std::shared_ptr Copy() override; - - /// \brief a base class override function to create the required runtime dataset op objects for this class - /// \param node_ops - A vector containing shared pointer to the Dataset Ops that this object will create - /// \return Status Status::OK() if build successfully - Status Build(std::vector> *const node_ops) override; - - /// \brief Parameters validation - /// \return Status Status::OK() if all the parameters are valid - Status ValidateParams() override; - - /// \brief Base-class override for GetDatasetSize - /// \param[in] size_getter Shared pointer to DatasetSizeGetter - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \param[out] dataset_size the size of the dataset - /// \return Status of the function - Status GetDatasetSize(const std::shared_ptr &size_getter, bool estimate, - int64_t *dataset_size) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status Accept(IRNodePass *const p, bool *const modified) override; - - /// \brief Base-class override for accepting IRNodePass visitor - /// \param[in] p The node to visit - /// \param[out] modified Indicator if the node was modified - /// \return Status of the node visit - Status AcceptAfter(IRNodePass *const p, bool *const modified) override; - - /// \brief Function to read dataset in json - /// \param[in] datasets A vector of datasets for Zip input - /// \param[out] result Deserialized dataset - /// \return Status The status code returned - static Status from_json(std::vector> datasets, std::shared_ptr *result); - - private: - std::vector> datasets_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_IR_DATASETOPS_ZIP_NODE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/jagged_connector.h b/mindspore-lite/minddata/dataset/engine/jagged_connector.h deleted file mode 100644 index a6af336ad..000000000 --- a/mindspore-lite/minddata/dataset/engine/jagged_connector.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_JAGGED_CONNECTOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_JAGGED_CONNECTOR_H_ - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/connector.h" - -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -class JaggedConnector : public Connector { - public: - JaggedConnector(int32_t num_producers, int32_t num_consumers, int32_t queue_capacity) - : Connector(num_producers, num_consumers, queue_capacity) { - for (int i = 0; i < num_producers; i++) { - is_queue_finished_.push_back(false); - } - } - - ~JaggedConnector() = default; - - Status Add(int32_t worker_d, TensorRow &&element) noexcept { - return Connector::Push(worker_d, std::move(element)); - } - - Status Pop(int32_t worker_id, TensorRow *result) noexcept override { - RETURN_UNEXPECTED_IF_NULL(result); - { - MS_ASSERT(worker_id < num_consumers_); - std::unique_lock lock(m_); - RETURN_IF_NOT_OK(cv_.Wait(&lock, [this, worker_id]() { return expect_consumer_ == worker_id; })); - if (is_queue_finished_[pop_from_]) { - std::string errMsg = "ERROR: popping from a finished queue in JaggedConnector"; - RETURN_STATUS_UNEXPECTED(errMsg); - } - - RETURN_IF_NOT_OK(queues_[pop_from_]->PopFront(result)); - if (result != nullptr && result->eoe()) { - is_queue_finished_[pop_from_] = true; - } - - for (int offset = 1; offset <= num_producers_; offset++) { - size_t nextQueueIndex = (pop_from_ + offset) % num_producers_; - if (!is_queue_finished_[nextQueueIndex]) { - pop_from_ = nextQueueIndex; - break; - } - } - - expect_consumer_ = (expect_consumer_ + 1) % num_consumers_; - } - - cv_.NotifyAll(); - return Status::OK(); - } - - void DoReset() { - for (auto i = 0; i < is_queue_finished_.size(); i++) { - is_queue_finished_[i] = false; - } - - Connector::Reset(); - } - - private: - std::vector is_queue_finished_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_JAGGED_CONNECTOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/operator_connector.h b/mindspore-lite/minddata/dataset/engine/operator_connector.h deleted file mode 100644 index 70400c072..000000000 --- a/mindspore-lite/minddata/dataset/engine/operator_connector.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPERATOR_CONNECTOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPERATOR_CONNECTOR_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/engine/connector.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { - -class OperatorConnector : public Queue { - public: - /// Constructor of OperatorConnector - /// \param queue_capacity The number of element (TensorRows) for the queue. - explicit OperatorConnector(int32_t queue_capacity) : Queue(queue_capacity), out_rows_count_(0) {} - - /// Destructor of -OperatorConnector - ~OperatorConnector() = default; - - Status PopFront(TensorRow *row) override { - out_rows_count_++; - return Queue::PopFront(row); - } - - Status SendEOE() noexcept { - TensorRow eoe = TensorRow(TensorRow::kFlagEOE); - return Add(std::move(eoe)); - } - - Status SendEOF() noexcept { - TensorRow eof = TensorRow(TensorRow::kFlagEOF); - return Add(std::move(eof)); - } - - Status SendEOB() noexcept { - TensorRow eob = TensorRow(TensorRow::kFlagEOB); - return Add(std::move(eob)); - } - - auto out_rows_count() const { return out_rows_count_; } - - private: - int64_t out_rows_count_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPERATOR_CONNECTOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pass.cc deleted file mode 100644 index 06a16d68e..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pass.cc +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/build_vocab_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_merge_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/cache_lookup_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h" -#endif -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/random_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/sync_wait_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h" - -namespace mindspore { -namespace dataset { - -// Driver method for TreePass -Status IRTreePass::Run(std::shared_ptr root_ir, bool *const modified) { - if (root_ir == nullptr || modified == nullptr) { - return Status(StatusCode::kMDUnexpectedError, "Null pointer passed to TreePass"); - } - // Initialize modified flag - *modified = false; - return this->RunOnTree(root_ir, modified); -} - -// Driver method for NodePass -Status IRNodePass::Run(std::shared_ptr root_ir, bool *const modified) { - if (root_ir == nullptr || modified == nullptr) { - return Status(StatusCode::kMDUnexpectedError, "Null pointer passed to NodePass"); - } - // Initialize modified flag - *modified = false; - if (traversalOrder_ == Order::DFS) { - // DFS - return DFSNodeVisit(root_ir, modified); - } else if (traversalOrder_ == Order::BFS) { - // BFS - return BFSNodeVisit(root_ir, modified); - } - return Status::OK(); -} - -// Helper function to perform DFS visit -Status IRNodePass::DFSNodeVisit(std::shared_ptr node_ir, bool *const modified) { - bool m = false; - - RETURN_IF_NOT_OK(node_ir->Accept(this, &m)); - *modified = *modified || m; - for (const auto &c : node_ir->Children()) { - RETURN_IF_NOT_OK(this->DFSNodeVisit(c, &m)); - *modified = *modified || m; - } - RETURN_IF_NOT_OK(node_ir->AcceptAfter(this, &m)); - *modified = *modified || m; - return Status::OK(); -} - -// Helper function to perform BFS visit -Status IRNodePass::BFSNodeVisit(std::shared_ptr node_ir, bool *const modified) { - bool m = false; - - // Initialize bfs queue with root - std::queue> bfsQueue; - bfsQueue.push(node_ir); - - // BFS loop - while (!bfsQueue.empty()) { - // Pop the front of the bfs queue - auto curNode = bfsQueue.front(); - bfsQueue.pop(); - - // Run node pass - RETURN_IF_NOT_OK(curNode->Accept(this, &m)); - *modified = *modified || m; - - // Push children into bfs queue - for (const auto &c : curNode->Children()) { - bfsQueue.push(c); - } - } - return Status::OK(); -} - -// For non-leaf IR node -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#ifndef ENABLE_ANDROID -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#endif -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#ifdef ENABLE_PYTHON -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#endif -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#ifndef ENABLE_ANDROID -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#endif -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#ifdef ENABLE_PYTHON -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#endif -#ifndef ENABLE_ANDROID -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -Status IRNodePass::VisitAfter(std::shared_ptr node, bool *const modified) { - return VisitAfter(std::static_pointer_cast(node), modified); -} -#endif - -// leaf-IR Node -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} - -Status IRNodePass::Visit(std::shared_ptr node, bool *const modified) { - return Visit(std::static_pointer_cast(node), modified); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pass.h b/mindspore-lite/minddata/dataset/engine/opt/pass.h deleted file mode 100644 index 587a50bfc..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pass.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Non-leaf IR node -class BatchNode; -class BucketBatchByLengthNode; -class BuildVocabNode; -#ifndef ENABLE_ANDROID -class CacheLookupNode; -class CacheMergeNode; -class CacheNode; -#endif -class ConcatNode; -class EpochCtrlNode; -class FilterNode; -class MapNode; -class ProjectNode; -class RenameNode; -class RepeatNode; -class RootNode; -class ShuffleNode; -class SkipNode; -class TakeNode; -class TFRecordNode; -class DataQueueNode; -class ZipNode; -#ifdef ENABLE_PYTHON -class SyncWaitNode; -#endif -#ifndef ENABLE_ANDROID -class BuildSentenceVocabNode; -#endif -// Leaf IR node -class AlbumNode; -class CelebANode; -class Cifar100Node; -class Cifar10Node; -class CocoNode; -class ImageFolderNode; -class ManifestNode; -class MnistNode; -class RandomNode; -class VOCNode; -#ifdef ENABLE_PYTHON -class GeneratorNode; -#endif -#ifndef ENABLE_ANDROID -class CLUENode; -class CSVNode; -class MindDataNode; -class TextFileNode; -class TFRecordNode; -#endif - -// The base class Pass is the basic unit of tree transformation. -// The actual implementation of the passes will be derived from here. -class IRPass : public std::enable_shared_from_this { - public: - // Run the transformation pass against the IR tree. - // @param root_ir - Pointer to the IR tree to be transformed. - // @param modified - Pointer to the modified flag, - virtual Status Run(std::shared_ptr root_ir, bool *const modified) = 0; - - virtual ~IRPass() = default; -}; - -// IRTreePass is a basic Pass class which performs transformation on IR tree directly. -class IRTreePass : public IRPass { - public: - /// \brief Run the transformation pass against the IR tree. - /// \param[in,out] root_ir Pointer to the IR tree to be transformed. - /// \param[in,out] modified Indicate if the tree was modified - Status Run(std::shared_ptr root_ir, bool *const modified) final; - - /// \brief Derived classes may implement the runOnTree function to implement tree transformation. - /// "modified" flag needs to be set to true if tree is modified during the pass execution. - /// \param[in,out] tree The tree to operate on. - /// \param[in,out] Indicate if the tree was modified. - /// \return Status The status code returned - virtual Status RunOnTree(std::shared_ptr root_ir, bool *const modified) { return Status::OK(); } -}; - -// IRNodePass is a base Pass class which performs transformation on node visiting. -// IRNodePass implements Visitor design pattern. -// The visiting happens twice for each node in the DFS traversal, one on the way down of the traversal, -// and the other when all the descending nodes are visited. -// Actual transformation is done by implementing a new derived class of IRNodePass. -// The derived class will implement the method Visit()/VisitAfter() passing specified node types -// it wants to action on them, overriding the ones defined in IRNodePass. -// If the derived class wants to perform the same action on all node types, -// it can simply implement the method Visit()/VisitAfter() passing the base class DatasetNode. -// This is made possible by overloading the method Visit()/VisitAfter() on each node type to fall back -// to call the Visit()/VisitAfter() in this parent IRNodePass class. -class IRNodePass : public IRPass { - public: - // Tree traversal order - enum Order { DFS, BFS }; - - // Constructor - // Default DFS traversal - explicit IRNodePass(Order order = Order::DFS) { traversalOrder_ = order; } - - ~IRNodePass() override = default; - - /// \brief Run the transformation pass against the IR tree - /// \param[in,out] root_ir Pointer to the IR tree to be transformed - /// \param[in,out] modified Indicator if the tree was changed - Status Run(std::shared_ptr root_ir, bool *const modified) final; - - /// \brief Derived classes may implement the Visit function to implement any initial visit work on the way down - /// a tree traversal. "modified" flag needs to be set to true if node is modified during the pass execution - /// \param[in] node The node being visited - /// \param[out] modified Indicator if the node was changed at all - /// \return Status The status code returned - virtual Status Visit(std::shared_ptr node, bool *const modified) { return Status::OK(); } - - /// \brief Derived classes may implement the VisitAfter function to implement node level tree transformation - /// "modified" flag needs to be set to true if node is modified during the pass execution - /// \param[in] node The node being visited - /// \param[out] modified Indicator if the node was changed at all. - /// \return Status The status code returned - virtual Status VisitAfter(std::shared_ptr node, bool *const modified) { return Status::OK(); } - - // Visit()/VisitAfter() method to be overridden. - // These pairs of Visit()/VisitAfter() for each derived class of DatasetNode are defined here. - // Their implementation are in .cc file to avoid adding the include files of those derived classes. - // The implementation simply falls back to call Visit()/VisitAfter of class DatasetNode, the parent of - // the derived classes. With this technique, the transformation classes derived from NodePass needs only to - // implement Visit()/VisitAfter() passing DatasetNode if it wants to action on any derived classes - // of DatasetNode in the same way. - // Note that virtual template functions are not permitted in C++. - // - // Non-leaf IR node - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#ifndef ENABLE_ANDROID - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#endif - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#ifndef ENABLE_ANDROID - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#endif - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#ifdef ENABLE_PYTHON - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#endif - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#ifndef ENABLE_ANDROID - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#endif - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#ifdef ENABLE_PYTHON - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); -#endif - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status VisitAfter(std::shared_ptr node, bool *const modified); - - // leaf-IR Node - virtual Status Visit(std::shared_ptr node, bool *const modified); - virtual Status Visit(std::shared_ptr node, bool *const modified); - - private: - // Helper function to perform DFS visit - Status DFSNodeVisit(std::shared_ptr node_ir, bool *const modified); - - // Helper function to perform BFS visit - Status BFSNodeVisit(std::shared_ptr node_ir, bool *const modified); - - // Tree traversal order of the NodePass - Order traversalOrder_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.cc deleted file mode 100644 index a3417111b..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.cc +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" - -namespace mindspore { -namespace dataset { - -// this will become the RootNode:DatasetNode when it is turned on -Status AutoWorkerPass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - RETURN_UNEXPECTED_IF_NULL(modified); - uint8_t config = GlobalContext::config_manager()->get_auto_worker_config(); - - OpWeightPass pass(kOpWeightConfigs[config < kOpWeightConfigs.size() ? config : 0]); - - std::string weight_str; - for (const auto &p : pass.weight_profile_) { - weight_str += ("(" + p.first + "=" + std::to_string(p.second) + ")"); - } - int32_t num_shards = GlobalContext::config_manager()->get_num_shards_for_auto_num_workers(); - num_shards = std::min(std::max(1, num_shards), thread_cnt_); - - MS_LOG(INFO) << "AutoWorkerPass is enabled; this could override existing num_workers set in each parallel op." - << "total number of threads on this CPU: " << thread_cnt_ << ", " - << "min num_workers to override:" << min_num_workers_ << ", " - << "max num_workers to override:" << max_num_workers_ << ", " - << "adjusted num_shards (between 1 and total thread cnt): " << num_shards - << ", weight profile:" << weight_str << "."; - - // get the maximum weight of all the ops, this value is used to ensure the ratio of num_workers between ops - float max_weight = 0; - for (const auto &p : pass.weight_profile_) { - max_weight = std::max(max_weight, p.second); - } - - CHECK_FAIL_RETURN_UNEXPECTED(std::fabs(max_weight) > std::numeric_limits::epsilon(), - "Internal error, doesn't allow divide zero."); - RETURN_IF_NOT_OK(pass.Run(root_ir, modified)); - constexpr size_t max_num_ops = 3; - if (pass.parallel_ops_.size() > max_num_ops) { - MS_LOG(WARNING) << "AutoNumWorker right now is only suitable for simple dataset pipelines that has at most, 1 leaf " - << "1 batch and 1 map. AutoNumWorker may not be optimal for usage on complex pipelines."; - } - - CHECK_FAIL_RETURN_UNEXPECTED(pass.weight_sum_ != 0, "Internal error, doesn't allow divide zero."); - for (auto &p : pass.parallel_ops_) { - // get the num worker via the weight ratio - int32_t num_workers = std::ceil((thread_cnt_ * p.second) / (pass.weight_sum_ * num_shards)); - // this is to ensure when thread_cnt_ is very large let's say 192, the num_worker ratio is still kept - // e.g. the optional 2:1 ratio between minddataset and batch - int32_t cur_node_max = std::ceil(p.second * max_num_workers_ / max_weight); - // this will ensure that num_workers will fall with the range of [1,cur_node_max] - int32_t cur_node_num_worker = std::max(std::min(num_workers, cur_node_max), min_num_workers_); - - // if the num_worker to set is same as original, skip setting and printing the logs - if (cur_node_num_worker == p.first->NumWorkers()) { - continue; - } - // log the change via warning msg so user can see what the num_worker is being set for which op - MS_LOG(WARNING) << "AutoNumWorker enabled, num_workers in " << p.first->Name() << " is auto-adjusted from " - << std::to_string(p.first->NumWorkers()) + " to " + std::to_string(cur_node_num_worker); - p.first->SetNumWorkers(cur_node_num_worker); - } - return Status::OK(); -} - -Status AutoWorkerPass::OpWeightPass::Visit(std::shared_ptr node, bool *const modified) { - auto itr = weight_profile_.find(node->Name()); - CHECK_FAIL_RETURN_UNEXPECTED(itr != weight_profile_.end(), node->Name() + "'s weight doesn't exist."); - int32_t weight = itr->second; - weight_sum_ += weight; - parallel_ops_.emplace_back(std::make_pair(std::static_pointer_cast(node), weight)); - return Status::OK(); -} - -Status AutoWorkerPass::OpWeightPass::Visit(std::shared_ptr node, bool *const modified) { - auto itr = weight_profile_.find(node->Name()); - CHECK_FAIL_RETURN_UNEXPECTED(itr != weight_profile_.end(), node->Name() + "'s weight doesn't exist."); - int32_t weight = itr->second; - weight_sum_ += weight; - parallel_ops_.emplace_back(std::make_pair(std::static_pointer_cast(node), weight)); - return Status::OK(); -} - -Status AutoWorkerPass::OpWeightPass::Visit(std::shared_ptr node, bool *const modified) { - RETURN_OK_IF_TRUE(node->Name() == kGeneratorNode); // generator is pipeline op, skip this - auto itr = weight_profile_.find("MappableSource"); - CHECK_FAIL_RETURN_UNEXPECTED(itr != weight_profile_.end(), - "LeafSourceNode::" + node->Name() + "'s weight doesn't exist."); - int32_t weight = itr->second; - weight_sum_ += weight; - parallel_ops_.emplace_back(std::make_pair(std::static_pointer_cast(node), weight)); - return Status::OK(); -} - -Status AutoWorkerPass::OpWeightPass::Visit(std::shared_ptr node, bool *const modified) { - auto itr = weight_profile_.find("NonMappableSource"); - CHECK_FAIL_RETURN_UNEXPECTED(itr != weight_profile_.end(), - "NonLeafSource::" + node->Name() + "'s weight doesn't exist."); - int32_t weight = itr->second; - weight_sum_ += weight; - parallel_ops_.emplace_back(std::make_pair(std::static_pointer_cast(node), weight)); - return Status::OK(); -} - -Status AutoWorkerPass::OpWeightPass::Visit(std::shared_ptr node, bool *const modified) { - weight_sum_ += GetNodeWeightFromProfile(node); - return Status::OK(); -} - -float AutoWorkerPass::OpWeightPass::GetNodeWeightFromProfile(std::shared_ptr node) { - auto itr = weight_profile_.find(node->Name()); - // returns 0 if name doesn't exist in the weight profile - return itr == weight_profile_.end() ? 0 : itr->second; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h b/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h deleted file mode 100644 index 52dafd8c8..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_OPT_POST_AUTO_WORKER_PASS_H_ -#define DATASET_ENGINE_OPT_POST_AUTO_WORKER_PASS_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -class AutoWorkerPass : public IRTreePass { - public: - // this map will contain weight for the basic pipeline ops. Pipeline op takes up 1 thread but doesn't have workers - const std::vector> kOpWeightConfigs = { - {{"MappableSource", 8}, {"NonMappableSource", 8}, {kBatchNode, 8}, {kMapNode, 8}}, // config1 leaf:batch:map=1:1:1 - {{"MappableSource", 8}, {"NonMappableSource", 8}, {kBatchNode, 4}, {kMapNode, 4}}, // config2 leaf:batch:map=2:1:1 - {{"MappableSource", 4}, {"NonMappableSource", 4}, {kBatchNode, 8}, {kMapNode, 4}}, // config3 leaf:batch:map=1:2:1 - {{"MappableSource", 4}, {"NonMappableSource", 4}, {kBatchNode, 4}, {kMapNode, 8}}, // config4 leaf:batch:map=1:1:2 - {{"MappableSource", 8}, {"NonMappableSource", 8}, {kBatchNode, 8}, {kMapNode, 4}}, // config5 leaf:batch:map=2:2:1 - {{"MappableSource", 8}, {"NonMappableSource", 8}, {kBatchNode, 4}, {kMapNode, 8}}, // config6 leaf:batch:map=2:1:2 - {{"MappableSource", 4}, {"NonMappableSource", 4}, {kBatchNode, 8}, {kMapNode, 8}}, // config7 leaf:batch:map=1:2:2 - }; - AutoWorkerPass() : min_num_workers_(1), thread_cnt_(GlobalContext::config_manager()->num_cpu_threads()) {} - - /// \brief destructor, by doing "= default", compiler will automatically generate the correct destructor - ~AutoWorkerPass() override = default; - - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; - - private: - class OpWeightPass : public IRNodePass { - public: - explicit OpWeightPass(const std::map &weight_profile) - : IRNodePass(), weight_sum_(0), weight_profile_(weight_profile) {} - - /// \brief destructor, by doing "= default", compiler will automatically generate the correct destructor - ~OpWeightPass() override = default; - - // this is the base class function which contains the logic to handle most of the pipeline ops - // pipeline ops although can't config num_workers it still runs 1 thread they need to be factored into weight - Status Visit(std::shared_ptr node, bool *const modified) override; - // these functions calculate the weights of more complex Nodes which may depend on its input arg. these functions - // will also push these nodes to a vector whose num_workers will be set int the Tree Pass - Status Visit(std::shared_ptr node, bool *const modified) override; - Status Visit(std::shared_ptr node, bool *const modified) override; - Status Visit(std::shared_ptr node, bool *const modified) override; - Status Visit(std::shared_ptr node, bool *const modified) override; - - // helper function to look up weight according to the name of this Op. - float GetNodeWeightFromProfile(std::shared_ptr node); - - int32_t weight_sum_; // sum of all weights in the pipeline - const std::map weight_profile_; // key: name of ir node, val: weight of this node - std::vector, float>> parallel_ops_; // first: node second: weight - }; - - const int32_t min_num_workers_; // minimum number of threads allowed for each op - const int32_t max_num_workers_ = 8; // maximum number of threads allowed for each op - const int32_t thread_cnt_; // thread cnt of current CPU, obtained through config manager -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_OPT_POST_AUTO_WORKER_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.cc deleted file mode 100644 index 074306079..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.cc +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// constructor -AddSkipPass::InjectionFinder::InjectionFinder(const std::shared_ptr &node) - : injection_point_(nullptr), has_shuffle_node_(false), has_batch_sampler_(false) {} - -// Performs finder work for BuildVocabOp that has special rules about skip injection -Status AddSkipPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - CHECK_FAIL_RETURN_UNEXPECTED(node->Children().size() > 0, - "Invalid data, the number of children should be greater than zero."); - // The injection is at the child of the root node - injection_point_ = node->Children()[0]; - num_epochs_ = node->NumEpochs(); - step_ = node->Step(); - dataset_size_ = node->DatasetSize(); - return Status::OK(); -} - -// Performs finder work for BuildVocabOp that has special rules about skip injection -Status AddSkipPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - injection_point_ = nullptr; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// Performs finder work for BuildSentencePieceVocabNode that has special rules about skip injection -Status AddSkipPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - injection_point_ = nullptr; - return Status::OK(); -} -#endif - -// Detect shuffle node -Status AddSkipPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - has_shuffle_node_ = true; - return Status::OK(); -} - -#ifdef ENABLE_PYTHON -// Detect batch sampler -Status AddSkipPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - if (node->HasBatchSampler()) { - has_batch_sampler_ = true; - } - return Status::OK(); -} -#endif - -Status AddSkipPass::InjectionFinder::VisitAfter(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - CHECK_FAIL_RETURN_UNEXPECTED(node->Children().size() > 0, - "Invalid data, the number of children should be greater than zero."); - // Assumption: There is only one DataQueueNode in a pipeline. This assumption is not validated here. - // Move the injection point to the child of this node. - injection_point_ = node->Children()[0]; - return Status::OK(); -} - -// Runs an injection pass to inject in operators needed at the pre pass stage -Status AddSkipPass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - RETURN_UNEXPECTED_IF_NULL(modified); - MS_LOG(INFO) << "Pre pass: AddSkipPass started."; - - // First, run the finder to perform any injection info before we can go ahead to drive the op injection work. - // The finder can make updates to the AddSkipPass object. - AddSkipPass::InjectionFinder finder(root_ir); - RETURN_IF_NOT_OK(finder.Run(root_ir, modified)); - // If we detect a shuffle node in the pipeline, we disable fast-recovery - if (GlobalContext::config_manager()->fast_recovery()) { - if (finder.HasShuffleNode()) { - GlobalContext::config_manager()->set_fast_recovery(false); - MS_LOG(WARNING) << "Disabling fast recovery of Dataset pipeline since shuffle node is detected."; - } - if (finder.HasBatchSampler()) { - GlobalContext::config_manager()->set_fast_recovery(false); - MS_LOG(WARNING) << "Disabling fast recovery of Dataset pipeline since batch_sampler is detected."; - } - } - // The first injection logic is to check if we should inject the skip op as the root node. - std::shared_ptr node = finder.injection_point(); - CHECK_FAIL_RETURN_UNEXPECTED(node != nullptr, "Failed to inject SkipOp."); - - int32_t num_epochs = finder.GetNumEpochs(); - int64_t step = finder.GetStep(); - int64_t dataset_size = finder.GetDatasetSize(); - CHECK_FAIL_RETURN_UNEXPECTED(dataset_size > 0, "Cannot reset the pipeline, dataset size is undefined"); - CHECK_FAIL_RETURN_UNEXPECTED(step >= 0, - "Cannot reset the pipeline, reset step must be >= 0. step: " + std::to_string(step)); - if (step >= dataset_size * num_epochs) { - std::string err_msg = "Cannot reset the pipeline, reset step must be less than dataset_size * num_epochs. step: " + - std::to_string(step) + ", dataset_size: " + std::to_string(dataset_size) + - ", num_epochs: " + std::to_string(num_epochs); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (step == 0) { - return Status::OK(); - } - // in fast recovery, we start from current epoch and skip remaining steps (skip node will also be pushed down) - if (GlobalContext::config_manager()->fast_recovery()) { - int64_t skip_num = step % dataset_size; - auto skip_node = std::make_shared(skip_num); - skip_node->SetOnceOnly(true); - RETURN_IF_NOT_OK(node->InsertAbove(skip_node)); - } else { // in non-fast we only add a skip node on top of the tree (to get same augmentations) - auto skip_node = std::make_shared(step); - skip_node->SetOnceOnly(true); - RETURN_IF_NOT_OK(node->InsertAbove(skip_node)); - } - - MS_LOG(INFO) << "Pre pass: AddSkipPass complete."; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h deleted file mode 100644 index 4b00b8872..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_ADD_SKIP_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_ADD_SKIP_PASS_H_ - -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { -class DatasetOp; - -/// \class AddSkipPass -/// \brief This is a pre pass that drives the injection of any nodes that could not be directly injected from the api -/// parsing. -class AddSkipPass : public IRTreePass { - /// \class InjectionFinder - /// \brief This is a nested node pass class whose job is to parse the tree and perform any identification logic for - /// operators that need to be injected. It is run first by the main injection pass to find out what operators - /// it may need to inject. - class InjectionFinder : public IRNodePass { - public: - /// \brief Constructor - explicit InjectionFinder(const std::shared_ptr &node); - - /// \brief Destructor - ~InjectionFinder() = default; - - /// \brief Performs finder work for RootNode that has special rules about skip injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Performs finder work for BuildVocabNode that has special rules about skip injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - -#ifndef ENABLE_ANDROID - /// \brief Performs finder work for BuildSentenceVocabNode that has special rules about skip injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; -#endif - - /// \brief Performs finder work for ShuffleNode that has special rules about skip injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - -#ifdef ENABLE_PYTHON - /// \brief Performs finder work for batch sampler that has special rules about skip injection. - /// \param[in] node The node being visited. - /// \param[in, out] modified Indicator if the node was changed at all. - /// \return Status The status code returned. - Status Visit(std::shared_ptr node, bool *const modified) override; -#endif - - /// \brief Register the DataQueueNode for further action. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Getter - std::shared_ptr injection_point() { return injection_point_; } - - int64_t GetStep() const { return step_; } - - int32_t GetNumEpochs() const { return num_epochs_; } - - int32_t GetDatasetSize() const { return dataset_size_; } - - bool HasShuffleNode() const { return has_shuffle_node_; } - - bool HasBatchSampler() const { return has_batch_sampler_; } - - private: - std::shared_ptr injection_point_; - int64_t step_ = 0; - int32_t num_epochs_ = 0; - int64_t dataset_size_ = -1; - bool has_shuffle_node_; - bool has_batch_sampler_; - }; - - public: - /// \brief Constructor - AddSkipPass() {} - - /// \brief Destructor - ~AddSkipPass() override = default; - - /// \brief Runs an injection pass to inject in operators needed at the pre pass stage - /// \param[in, out] tree The tree to operate on. - /// \param[in, out] Indicate of the tree was modified. - /// \return Status The status code returned - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_ADD_SKIP_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.cc deleted file mode 100644 index c85a3fc84..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.cc +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h" - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/filter_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { - -// Constructor -CacheValidationPass::CacheValidationPass() : is_cached_(false), is_mappable_(false) {} - -// Returns an error if BatchNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("BatchNode is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("BatchNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if ConcatNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("ConcatNode is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("ConcatNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if FilterNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("FilterNode is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("FilterNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if SkipNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("SkipNode is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("SkipNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if TakeNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("TakeNode (possibly from Split) is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("TakeNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if ZipNode exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("ZipNode is not supported as a descendant operator under a cache."); - } - if (node->IsCached()) { - RETURN_STATUS_UNEXPECTED("ZipNode cannot be cached."); - } - return Status::OK(); -} - -// Returns an error if MapNode with non-deterministic tensor operations exists under a cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (node->IsCached()) { - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("Nested cache operations over MapNode is not supported."); - } - // If Map is created to be cached, set the flag indicating we found an operation with a cache. - is_cached_ = true; - - // This is temporary code. - // Because the randomness of its tensor operations is not known in TensorOperation form until we convert them - // to TensorOp, we need to check the randomness in MapNode::Build(). - // By setting this MapNode is under a cache, we will check the randomness of its tensor operations without the need - // to walk the IR tree again. - node->HasCacheAbove(); - - auto tfuncs = node->TensorOperations(); - for (size_t i = 0; i < tfuncs.size(); i++) { - if (tfuncs[i]->IsRandomOp()) { - RETURN_STATUS_UNEXPECTED("MapNode containing random operation is not supported as a descendant of cache."); - } - } - } - return Status::OK(); -} - -// Flag an error if we have a cache over another cache -Status CacheValidationPass::Visit(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::Visit(): visiting " << node->Name() << "."; - if (node->IsCached()) { - if (is_cached_) { - RETURN_STATUS_UNEXPECTED("Nested cache operations over " + node->Name() + " is not supported."); - } - // If this node is created to be cached, set the flag. - is_cached_ = true; - } - if (node->IsLeaf() && node->IsMappableDataSource()) { - is_mappable_ = true; - } - return Status::OK(); -} - -// Returns an error if MappableSource <- Repeat <- Node with a cache -// Because there is no operator in the cache hit stream to consume EoEs, caching above repeat causes problem. -Status CacheValidationPass::VisitAfter(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::VisitAfter(): visiting " << node->Name() << "."; - if (is_cached_ && is_mappable_) { - RETURN_STATUS_UNEXPECTED("A cache over a RepeatNode of a mappable dataset is not supported."); - } - return Status::OK(); -} - -Status CacheValidationPass::VisitAfter(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::VisitAfter(): visiting " << node->Name() << "."; - if (!is_cached_) { - // If we are not in a cache path, then we must validate the file-based sharding config. - // If we are in a cache path, there is no file-based sharding so the check is not required. - if (!node->shard_equal_rows_ && node->dataset_files_.size() < static_cast(node->num_shards_)) { - RETURN_STATUS_UNEXPECTED( - "Invalid file, numbers of tfrecord file should not less than num_shards when shard_equal_rows is false, " - "but got numbers of tfrecord file: " + - std::to_string(node->dataset_files_.size()) + ", num_shards: " + std::to_string(node->num_shards_)); - } - } - // Reset the flag when this node is cached and is already visited - if (node->IsCached()) { - is_cached_ = false; - } - return Status::OK(); -} - -Status CacheValidationPass::VisitAfter(std::shared_ptr node, bool *const modified) { - MS_LOG(DEBUG) << "CacheValidationPass::VisitAfter(): visiting " << node->Name() << "."; - // Reset the flag when all descendants are visited - if (node->IsCached()) { - is_cached_ = false; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h deleted file mode 100644 index 7964c7fa1..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_CACHE_VALIDATION_PASS_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_CACHE_VALIDATION_PASS_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -/// \class CacheValidationPass cache_validation_pass.h -/// \brief This is a NodePass who's job is to catch invalid tree configurations related to cache and generate failures. -class CacheValidationPass : public IRNodePass { - public: - /// \brief Constructor - CacheValidationPass(); - - /// \brief Destructor - ~CacheValidationPass() = default; - - /// \brief Returns an error if BatchNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if ConcatNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if FilterNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if SkipNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if TakeNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if ZipNode exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if MapNode with non-deterministic tensor operations exists under a cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Returns an error if there is a cache over another cache - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Identifies and block repeat under cache scenarios - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Identifies the subtree above this node as not being cached - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Identifies the subtree above this node as not being cached - /// \param[in] node The node being visited - /// \param[in,out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - private: - bool is_cached_; - bool is_mappable_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_CACHE_VALIDATION_PASS_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.cc deleted file mode 100644 index c25852739..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.cc +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h" - -#include -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -namespace mindspore { -namespace dataset { -constexpr uint32_t kSeedValue = 1; -bool DebugModePass::DebugPass::RemoveCache(std::shared_ptr node) const { - // remove DatasetNode cache - bool ret = false; - if (node->IsCached()) { - MS_LOG(WARNING) << node->Name() << " with cache found in the debug mode. Dropping the cache." - << " If performance is a concern, then disable debug mode."; - (void)node->SetDatasetCache(nullptr); - ret = true; - } - if (node->IsDescendantOfCache()) { - node->setDescendantOfCache(false); - ret = true; - } - return ret; -} - -Status DebugModePass::DebugPass::Visit(std::shared_ptr node, bool *const modified) { - *modified = RemoveCache(node); - if (node->GetOffload() == ManualOffloadMode::kEnabled) { - MS_LOG(WARNING) << "Map operation with offload found in the debug mode. Ignoring offload." - " If performance is a concern, then disable debug mode."; - node->SetOffload(ManualOffloadMode::kDisabled); - *modified = true; - } - - return Status::OK(); -} - -Status DebugModePass::DebugPass::Visit(std::shared_ptr node, bool *const modified) { - // Debug mode requires deterministic result. Replace shuffle_seed in Shuffle node with a fixed internal seed if users - // didn't set a global config seed. (Global seed has not been configured by this time. So GlobalContext still returns - // the user configured value.) - uint32_t seed = GlobalContext::config_manager()->seed(); - if (seed == std::mt19937::default_seed) { - MS_LOG(INFO) << "Replace shuffle_seed of Shuffle node with internal seed: " << std::to_string(kSeedValue) << "."; - (void)node->SetShuffleSeed(kSeedValue); - *modified = true; - } - return Status::OK(); -} - -Status DebugModePass::DebugPass::Visit(std::shared_ptr node, bool *const modified) { - *modified = RemoveCache(node); - return Status::OK(); -} - -Status DebugModePass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - RETURN_UNEXPECTED_IF_NULL(modified); - - // The debug_pass can make updates to the DebugModePass object. - DebugPass debug_pass = DebugPass(); - RETURN_IF_NOT_OK(debug_pass.Run(root_ir, modified)); - - // Debug mode requires the deterministic result. Set seed if users have not done so. - uint32_t seed = GlobalContext::config_manager()->seed(); - if (seed == std::mt19937::default_seed) { - MS_LOG(WARNING) << "Debug mode is enabled. Set seed to ensure deterministic results. Seed value: " - << std::to_string(kSeedValue) << "."; - GlobalContext::config_manager()->set_seed(kSeedValue); - } - if (GlobalContext::config_manager()->get_auto_offload()) { - MS_LOG(WARNING) << "Both debug mode and auto offload are enabled. Ignoring auto offload." - " If performance is a concern, then disable debug mode."; - } - if ((GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kReplace) || - (GlobalContext::config_manager()->error_samples_mode() == ErrorSamplesMode::kSkip)) { - MS_LOG(WARNING) << "Both debug mode and error samples mode are enabled. Ignoring error samples mode setting."; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h deleted file mode 100644 index 67f4e063e..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_OPT_PRE_DEBUG_MODE_PASS_H_ -#define DATASET_ENGINE_OPT_PRE_DEBUG_MODE_PASS_H_ - -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { -/// \class DebugModePass -/// \brief This is a pre parse pass that disable some nodes and prepares for the debug mode. -class DebugModePass : public IRTreePass { - public: - /// \brief Constructor - DebugModePass() {} - - /// \brief Destructor - ~DebugModePass() = default; - - /// \brief Runs an debug pass to drop some nodes and config settings at the pre pass stage for the debug mode. - /// \param[in, out] tree The tree to operate on. - /// \param[in, out] Indicate of the tree was modified. - /// \return Status The status code returned - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; - - class DebugPass : public IRNodePass { - public: - /// \brief Constructor - DebugPass() {} - - /// \brief Destructor - ~DebugPass() = default; - - /// \brief Runs a pass on MapNode - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates if the node was changed at all - /// \return Status code - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Runs a pass on ShuffleNode - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates if the node was changed at all - /// \return Status code - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Runs a pass on DatasetNode - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates if the node was changed at all - /// \return Status code - Status Visit(std::shared_ptr node, bool *const modified) override; - - protected: - /// \brief Check and remove cache if node has any - /// \param[in] node The node being visited - /// \return true if the node was changed; otherwise, false - bool RemoveCache(std::shared_ptr node) const; - }; -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_OPT_PRE_DEBUG_MODE_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.cc deleted file mode 100644 index 9fc4da9f5..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.cc +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h" - -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -namespace mindspore { -namespace dataset { - -DeepCopyPass::DeepCopyPass() { - root_ = std::make_shared(); - parent_ = root_.get(); -} - -Status DeepCopyPass::Visit(std::shared_ptr node, bool *const modified) { - *modified = true; - // Do a nested-loop walk to check whether a node has the same child more than once. - // This is an artificial restriction. We can support it since we will do a clone of the input tree in this pass. - // Example: ds2 = ds1 + ds1; - auto children = node->Children(); - if (children.size() > 0) { - for (auto it1 = children.begin(); it1 != children.end() - 1; ++it1) { - for (auto it2 = it1 + 1; it2 != children.end(); ++it2) { - if (*it1 == *it2) { - std::string err_msg = "The same node " + (*it1)->Name() + " is a child of its parent more than once."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - } - } - - // Clone a new copy of this node - std::shared_ptr new_node = node->Copy(); - // Temporary fix to set the num_workers and connector_queue_size to each cloned node. - // This can be improved by adding a new method in the base class DatasetNode to transfer the properties to - // the cloned node. Each derived class's Copy() will need to include this method. - (void)new_node->SetNumWorkers(node->NumWorkers()); - (void)new_node->SetConnectorQueueSize(node->ConnectorQueueSize()); - // This method below assumes a DFS walk and from the first child to the last child. - // Future: A more robust implementation that does not depend on the above assumption. - RETURN_IF_NOT_OK(parent_->AppendChild(new_node)); - - // Then set this node to be a new parent to accept a copy of its next child - parent_ = new_node.get(); - return Status::OK(); -} - -Status DeepCopyPass::VisitAfter(std::shared_ptr node, bool *const modified) { - *modified = true; - // After visit the node, move up to its parent - parent_ = parent_->parent_; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h deleted file mode 100644 index 1a027fa62..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_OPT_PRE_DEEP_COPY_PASS_H_ -#define DATASET_ENGINE_OPT_PRE_DEEP_COPY_PASS_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -/// \class DeepCopyPass -/// \brief This pass clones a new copy of IR tree. A new copy is used in the compilation to avoid any modification to -/// the IR tree associated with the user code. -class DeepCopyPass : public IRNodePass { - public: - /// \brief Constructor - DeepCopyPass(); - - /// \brief Destructor - ~DeepCopyPass() = default; - - /// \brief Clone a new copy of the node - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates whether the node has been visited - /// \return Status code - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Reset parent after walking its sub tree. - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates whether the node has been visited - /// \return Status code - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Getter method to retrieve the root node - /// \return the root node of the new cloned tree - std::shared_ptr Root() { return root_; } - - private: - std::shared_ptr root_; - DatasetNode *parent_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_OPT_PRE_DEEP_COPY_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.cc deleted file mode 100644 index 099310349..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.cc +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h" -#include -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" - -namespace mindspore { -namespace dataset { - -// constructor -EpochCtrlPass::InjectionFinder::InjectionFinder(std::shared_ptr node) - : injection_point_(nullptr), num_epochs_(-1) {} - -// Performs finder work for BuildVocabOp that has special rules about epoch control injection -Status EpochCtrlPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - CHECK_FAIL_RETURN_UNEXPECTED(node->Children().size() > 0, - "Invalid data, the node of child should be greater than zero."); - // The injection is at the child of the root node - injection_point_ = node->Children()[0]; - num_epochs_ = node->NumEpochs(); - return Status::OK(); -} - -// Performs finder work for BuildVocabOp that has special rules about epoch control injection -Status EpochCtrlPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - injection_point_ = nullptr; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// Performs finder work for BuildSentencePieceVocabNode that has special rules about epoch control injection -Status EpochCtrlPass::InjectionFinder::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - injection_point_ = nullptr; - return Status::OK(); -} -#endif - -Status EpochCtrlPass::InjectionFinder::VisitAfter(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - CHECK_FAIL_RETURN_UNEXPECTED(node->Children().size() > 0, - "Invalid data, the node of child should be greater than zero."); - // Assumption: There is only one DataQueueNode in a pipeline. This assumption is not validated here. - // Move the injection point to the child of this node. - injection_point_ = node->Children()[0]; - return Status::OK(); -} - -// constructor -EpochCtrlPass::EpochCtrlPass() {} - -// Runs an injection pass to inject in operators needed at the pre pass stage -Status EpochCtrlPass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - RETURN_UNEXPECTED_IF_NULL(modified); - MS_LOG(INFO) << "Pre pass: Injection pass started."; - - // First, run the finder to perform any injection info before we can go ahead to drive the op injection work. - // The finder can make updates to the EpochInjectionPass object. - EpochCtrlPass::InjectionFinder finder(root_ir); - RETURN_IF_NOT_OK(finder.Run(root_ir, modified)); - - // The first injection logic is to check if we should inject the epoch control op as the root node. - // Do not inject the op if the number of epochs is 1. - std::shared_ptr node = finder.injection_point(); - int32_t num_epochs = finder.num_epochs(); - if (num_epochs != 1 && node != nullptr) { - auto epoch_ctrl_node = std::make_shared(num_epochs); - RETURN_IF_NOT_OK(node->InsertAbove(epoch_ctrl_node)); - } - MS_LOG(INFO) << "Pre pass: Injection pass complete."; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h deleted file mode 100644 index 0f857143a..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_OPT_PASS_PRE_EPOCH_INJECTION_PASS_H_ -#define DATASET_ENGINE_OPT_PASS_PRE_EPOCH_INJECTION_PASS_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -class DatasetOp; - -/// \class EpochInjectionPass epoch_ctrl_pass.h -/// \brief This is a pre pass that drives the injection of any nodes that could not be directly injected from the api -/// parsing. -class EpochCtrlPass : public IRTreePass { - /// \class InjectionFinder - /// \brief This is a nested node pass class who's job is to parse the tree and perform any identification logic for - /// operators that need to be injected. It is run first by the main injection pass to find out what operators - /// it may need to inject. - class InjectionFinder : public IRNodePass { - public: - /// \brief Constructor - explicit InjectionFinder(std::shared_ptr node); - - /// \brief Destructor - ~InjectionFinder() = default; - - /// \brief Performs finder work for BuildVocabNode that has special rules about epoch control injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Performs finder work for BuildVocabNode that has special rules about epoch control injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - -#ifndef ENABLE_ANDROID - /// \brief Performs finder work for BuildSentenceVocabNode that has special rules about epoch control injection. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; -#endif - - /// \brief Register the DataQueueNode for further action. - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Getter - std::shared_ptr injection_point() { return injection_point_; } - - /// \brief Getter - int32_t num_epochs() const { return num_epochs_; } - - private: - std::shared_ptr injection_point_; - int32_t num_epochs_; - }; - - public: - /// \brief Constructor - EpochCtrlPass(); - - /// \brief Destructor - ~EpochCtrlPass() = default; - - /// \brief Runs an injection pass to inject in operators needed at the pre pass stage - /// \param[in, out] tree The tree to operate on. - /// \param[in, out] Indicate of the tree was modified. - /// \return Status The status code returned - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_OPT_PASS_PRE_EPOCH_INJECTION_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.cc deleted file mode 100644 index 8602fcc9a..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.cc +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -namespace mindspore { -namespace dataset { - -Status GetterPass::Visit(std::shared_ptr node, bool *const modified) { - node->ClearCallbacks(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h deleted file mode 100644 index b8f90ccca..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_PRE_GETTER_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_PRE_GETTER_PASS_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -class DatasetOp; - -/// \class GetterPass -/// \brief This is a tree pass that will for now only clear the callback in MapOp to prevent hang -class GetterPass : public IRNodePass { - public: - /// \brief Default Constructor - GetterPass() = default; - - /// \brief Default Destructor - ~GetterPass() = default; - - Status Visit(std::shared_ptr node, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PASS_PRE_GETTER_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.cc deleted file mode 100644 index 7183b1125..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.cc +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h" - -#include - -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" - -namespace mindspore { -namespace dataset { - -Status InputValidationPass::Visit(std::shared_ptr node, bool *const modified) { - *modified = false; - RETURN_IF_NOT_OK(node->ValidateParams()); - - // A data source node must be a leaf node - if ((node->IsMappableDataSource() || node->IsNonMappableDataSource()) && !node->IsLeaf()) { - std::string err_msg = node->Name() + " is a data source and must be a leaf node."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // A non-leaf node must not be a data source node - if (node->IsNotADataSource() && node->IsLeaf()) { - std::string err_msg = node->Name() + " is a dataset operator and must not be a leaf node."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h deleted file mode 100644 index 25d0febb5..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef DATASET_ENGINE_OPT_PRE_INPUT_VALIDATION_PASS_H_ -#define DATASET_ENGINE_OPT_PRE_INPUT_VALIDATION_PASS_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -/// \class InputValidationPass -/// \brief This is a parse pass that validates input parameters of the IR tree. -class InputValidationPass : public IRNodePass { - public: - /// \brief Runs a validation pass to check input parameters - /// \param[in] node The node being visited - /// \param[in, out] *modified indicates whether the node has been visited - /// \return Status code - Status Visit(std::shared_ptr node, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // DATASET_ENGINE_OPT_PRE_INPUT_VALIDATION_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.cc deleted file mode 100644 index 834f8ea2e..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.cc +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h" - -namespace mindspore::dataset { -#ifndef ENABLE_ANDROID -Status InsertMapPass::Visit(std::shared_ptr node, bool *const modified) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(modified); - -#if !defined(_WIN32) && !defined(_WIN64) - // construct schema from the inputs of TFRecordNode - auto data_schema = DataSchema(); - RETURN_IF_NOT_OK(node->CreateDataSchema(&data_schema)); - - // get the output column list - std::vector output_columns; - RETURN_IF_NOT_OK(data_schema.GetColumnName(&output_columns)); - if (output_columns.empty()) { - if (!node->ColumnsList().empty()) { - output_columns = node->ColumnsList(); - } else { - // Unable to fetch output columns, degraded to do parsing directly in TFRecordOp - MS_LOG(WARNING) - << "If both schema and column list are not set, the performance of TFRecordDataset may be degraded."; - *modified = false; - return Status::OK(); - } - } - - // not to parse the protobuf in TFRecordOp - node->SetDecode(false); - - // if the next node is batch, do parallel parsing in ParseExampleOp - bool parallel_parse = node->Parent()->Name() == kBatchNode; - const auto parse_example = - std::make_shared(data_schema, node->ColumnsList(), parallel_parse); - auto map_node = std::make_shared(std::vector>{parse_example}, - std::vector{"proto"}, output_columns); - if (parallel_parse) { - // parallel parsing use a thread pool inside ParseExampleOp, so we only need 1 worker for map - (void)map_node->SetNumWorkers(1); - } - - if (node->Parent()->Name() == kBatchNode) { - MS_LOG(INFO) << "Insert a Map node after Batch to parse protobuf in parallel."; - RETURN_IF_NOT_OK(node->Parent()->InsertAbove(map_node)); - } else { - MS_LOG(INFO) << "Insert a Map node after TFRecord to parse protobuf one by one."; - RETURN_IF_NOT_OK(node->InsertAbove(map_node)); - } - *modified = true; -#endif - return Status ::OK(); -} -#endif -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h deleted file mode 100644 index f2d895946..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_INSERT_MAP_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_INSERT_MAP_PASS_H_ - -#include - -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { -class InsertMapPass : public IRNodePass { - public: - /// \brief Constructor - InsertMapPass() = default; - - /// \brief Destructor - ~InsertMapPass() override = default; - -#ifndef ENABLE_ANDROID - /// \brief Insert map node to parse the protobuf for TFRecord. - /// \param[in] node The TFRecordNode being visited. - /// \param[in, out] modified Indicator if the node was changed at all. - /// \return The status code. - Status Visit(std::shared_ptr node, bool *const modified) override; -#endif -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_INSERT_MAP_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.cc deleted file mode 100644 index 29ae26777..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" - -namespace mindspore { -namespace dataset { - -NodeRemovalPass::RemovalNodes::RemovalNodes() {} - -// Perform RepeatNode removal check. -Status NodeRemovalPass::RemovalNodes::Visit(std::shared_ptr node, bool *const modified) { - *modified = false; - if (node->Count() == 1) { - nodes_to_remove_.push_back(std::static_pointer_cast(node)); - } - return Status::OK(); -} - -// Perform SkipNode removal check. -Status NodeRemovalPass::RemovalNodes::Visit(std::shared_ptr node, bool *const modified) { - *modified = false; - if (node->Count() == 0) { - nodes_to_remove_.push_back(std::static_pointer_cast(node)); - } - return Status::OK(); -} - -// Perform TakeNode removal check. -Status NodeRemovalPass::RemovalNodes::Visit(std::shared_ptr node, bool *const modified) { - *modified = false; - if (node->Count() == -1) { - nodes_to_remove_.push_back(std::static_pointer_cast(node)); - } - return Status::OK(); -} - -// constructor -NodeRemovalPass::NodeRemovalPass() {} - -// Walk the tree to collect the nodes to remove, then removes them. -Status NodeRemovalPass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - MS_LOG(INFO) << "Pre pass: node removal pass started."; - // Create the removal node pass which can identify which nodes need to be removed. - std::unique_ptr removal_nodes = std::make_unique(); - RETURN_IF_NOT_OK(removal_nodes->Run(root_ir, modified)); - - // Update modified flag if there were any nodes identified to be removed - if (removal_nodes->nodes_to_remove().empty() == false) { - *modified = true; - } - - // Then, execute the removal of any nodes that were set up for removal - for (auto node : removal_nodes->nodes_to_remove()) { - RETURN_IF_NOT_OK(node->Drop()); - } - MS_LOG(INFO) << "Pre pass: node removal pass complete."; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h deleted file mode 100644 index d5598aa51..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_NODE_REMOVAL_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_NODE_REMOVAL_PASS_H_ - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { - -class DatasetOp; - -/// \class RemovalPass removal_pass.h -/// \brief This is a tree pass that will remove nodes. It uses removal_nodes to first identify which -/// nodes should be removed, and then removes them. -class NodeRemovalPass : public IRTreePass { - /// \class RemovalNodes - /// \brief This is a NodePass whose job is to identify which nodes should be removed. - /// It works in conjunction with the removal_pass. - class RemovalNodes : public IRNodePass { - public: - /// \brief Constructor - RemovalNodes(); - - /// \brief Destructor - ~RemovalNodes() = default; - - /// \brief Perform RepeatNode removal check - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform SkipNode removal check - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform TakeNode removal check - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Getter - /// \return All the nodes to be removed - std::vector> nodes_to_remove() { return nodes_to_remove_; } - - private: - std::vector> nodes_to_remove_; - }; - - public: - /// \brief Constructor - NodeRemovalPass(); - - /// \brief Destructor - ~NodeRemovalPass() = default; - - /// \brief Runs a removal_nodes pass first to find out which nodes to remove, then removes them. - /// \param[in, out] tree The tree to operate on. - /// \param[in, out] Indicate of the tree was modified. - /// \return Status The status code returned - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_NODE_REMOVAL_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.cc b/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.cc deleted file mode 100644 index b40333ff5..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.cc +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/generator_node.h" -#endif -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/minddata_node.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h" - -namespace mindspore { -namespace dataset { -SkipPushdownPass::SkipNodes::SkipNodes() : skip_count_(0), skip_steps_(0) {} - -// activate the optimization steps, and increase skip_count_ (if not the first skip node in the pipeline) -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - if (!node->OnceOnly()) { - return Visit(std::static_pointer_cast(node), modified); - } - skip_count_ += node->Count(); - nodes_to_remove_.push_back(node); - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::VisitAfter(std::shared_ptr node, bool *const modified) { - if (!node->OnceOnly()) { - return VisitAfter(std::static_pointer_cast(node), modified); - } - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ == 0, "The skip_count_ cannot be non-zero here."); - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { -#ifdef ENABLE_PYTHON - if (node->BatchSizeFunc()) { - return Visit(std::static_pointer_cast(node), modified); - } -#endif - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - if (skip_count_ == 0) { - return Status::OK(); - } // no active skip node above. normal flow - - // we have an active skip node above. - skip_count_ *= node->BatchSize(); - - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - if (skip_count_ == 0) { - return Status::OK(); - } // no active skip node above. normal flow - - // we have an active skip node above. - auto new_sampler = std::make_shared(skip_count_); - MS_LOG(INFO) << "Adding SkipFirstEpochSampler(" << skip_count_ << ")"; - auto sampler = node->Sampler(); - if (sampler != nullptr) { - RETURN_IF_NOT_OK(new_sampler->AddChildSampler(sampler)); - } - node->SetSampler(new_sampler); - skip_count_ = 0; - - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - if (skip_count_ == 0) { - return Status::OK(); - } // no active skip node above. normal flow - - // we have an active skip node above. - MS_LOG(WARNING) - << "Pushing down skip node below a map node will result in slightly different outputs for random transformations."; - return Status::OK(); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - node->SetSkipSteps(skip_steps_); - return InsertSkipNode(node); -} - -#ifdef ENABLE_PYTHON -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - if (!node->IsMappableSource()) { - RETURN_IF_NOT_OK(InsertSkipNode(node)); - } else { - RETURN_IF_NOT_OK(Visit(std::static_pointer_cast(node), modified)); - } - return Status::OK(); -} -#endif - -// This functions is used for Ops that are random, and the ones in which Visit is Not Implemented yet; -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - return InsertSkipNode(node); -} - -Status SkipPushdownPass::SkipNodes::Visit(std::shared_ptr node, bool *const modified) { - int64_t dataset_size = node->DatasetSize(); - int64_t step = node->Step(); - // in fast recover mode, we need to know how many steps are actually skipped - // when we skip `step / dataset_size` epochs - skip_steps_ = step / dataset_size * dataset_size; - return Status::OK(); -} - -// constructor -SkipPushdownPass::SkipPushdownPass() = default; - -// Walk the tree to push down the skip node inserted when Reset is called. -Status SkipPushdownPass::RunOnTree(std::shared_ptr root_ir, bool *const modified) { - // Since previous pass may have disabled fast_recovery (due to shuffle node in pipeline), - // we need to double check here if it is enabled. - if (!GlobalContext::config_manager()->fast_recovery()) { - MS_LOG(INFO) << "Pre pass: ignoring skip node pushdown pass (shuffle node was detected)."; - return Status::OK(); - } - MS_LOG(INFO) << "Pre pass: skip node pushdown pass started."; - // Assumption: The total skip counts in the first_epoch_only skip node is less than the size of the dataset. This - // assumption is not validated here. - // Create the skip node pass which can identify which nodes need to be removed and which ones added. - std::unique_ptr skip_nodes = std::make_unique(); - if (root_ir->IsSizeDefined()) { - RETURN_IF_NOT_OK(skip_nodes->Run(root_ir, modified)); - } - - // Update modified flag if there were any nodes identified to be removed - if (!skip_nodes->nodes_to_remove().empty() || !skip_nodes->insert_skip_above().empty()) { - *modified = true; - } - - // Add skip node(s) to the tree (if any) - for (const auto &iter : skip_nodes->insert_skip_above()) { - MS_LOG(INFO) << "Inserting a Skip(" << iter.second << ") node above this node: " << iter.first->Name(); - auto new_skip_node = std::make_shared(iter.second); - new_skip_node->SetOnceOnly(true); - RETURN_IF_NOT_OK(iter.first->InsertAbove(new_skip_node)); - } - - // Then, execute the removal of any nodes that were set up for removal - for (const auto &node : skip_nodes->nodes_to_remove()) { - RETURN_IF_NOT_OK(node->Drop()); - } - - MS_LOG(INFO) << "Pre pass: skip node pushdown pass is complete."; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h b/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h deleted file mode 100644 index feca21d27..000000000 --- a/mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_SKIP_PUSHDOWN_PASS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_SKIP_PUSHDOWN_PASS_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" - -namespace mindspore { -namespace dataset { -class BatchNode; -class DatasetNode; -class DatasetOp; -class MappableSourceNode; -class MapNode; -#ifndef ENABLE_ANDROID -class MindDataNode; -#endif -class NonMappableSourceNode; -class ProjectNode; -class RenameNode; -class RootNode; -class SkipNode; - -/// \class SkipPushdownPass skip_pushdown_pass.h -/// \brief This is a tree pass that will push down a skip node. It uses SkipNodes to first identify if we have a skip -/// node, and then based on the node types we observe in the tree, decide where to place the skip node (or use a -/// SequentialSampler for MappableSource nodes). -class SkipPushdownPass : public IRTreePass { - /// \class SkipNodes - /// \brief This is a NodePass whose job is to handle different nodes accordingly. - /// It works in conjunction with the SkipPushdownPass. - class SkipNodes : public IRNodePass { - public: - /// \brief Constructor - SkipNodes(); - - /// \brief Destructor - ~SkipNodes() override = default; - - /// \brief Perform skip node pushdown initiation check on a SkipNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown completion check on a SkipNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a BatchNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a ProjectNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a RenameNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a MappableSourceNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a MapNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a NonMappableSourceNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - -#ifdef ENABLE_PYTHON - /// \brief Perform skip node pushdown check on a GeneratorNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; -#endif - - /// \brief Perform skip node pushdown check on a DatasetNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown check on a RootNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status Visit(std::shared_ptr node, bool *const modified) override; - - /// \brief Perform skip node pushdown completion check on a DatasetNode - /// \param[in] node The node being visited - /// \param[in, out] modified Indicator if the node was changed at all - /// \return Status The status code returned - Status VisitAfter(std::shared_ptr node, bool *const modified) override { return Status::OK(); }; - - /// \brief Getter - /// \return All the nodes where a skip node needs to be inserted above (and the skip count). - const std::vector, int64_t>> &insert_skip_above() const { - return insert_skip_above_; - } - - /// \brief Getter - /// \return All the nodes to be removed - const std::vector> &nodes_to_remove() const { return nodes_to_remove_; } - - private: - template - Status InsertSkipNode(std::shared_ptr node) { - CHECK_FAIL_RETURN_UNEXPECTED(skip_count_ >= 0, "The skip size cannot be negative."); - if (skip_count_ == 0) { - return Status::OK(); - } // no active skip node above. normal flow - - // insert a skip node above - (void)insert_skip_above_.emplace_back(node, skip_count_); - skip_count_ = 0; - return Status::OK(); - } - - std::vector, int64_t>> insert_skip_above_; - std::vector> nodes_to_remove_; - int64_t skip_count_; - int64_t skip_steps_; - }; - - public: - /// \brief Constructor - SkipPushdownPass(); - - /// \brief Destructor - ~SkipPushdownPass() override = default; - - /// \brief Runs a skip_pushdown pass to push down the skip node found in the tree (for Reset scenario). - /// \param[in, out] tree The tree to operate on. - /// \param[in, out] Indicate of the tree was modified. - /// \return Status The status code returned - Status RunOnTree(std::shared_ptr root_ir, bool *const modified) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_OPT_PRE_SKIP_PUSHDOWN_PASS_H_ diff --git a/mindspore-lite/minddata/dataset/engine/perf/auto_tune.cc b/mindspore-lite/minddata/dataset/engine/perf/auto_tune.cc deleted file mode 100644 index ef9a7ccb4..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/auto_tune.cc +++ /dev/null @@ -1,757 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/perf/auto_tune.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.h" -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -AutoTune::AutoTune(TreeAdapter *tree_adap, ProfilingManager *profiling_mgr) - : tree_adapter_(tree_adap), - profiling_manager_(profiling_mgr), - tree_modifier_(std::make_unique(tree_adapter_)), - leaf_op_id_(-1), - cur_epoch_running_(1), - last_epoch_autotuned_(0), - cur_step_running_(1), - last_step_autotuned_(0), - mode_(0), - step_gap_(GlobalContext::config_manager()->autotune_interval()), - skip_flag_(true), - AT_phase_(AutoTunePhase::kAutoTunePhaseTime), - AT_change_(false), - phase_1_best_time_(-1), - phase_1_no_improve_count_(0), - count_down_(0), - phase_3_state_(AutoTuneMemPhase::kAutoTuneMemInit), - phase_3_ID_(0), - avg_batch_time(0.0), - phase_3_prev_avg_(0.0), - save_autoconfig_(GlobalContext::config_manager()->save_autoconfig()) { - max_workers_ = GlobalContext::config_manager()->num_cpu_threads(); - autotune_json_filepath_ = GlobalContext::config_manager()->get_autotune_json_filepath(); -} - -Status AutoTune::Main() { - TaskManager::FindMe()->Post(); - MS_LOG(INFO) << "Dataset AutoTune thread has started."; - if (step_gap_ != 0) { - mode_ = AutoTuneMode::kAutoTuneModeStep; - } else { - mode_ = AutoTuneMode::kAutoTuneModeEpoch; - } - const bool nodes_offloaded = !tree_adapter_->GetOffloadJson().empty(); - if (nodes_offloaded) { - // When nodes are offloaded they are removed from the optimized IR tree. - // Serializing the optimized IR Tree and then deserializing will not work. - MS_LOG(WARNING) << "Some nodes have been offloaded. AutoTune is unable to write the autotune configuration to " - "disk. Disable offload to prevent this from happening."; - } - bool output_final_config = save_autoconfig_ && !nodes_offloaded; - bool output_intermediate_config = save_intermediate_autoconfig_ && output_final_config; - RETURN_IF_NOT_OK(ATMainLoop(output_intermediate_config)); - RETURN_IF_NOT_OK(profiling_manager_->Stop()); - PostMainLogging(); -#ifndef ENABLE_ANDROID - if (output_final_config && - (SaveAutotuneConfig(autotune_json_filepath_ + "_" + profiling_manager_->GetRankID() + ".json").IsError())) { - MS_LOG(WARNING) << "Failed to write the final autotune configuration to disk"; - } -#endif - return Status::OK(); -} - -Status AutoTune::ATMainLoop(bool output_intermediate_config) { - std::unique_lock _lock(mux_); - int loop_cnt = 0; - Status rc; - while (!this_thread::is_interrupted() && !(tree_adapter_->tree_->isFinished())) { -#ifndef ENABLE_ANDROID - auto last_epoch = cur_epoch_running_; - auto last_step = cur_step_running_; -#endif - RETURN_IF_NOT_OK(UpdateCurrentRunInfo()); - if (!WarmupSkipCheck()) { - // Warm up complete - AT normally - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - rc = RunIterationEpoch(); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - rc = RunIterationStep(); - } - if (rc.IsError()) { - if (rc.StatusCode() != StatusCode::kMDInterrupted) { - MS_LOG(ERROR) << "Dataset AutoTune failed and will exit with the following error: " << rc; - } - RETURN_IF_NOT_OK(profiling_manager_->Stop()); - break; - } -#ifndef ENABLE_ANDROID - if (last_epoch != cur_epoch_running_ || last_step != cur_step_running_) { - if (output_intermediate_config && - (SaveAutotuneConfig(autotune_json_filepath_ + "_" + profiling_manager_->GetRankID() + "_" + - std::to_string(loop_cnt) + ".json") - .IsError())) { - MS_LOG(WARNING) << "Failed to write the current iteration autotune configuration to disk"; - } - ++loop_cnt; - } -#endif - if (AT_phase_ == AutoTunePhase::kAutoTuneEnd) { - MS_LOG(INFO) << "Dataset AutoTune stop, optimization complete."; - break; - } - } - rc = cv_.WaitFor(&_lock, GlobalContext::config_manager()->monitor_sampling_interval()); - // The thread may be interrupted for tree termination when waiting (we should not report error in this case) - if (rc.IsError() && rc != StatusCode::kMDInterrupted) { - return rc; - } - } - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status AutoTune::SaveAutotuneConfig(const std::string &file_name) { - Path jsonpath(file_name); - - std::string parent_dir = jsonpath.ParentPath(); - if (access(parent_dir.c_str(), R_OK) == -1) { - std::string err_msg = "AutoTune has no access to specified path: " + parent_dir + ", check permission."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (jsonpath.Exists()) { - std::string err_msg = "File: <" + file_name + - "> already exists. File will be overwritten with the AutoTuned data pipeline configuration."; - MS_LOG(WARNING) << err_msg; - } - - RETURN_IF_NOT_OK(SetAutotuneConfigJson()); - // The Execution Tree is built by visiting the optimized IR Tree in DFS order. - // So we visit the optimized IR tree in DFS order and try to match each IR node with its corresponding dataset op. - RETURN_IF_NOT_OK(Serdes::UpdateOptimizedIRTreeJSON(&autotune_config_json_, ops_)); - std::vector summary; - RETURN_IF_NOT_OK(SummarizeTreeConfiguration(&summary)); - nlohmann::json out_json; - out_json["summary"] = summary; - out_json["tree"] = autotune_config_json_; - std::string remark_value = "The following file has been auto-generated by the Dataset AutoTune."; - if (tree_modifier_->GetRequestsCount() == 0) { - remark_value += " Dataset Pipeline is not the bottleneck. No configuration changes were made by Dataset AutoTune."; - } - out_json["remark"] = remark_value; - RETURN_IF_NOT_OK(Serdes::SaveJSONToFile(out_json, file_name, true)); - return Status::OK(); -} - -Status AutoTune::SetAutotuneConfigJson() { - if (autotune_config_json_.empty()) { - nlohmann::json out_json; - RETURN_IF_NOT_OK(Serdes::SaveToJSON(tree_adapter_->RootIRNode(), "", &out_json)); - // We do not want to serialize DataQueueNode/DataQueueOp - if (out_json["op_type"] == kTransferNode) { - CHECK_FAIL_RETURN_UNEXPECTED( - out_json["children"].size() == 1, - "Expected Transfer node to have exactly 1 child but it has " + std::to_string(out_json["children"].size())); - out_json = out_json["children"][0]; - } - autotune_config_json_ = std::move(out_json); - } - return Status::OK(); -} -#endif - -Status AutoTune::SummarizeTreeConfiguration(std::vector *out) { - constexpr int op_name_width = 20; - constexpr int val_width = 2; - for (int i = static_cast(ops_.size()) - 1; i >= 0; --i) { - const auto op = ops_[i]; - if (!op->inlined() && op->Name() != "DataQueueOp") { - std::stringstream s; - s << std::left << std::setw(op_name_width) << op->NameWithID() << "(num_parallel_workers:" << std::right - << std::setw(val_width) << (op->NumWorkers() == 0 ? "NA" : std::to_string(op->NumWorkers())) - << ", prefetch_size:" << std::setw(val_width) << op->ConnectorCapacity() << ")"; - (void)out->emplace_back(s.str()); - } - } - return Status::OK(); -} - -void AutoTune::PostMainLogging() const { - MS_LOG(INFO) << "Dataset AutoTune thread is finished."; - MS_LOG(INFO) << "Printing the final tree configuration"; - PrintTreeConfiguration(); - // Print the suggestion in logs only if autotune requested some changes - if (tree_modifier_->GetRequestsCount() > 0) { - MS_LOG(INFO) << "Suggest to set proper num_parallel_workers for each Operation or use global setting API: " - << "mindspore.dataset.config.set_num_parallel_workers"; - MS_LOG(INFO) << "Suggest to choose maximum prefetch_size from tuned result and set by global setting API: " - << "mindspore.dataset.config.set_prefetch_size"; - } -} - -void AutoTune::PrintTreeConfiguration() const { - ExecutionTree const *tree = tree_adapter_->tree_.get(); - for (auto itr = tree->begin(); itr != tree->end(); (void)itr++) { - if (!itr->inlined() && itr->Name() != "DataQueueOp") { - MS_LOG(INFO) << itr->NameWithID() << " num_parallel_workers: " << itr->NumWorkers() - << " prefetch_size: " << itr->ConnectorCapacity(); - } - } -} - -Status AutoTune::LaunchThread() { - MS_LOG(INFO) << "Launching Dataset AutoTune thread"; - Status rc = CollectOpsInfo(); - if (rc.IsError()) { - if (rc.StatusCode() != StatusCode::kMDInterrupted) { - MS_LOG(ERROR) << "Dataset AutoTune failed and will exit with the following error: " << rc; - } - RETURN_IF_NOT_OK(profiling_manager_->Stop()); - return Status::OK(); - } - RETURN_IF_NOT_OK(cv_.Register(tree_adapter_->AllTasks()->GetIntrpService())); - RETURN_IF_NOT_OK(tree_adapter_->AllTasks()->CreateAsyncTask("AutoTune Thread", std::bind(&AutoTune::Main, this))); - return Status::OK(); -} - -Status AutoTune::CollectOpsInfo() { - ExecutionTree const *tree = tree_adapter_->tree_.get(); - RETURN_UNEXPECTED_IF_NULL(tree); - for (auto itr = tree->begin(); itr != tree->end(); ++itr) { - ops_[itr->id()] = itr.get(); - // Get all parallel ops (num_workers>0) except leaf nodes (no children) - if (itr->NumWorkers() > 0) { - parallel_ops_ids_.push_back(itr->id()); - } - } - // Sort parallel ops in reverse order of IDs (i.e., bottommost op is first) - std::sort(parallel_ops_ids_.begin(), parallel_ops_ids_.end(), std::greater<>()); - leaf_op_id_ = static_cast(ops_.size()) - 1; - return Status::OK(); -} - -Status AutoTune::GetOpConnectorCapacity(int32_t op_id, int64_t *capacity) { - auto item = ops_.find(op_id); - CHECK_FAIL_RETURN_UNEXPECTED(item != ops_.end(), "Invalid Operator ID."); - *capacity = item->second->ConnectorCapacity(); - return Status::OK(); -} - -Status AutoTune::GetOpsCpuUtil(std::map *ops_cpu_util) { - // Loop over all itr keys and get avg cpu usage - for (auto itr = ops_.begin(); itr != ops_.end(); ++itr) { - std::vector sys_util; - std::vector user_util; -#ifndef ENABLE_ANDROID - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetSysCpuUtilByEpoch(itr->first, cur_epoch_running_, &sys_util)); - RETURN_IF_NOT_OK(profiling_manager_->GetUserCpuUtilByEpoch(itr->first, cur_epoch_running_, &user_util)); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK( - profiling_manager_->GetSysCpuUtilByStep(itr->first, last_step_autotuned_, cur_step_running_ - 1, &sys_util)); - RETURN_IF_NOT_OK( - profiling_manager_->GetUserCpuUtilByStep(itr->first, last_step_autotuned_, cur_step_running_ - 1, &user_util)); - } -#endif - double sys_cpu_util = Mean(sys_util); - double user_cpu_util = Mean(user_util); - (*ops_cpu_util)[itr->first] = sys_cpu_util + user_cpu_util; - } - return Status::OK(); -} - -Status AutoTune::GetOpsQueueUtil(std::map *out_ops_queue_util, - std::map *in_ops_queue_util) { - // Loop over all itr keys in the ops_ and get output_queue usage - for (auto itr = ops_.begin(); itr != ops_.end(); ++itr) { - if (itr->second->inlined()) { - (*out_ops_queue_util)[itr->first] = -1; - continue; - } - std::vector sizes; - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetConnectorSizeByEpoch(itr->first, cur_epoch_running_, &sizes)); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK( - profiling_manager_->GetConnectorSizeByStep(itr->first, last_step_autotuned_, cur_step_running_ - 1, &sizes)); - } - double avg_size = Mean(sizes); - int64_t capacity = itr->second->ConnectorCapacity(); - CHECK_FAIL_RETURN_UNEXPECTED(capacity != 0, "Capacity of connector should not be 0"); - (*out_ops_queue_util)[itr->first] = avg_size / capacity; - } - for (auto itr = ops_.rbegin(); itr != ops_.rend(); ++itr) { - // Assume that leaf op has 100% input queue util - if (itr->first + 1 == ops_.size()) { - (*in_ops_queue_util)[itr->first] = 1; - continue; - } - // Input queue is the output queue of the child - (*in_ops_queue_util)[itr->first] = (*out_ops_queue_util)[itr->first + 1]; - // If the child is an inlined op, use the prev known utilization - if ((*in_ops_queue_util)[itr->first] == -1.0) { - (*in_ops_queue_util)[itr->first] = (*in_ops_queue_util)[itr->first + 1]; - } - } - for (const auto &op : ops_) { - if (op.second->inlined()) { - (*in_ops_queue_util)[op.first] = -1; - } - } - return Status::OK(); -} - -Status AutoTune::GetOpsNumWorker(std::map *ops_num_workers) { - for (const auto &op : ops_) { - (*ops_num_workers)[op.first] = op.second->NumWorkers(); - } - return Status::OK(); -} - -bool AutoTune::IsSink() const { - std::shared_ptr node; - return profiling_manager_->GetTracingNode(kDeviceQueueTracingName, &node).IsOk(); -} - -template -double AutoTune::Mean(const std::vector &items) const { - if (items.size() == 0) { - return 0; - } - return std::accumulate(items.begin(), items.end(), 0.0) / static_cast(items.size()); -} - -Status AutoTune::UpdateCurrentRunInfo() { - // Get current running epoch - cur_epoch_running_ = profiling_manager_->GetNumOfProfiledEpochs(); - // Get current running step - int32_t step_temp = 0; - RETURN_IF_NOT_OK(profiling_manager_->GetNumberOfProfiledSteps(&step_temp)); - cur_step_running_ = step_temp; - return Status::OK(); -} - -bool AutoTune::WarmupSkipCheck() { - if (skip_flag_ == false) { - return false; - } - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - if (cur_epoch_running_ > EPOCH_WARMUP) { - skip_flag_ = false; - return false; - } - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - int64_t skip_value = std::max(STEP_WARMUP, step_gap_); - if (cur_step_running_ > skip_value) { - last_step_autotuned_ = std::min(STEP_WARMUP, step_gap_); - skip_flag_ = false; - return false; - } - } - return true; -} - -Status AutoTune::RunIterationEpoch() { - // Run every epoch - if (last_epoch_autotuned_ < cur_epoch_running_ - 1) { - MS_LOG(INFO) << "Run Dataset AutoTune at epoch # " << cur_epoch_running_; - RETURN_IF_NOT_OK(RunIteration()); - last_epoch_autotuned_ = cur_epoch_running_ - 1; - } - return Status::OK(); -} - -Status AutoTune::RunIterationStep() { - // Run at autotune step interval - if (cur_step_running_ - last_step_autotuned_ >= step_gap_) { - MS_LOG(INFO) << "Run AutoTune at step # " << cur_step_running_; - RETURN_IF_NOT_OK(RunIteration()); - last_step_autotuned_ = cur_step_running_; - } - return Status::OK(); -} - -Status AutoTune::RegisterWorkersQueue() { - ExecutionTree *tree = tree_adapter_->tree_.get(); - for (auto itr = tree->begin(); itr != tree->end(); (void)itr++) { - if (!itr->inlined() && itr->Name() != "DataQueueOp") { - (void)phase_1_best_workers.push_back(itr->NumWorkers()); - (void)phase_1_best_queue.push_back(itr->ConnectorCapacity()); - } - } - return Status::OK(); -} - -Status AutoTune::ResetWorkersQueue() { - if (phase_1_best_workers.size() == 0 || phase_1_best_queue.size() == 0) { - return Status::OK(); - } - ExecutionTree *tree = tree_adapter_->tree_.get(); - int counter = 0; - for (auto itr = tree->begin(); itr != tree->end(); (void)itr++) { - if (!itr->inlined() && itr->Name() != "DataQueueOp") { - int32_t target_workers = phase_1_best_workers[counter]; - int32_t target_queue = phase_1_best_queue[counter]; - RETURN_IF_NOT_OK(RequestNumWorkerChange(itr->id(), -1, &target_workers)); - RETURN_IF_NOT_OK(RequestConnectorCapacityChange(itr->id(), -1, target_queue)); - counter++; - } - } - return Status::OK(); -} - -Status AutoTune::TrackPipelineTime() { - std::vector pipeline_times; - std::vector batch_times; - // Select early stop threshold based on running mode - int early_stop_threshold_mode; - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetPipelineTimeByEpoch(cur_epoch_running_, &pipeline_times)); - RETURN_IF_NOT_OK(profiling_manager_->GetBatchTimeByEpoch(cur_epoch_running_ - 1, &batch_times)); - early_stop_threshold_mode = EARLY_STOP_TRIAL_THRESHOLD_EPOCH; - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK( - profiling_manager_->GetPipelineTimeByStep(last_step_autotuned_, cur_step_running_ - 1, &pipeline_times)); - RETURN_IF_NOT_OK(profiling_manager_->GetBatchTimeByStep(last_step_autotuned_, cur_step_running_ - 1, &batch_times)); - early_stop_threshold_mode = EARLY_STOP_TRIAL_THRESHOLD_STEP; - } - double avg_time_pipeline = Mean(pipeline_times); - double avg_time_batch = Mean(batch_times); - (void)avg_pipeline_times_.push_back(avg_time_pipeline); - MS_LOG(INFO) << "Average Pipeline time is " << avg_time_pipeline << " ms. The avg pipeline time for all epochs is " - << Mean(avg_pipeline_times_) << "ms"; - // Time phase (phase 1) improvement tracking - if (AT_phase_ == AutoTunePhase::kAutoTunePhaseTime) { - if (phase_1_best_time_ < 0) { - phase_1_best_time_ = avg_time_batch; // set first value - } else if (avg_time_batch < phase_1_best_time_) { - phase_1_no_improve_count_ = 0; - phase_1_best_time_ = avg_time_batch; - // Trigger save process - if (AT_change_) { - AT_change_ = false; // Reset for next analysis run - RETURN_IF_NOT_OK(RegisterWorkersQueue()); - } - } else { - phase_1_no_improve_count_++; - } - if (phase_1_no_improve_count_ > early_stop_threshold_mode) { - // Reset best config and exit - AT_phase_ = AutoTunePhase::kAutoTunePhaseMemory; - RETURN_IF_NOT_OK(ResetWorkersQueue()); - } - } - return Status::OK(); -} - -Status AutoTune::RunIteration() { - RETURN_IF_NOT_OK(TrackPipelineTime()); - if (AT_phase_ == AutoTunePhase::kAutoTunePhaseTime) { - RETURN_IF_NOT_OK(AnalyseTime()); - } else if (AT_phase_ == AutoTunePhase::kAutoTunePhaseMemory) { - RETURN_IF_NOT_OK(AnalyseMemory()); - } - return Status::OK(); -} - -Status AutoTune::GetConnectorSize(std::vector *sizes) const { - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetConnectorSizeByEpoch(cur_epoch_running_, sizes)); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK(profiling_manager_->GetConnectorSizeByStep(last_step_autotuned_, cur_step_running_ - 1, sizes)); - } - return Status::OK(); -} - -Status AutoTune::GetConnectorCapacity(std::vector *capacities) const { - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetConnectorCapacityByEpoch(cur_epoch_running_, capacities)); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK( - profiling_manager_->GetConnectorCapacityByStep(last_step_autotuned_, cur_step_running_ - 1, capacities)); - } - return Status::OK(); -} - -Status AutoTune::GetConnectorUtil(double *usage_avg_last, double *avg_size, double *avg_capacity) { - std::vector sizes; - RETURN_IF_NOT_OK(GetConnectorSize(&sizes)); - *avg_size = Mean(sizes); - std::vector capacities; - RETURN_IF_NOT_OK(GetConnectorCapacity(&capacities)); - *avg_capacity = Mean(capacities); - CHECK_FAIL_RETURN_UNEXPECTED(*avg_capacity != 0.0, "Capacities of connectors should not be 0.0"); - // size here means size of queue utilized - *usage_avg_last = (*avg_size / *avg_capacity); - return Status::OK(); -} - -Status AutoTune::GetEmptyQueueFrequency(float *empty_freq) const { - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - RETURN_IF_NOT_OK(profiling_manager_->GetEmptyQueueFrequencyByEpoch(cur_epoch_running_, empty_freq)); - } else if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - RETURN_IF_NOT_OK( - profiling_manager_->GetEmptyQueueFrequencyByStep(last_step_autotuned_, cur_step_running_ - 1, empty_freq)); - } - return Status::OK(); -} - -Status AutoTune::IsDSaBottleneck(bool *isBottleneck) { - double usage_avg_last, avg_size, avg_capacity; - RETURN_IF_NOT_OK(GetConnectorUtil(&usage_avg_last, &avg_size, &avg_capacity)); - float empty_freq = 0; - RETURN_IF_NOT_OK(GetEmptyQueueFrequency(&empty_freq)); - if (mode_ == AutoTuneMode::kAutoTuneModeStep) { - MS_LOG(INFO) << "Step # " << cur_step_running_ << ". Status:"; - } else { - MS_LOG(INFO) << "Epoch # " << cur_epoch_running_ << ". Status:"; - } - // Reporting values - MS_LOG(INFO) << "Device Connector Size: " << avg_size << ", Connector Capacity: " << avg_capacity - << ", Utilization: " << (usage_avg_last * TO_PERCENT) << "%" - << ", Empty Freq: " << (empty_freq * TO_PERCENT) << "% "; - // Decision - if (usage_avg_last < DEVICE_CONNECTOR_UTIL_THRESHOLD) { - MS_LOG(INFO) << "Utilization: " << (usage_avg_last * TO_PERCENT) << "% < " - << (DEVICE_CONNECTOR_UTIL_THRESHOLD * TO_PERCENT) - << "% threshold, dataset pipeline performance may benefit from tuning."; - *isBottleneck = true; - } else { - MS_LOG(INFO) << "Utilization: " << (usage_avg_last * TO_PERCENT) << "% > " - << (DEVICE_CONNECTOR_UTIL_THRESHOLD * TO_PERCENT) - << "% threshold, dataset pipeline performance is OK."; - *isBottleneck = false; - } - return Status::OK(); -} - -Status AutoTune::RequestNumWorkerChange(int32_t op_id, int32_t old_workers, int32_t *num_workers_requested) { - AT_change_ = true; - int new_workers = std::min(*num_workers_requested, max_workers_); - new_workers = std::max(new_workers, MIN_NUM_WORKERS); - RETURN_IF_NOT_OK(tree_modifier_->AddChangeRequest(op_id, std::make_shared(new_workers))); - if (old_workers == -1) { - MS_LOG(INFO) << "Added request to change \"num_parallel_workers\" of Operator: " << ops_[op_id]->NameWithID() - << "to value: [" << new_workers << "]."; - } else { - MS_LOG(INFO) << "Added request to change \"num_parallel_workers\" of Operator: " << ops_[op_id]->NameWithID() - << "From old value: [" << old_workers << "] to new value: [" << new_workers << "]."; - } - *num_workers_requested = new_workers; - return Status::OK(); -} - -Status AutoTune::RequestConnectorCapacityChange(int32_t op_id, int32_t old_size, int32_t new_size) { - AT_change_ = true; - new_size = std::min(new_size, MAX_QUEUE_SIZE); - new_size = std::max(new_size, MIN_QUEUE_SIZE); - RETURN_IF_NOT_OK(tree_modifier_->AddChangeRequest(op_id, std::make_shared(new_size))); - if (old_size == -1) { - MS_LOG(INFO) << "Added request to change \"prefetch_size\" of Operator: " << ops_[op_id]->NameWithID() - << "to value: [" << new_size << "]."; - } else { - MS_LOG(INFO) << "Added request to change \"prefetch_size\" of Operator: " << ops_[op_id]->NameWithID() - << "From old value: [" << old_size << "] to new value: [" << new_size << "]."; - } - return Status::OK(); -} - -bool AutoTune::SkipOpsCheck(int op_id) { - // Skip Generator op - if (ops_[op_id]->Name() == "GeneratorOp") { - return true; - } - // NonMappableDataset is not supported in AutoTune -#ifndef ENABLE_ANDROID - if (std::dynamic_pointer_cast(ops_[op_id]) != nullptr) { - return true; - } -#endif - return false; -} - -Status AutoTune::AnalyseTime() { - // check for connector queue bottleneck - bool isBottleneck = false; - RETURN_IF_NOT_OK(IsDSaBottleneck(&isBottleneck)); - if (!isBottleneck) { - return Status::OK(); - } - // collect stats - std::map ops_num_workers; - RETURN_IF_NOT_OK(GetOpsNumWorker(&ops_num_workers)); - std::map out_ops_queue_util; - std::map in_ops_queue_util; - RETURN_IF_NOT_OK(GetOpsQueueUtil(&out_ops_queue_util, &in_ops_queue_util)); - std::map ops_cpu_util; - RETURN_IF_NOT_OK(GetOpsCpuUtil(&ops_cpu_util)); - // check parallel ops in loop - for (const auto &op_id : parallel_ops_ids_) { - if (SkipOpsCheck(op_id)) { - continue; - } - // op specifics - double output_queue_util = out_ops_queue_util[op_id]; - double input_queue_util = in_ops_queue_util[op_id]; - double cpu_util = ops_cpu_util[op_id]; - int32_t num_workers = ops_num_workers[op_id]; - CHECK_FAIL_RETURN_UNEXPECTED(num_workers != 0, "ParallelOp with num_workers=0"); - // derived metrics - double queue_diff = input_queue_util - output_queue_util; - int64_t queue_capacity; - RETURN_IF_NOT_OK(GetOpConnectorCapacity(op_id, &queue_capacity)); - int64_t new_queue_capacity = queue_capacity; - int32_t requested_workers = 0; - MS_LOG(DEBUG) << "Op (" << ops_[op_id]->NameWithID() << ") CPU=" << cpu_util / num_workers - << ", in=" << input_queue_util << "out=" << output_queue_util; - // map decisions - queue - if (queue_diff > INPUT_OUTPUT_QUEUE_DIFF_THRESHOLD) { - MS_LOG(INFO) << "Op (" << ops_[op_id]->NameWithID() - << ") is slow, input connector utilization=" << input_queue_util - << ", output connector utilization=" << output_queue_util << ", diff= " << queue_diff << " > " - << INPUT_OUTPUT_QUEUE_DIFF_THRESHOLD << " threshold."; - requested_workers = num_workers + INCREMENT_WORKER; - RETURN_IF_NOT_OK(RequestNumWorkerChange(op_id, num_workers, &requested_workers)); - } else if ((cpu_util / num_workers) > MAP_OP_WORKER_HIGH_THRESHOLD) { - MS_LOG(INFO) << "Op (" << ops_[op_id]->NameWithID() << ") getting high average worker cpu utilization " - << (cpu_util / num_workers) << "% > " << MAP_OP_WORKER_HIGH_THRESHOLD << "% threshold."; - requested_workers = num_workers + INCREMENT_WORKER; - RETURN_IF_NOT_OK(RequestNumWorkerChange(op_id, num_workers, &requested_workers)); - } - if ((cpu_util / num_workers) < MAP_OP_WORKER_LOW_THRESHOLD && - ((input_queue_util < INPUT_QUEUE_LOW) || (-1 * queue_diff > INPUT_OUTPUT_QUEUE_DIFF_THRESHOLD))) { - MS_LOG(INFO) << "Op (" << ops_[op_id]->NameWithID() << ") getting low average worker cpu utilization " - << (cpu_util / num_workers) << "% < " << MAP_OP_WORKER_LOW_THRESHOLD << "% threshold."; - new_queue_capacity = queue_capacity + INCREMENT_QUEUE_SIZE; - if (requested_workers == 0) { - requested_workers = num_workers; - } - new_queue_capacity = std::max(new_queue_capacity, static_cast(requested_workers)); - RETURN_IF_NOT_OK(RequestConnectorCapacityChange(op_id, queue_capacity, new_queue_capacity)); - } - } - return Status::OK(); -} - -bool AutoTune::MemoryPhaseCompareMetric(double prev_avg, double cur_avg) { - double lower_bound = prev_avg - (prev_avg * MEMORY_COMPARISON_LOWER_BOUND_PERCENT); - // If cur_avg worse than lower bound - negative impact on performance - // lower bound set to account for expected fluctuations in util numbers - if (cur_avg < lower_bound) { - return false; - } else { - return true; - } -} - -Status AutoTune::AnalyseMemory() { - double prev_avg = 0; - double cur_avg = 0; - bool comp_flag = true; - double connector_avg_size; - double connector_avg_capacity; - int total = parallel_ops_ids_.size(); - if (total == 0) { - AT_phase_ = AutoTunePhase::kAutoTuneEnd; - return Status::OK(); - } - std::map ops_num_workers; - RETURN_IF_NOT_OK(GetOpsNumWorker(&ops_num_workers)); - double reduce_percent_mode; - // Decrease queue sizes faster in epoch mode - if (mode_ == AutoTuneMode::kAutoTuneModeEpoch) { - reduce_percent_mode = QUEUE_REDUCTION_PERCENTAGE_EPOCH; - } else if (AutoTuneMode::kAutoTuneModeStep) { - reduce_percent_mode = QUEUE_REDUCTION_PERCENTAGE_STEP; - } - // Init state on first call - if (phase_3_state_ == AutoTuneMemPhase::kAutoTuneMemInit) { - count_down_ = 0; - for (auto op_id : parallel_ops_ids_) { - if ((SkipOpsCheck(op_id)) || (ops_[op_id]->Name() == "DataQueueOp")) { - // Op not supported - ignore throughout AT - (void)OP_values.push_back(-1); - continue; - } - // Op supported - attempt memory reduction on this - (void)OP_values.push_back(0); - count_down_++; - } - phase_3_state_ = AutoTuneMemPhase::kAutoTuneMemSet; - phase_3_ID_ = 0; - } - - // Exit when all viable ops have been tested - // Or if none found - if (count_down_ == 0) { - AT_phase_ = AutoTunePhase::kAutoTuneEnd; - return Status::OK(); - } - - if (phase_3_state_ == AutoTuneMemPhase::kAutoTuneMemSet) { - RETURN_IF_NOT_OK(GetConnectorUtil(&phase_3_prev_avg_, &connector_avg_size, &connector_avg_capacity)); - // Search for next viable op that can be tested - while (OP_values[phase_3_ID_] == -1) { - phase_3_ID_ = ((phase_3_ID_ + 1) % total); - } - int64_t current_queue_size; - RETURN_IF_NOT_OK(GetOpConnectorCapacity(parallel_ops_ids_[phase_3_ID_], ¤t_queue_size)); - int op_workers = ops_num_workers[parallel_ops_ids_[phase_3_ID_]]; - int target_mem = current_queue_size * reduce_percent_mode; - if (std::max(target_mem, op_workers) == op_workers) { - // Lower bound on range of queue size testing - OP_values[phase_3_ID_] = -1; - count_down_--; - target_mem = op_workers; - } else { - // Save current queue size for possible recovery and switch to compare mode - OP_values[phase_3_ID_] = current_queue_size; - phase_3_state_ = AutoTuneMemPhase::kAutotTuneMemCompare; - } - RequestConnectorCapacityChange(parallel_ops_ids_[phase_3_ID_], -1, target_mem); - } else if (phase_3_state_ == AutoTuneMemPhase::kAutotTuneMemCompare) { - // Analyse impact on model from previous change made - RETURN_IF_NOT_OK(GetConnectorUtil(&cur_avg, &connector_avg_size, &connector_avg_capacity)); - prev_avg = phase_3_prev_avg_; - comp_flag = MemoryPhaseCompareMetric(prev_avg, cur_avg); - // Compare current avg against pre-change avg - if (comp_flag == false) { - int reset_val = OP_values[phase_3_ID_]; - RequestConnectorCapacityChange(parallel_ops_ids_[phase_3_ID_], -1, reset_val); - // Op queue size reduction caused performance decrease - ignore onwards - OP_values[phase_3_ID_] = -1; - count_down_--; - } - phase_3_state_ = AutoTuneMemPhase::kAutoTuneMemSet; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/auto_tune.h b/mindspore-lite/minddata/dataset/engine/perf/auto_tune.h deleted file mode 100644 index 84e28ad3e..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/auto_tune.h +++ /dev/null @@ -1,306 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_AUTO_TUNE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_AUTO_TUNE_H_ - -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" -#include "mindspore-lite/minddata/dataset/engine/tree_modifier.h" -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" - -namespace mindspore { -namespace dataset { -class TreeModifier; -class AutoTune { - public: - /// AutoTune constructor - /// \param tree_adap_ pointer to the tree adapter - /// \param profiling_mgr_ pointer to the profiler manager - AutoTune(TreeAdapter *tree_adap, ProfilingManager *profiling_mgr); - - ~AutoTune() = default; - - /// Function to create and launch the AutoTune thread. - /// \return Status object - Status LaunchThread(); - - private: - /// Main entry function for AT, triggers loop function. - /// \return Status object - Status Main(); - - /// Primary AT loop till exit - /// \return Status object - Status ATMainLoop(bool output_intermediate_config); - - /// \brief Helper to print the tree configuration - void PrintTreeConfiguration() const; - - /// \brief Helper to print the logs after/post the main loop in AutoTune - void PostMainLogging() const; - - /// \brief Helper to summarize the execution tree - /// \param[out] out An output vector of string to store the summary - /// \return Status object - Status SummarizeTreeConfiguration(std::vector *out); - -#ifndef ENABLE_ANDROID - /// \brief Serialize the dataset and save the AT config (workers and queue size) to a json file - /// \param file_name Name of the file - /// \return Status object - Status SaveAutotuneConfig(const std::string &file_name); - - /// Setter for autotune_config_json_ - /// \return Status code - Status SetAutotuneConfigJson(); -#endif - - /// Function to collect info from the tree - /// \return Status code - Status CollectOpsInfo(); - - /// Function to check for current step and execute logic - /// \return status code - Status RunIterationStep(); - - /// Function to check for current epoch and execute logic - /// \return status code - Status RunIterationEpoch(); - - /// The AutoTune logic for pipelines that executes every epoch - /// \return status code - Status RunIteration(); - - /// Fetches connector size for steps or epoch based on mode - /// \return status code - Status GetConnectorSize(std::vector *sizes) const; - - /// Fetches connector capacity for steps or epoch based on mode - /// \return status code - Status GetConnectorCapacity(std::vector *capacities) const; - - /// Computes current connector queue util percentage - /// \param[out] usage_avg_last double return avg util percentage for connector queue - /// \param[out] avg_size double to return avg size (usage) of connector queue - /// \param[out] avg_capacity double to return avg capacity for connector queue - /// \return status code - Status GetConnectorUtil(double *usage_avg_last, double *avg_size, double *avg_capacity); - - /// Fetches Connector Queue empty frequency for steps or epoch based on mode - /// \return status code - Status GetEmptyQueueFrequency(float *empty_freq) const; - - /// Check if the dataset pipeline is the bottleneck - /// \param[out] isBottleneck bool - /// \return Status code - Status IsDSaBottleneck(bool *isBottleneck); - - /// Returns true if the pipeline is sink or non-sink - /// \return bool - bool IsSink() const; - - const int32_t TO_PERCENT = 100; - // system specifics - int32_t max_workers_; - const int32_t MIN_NUM_WORKERS = 1; - const int32_t MAX_QUEUE_SIZE = 128; - const int32_t MIN_QUEUE_SIZE = 1; - // Warmup specifics - const int32_t EPOCH_WARMUP = 1; - const int64_t STEP_WARMUP = 150; - // Worker specifics - const int32_t INCREMENT_WORKER = 2; - const int32_t DECREMENT_WORKER = -1; - // Queue Specifics - const float_t INPUT_QUEUE_LOW = 0.5; - - // Value to maintain checking for device_queue utlization at. - const float_t DEVICE_CONNECTOR_UTIL_THRESHOLD = 0.75; - - const float_t LEAF_QUEUE_THRESHOLD = 0.9; - const float_t INPUT_OUTPUT_QUEUE_DIFF_THRESHOLD = 0.35; - const int64_t INCREMENT_QUEUE_SIZE = 4; - // CPU Specifics - const float_t MAP_OP_WORKER_HIGH_THRESHOLD = 75; - const float_t MAP_OP_WORKER_LOW_THRESHOLD = 35; - // Running mode specifics - enum AutoTuneMode { kAutoTuneModeEpoch, kAutoTuneModeStep }; - enum AutoTunePhase { kAutoTunePhaseTime, kAutoTunePhaseMemory, kAutoTuneEnd }; - enum AutoTuneMemPhase { kAutoTuneMemInit, kAutoTuneMemSet, kAutotTuneMemCompare }; - // Early stop specifics - const int32_t EARLY_STOP_TRIAL_THRESHOLD_EPOCH = 4; - const int32_t EARLY_STOP_TRIAL_THRESHOLD_STEP = 10; - // Memory specifics - const float MEMORY_COMPARISON_LOWER_BOUND_PERCENT = 0.02; - const float QUEUE_REDUCTION_PERCENTAGE_EPOCH = 0.5; - const float QUEUE_REDUCTION_PERCENTAGE_STEP = 0.8; - - /// Get the out connector capacity of the operator - /// \param[in] op_id operator id - /// \param[out] capacity the capacity of the connector - /// \return Status code - Status GetOpConnectorCapacity(int32_t op_id, int64_t *capacity); - - /// Get the CPU usage of each operator in the pipeline - /// \param[out] ops_cpu_util map from op_id to cpu utilization - /// \return Status code - Status GetOpsCpuUtil(std::map *ops_cpu_util); - - /// Get the queue utilization of each operator in the pipeline - /// \param[out] ops_queue_util map from op_id to output queue utilization - /// \param[out] ops_queue_util map from op_id to input queue utilization - /// \note inline ops would report -1 in both input and output queue utilization - /// \return Status code - Status GetOpsQueueUtil(std::map *out_ops_queue_util, std::map *in_ops_queue_util); - - /// Get the number of workers for each operator in the pipeline - /// \param[out] ops_num_workers map from op_id to num_workers - /// \return Status code - Status GetOpsNumWorker(std::map *ops_num_workers); - - /// Check whether an op is an unsupported by AutoTune - /// \param op_id ID to check - /// \return bool to skip or not - bool SkipOpsCheck(int op_id); - - /// Main AutoTune algorithm - /// \return Status code - Status AnalyseTime(); - - /// AutoTune memory algorithm - /// \return Status code - Status AnalyseMemory(); - - /// Send a ChangeRequest to the operator to update the number of workers - /// \param op_id operator ID - /// \param old_workers Old number of workers for logging purposes - /// \param new_workers new number of worker - /// \return Status code - Status RequestNumWorkerChange(int32_t op_id, int32_t old_workers, int32_t *num_workers_requested); - - /// Send a ChangeRequest to the operator to update the connector capacity - /// \param op_id operator ID - /// \param old_workers Old size for logging purposes - /// \param new_workers new size - /// \return Status code - Status RequestConnectorCapacityChange(int32_t op_id, int32_t old_size, int32_t new_size); - - /// Track the pipeline time of the current epoch into avg_pipeline_times_ - /// \return Status code - Status TrackPipelineTime(); - - /// Utility function to calculate the mean/average of a list of numbers - /// \tparam T type of the vector - /// \param items vector of T - /// \return double the calculated mean - template - double Mean(const std::vector &items) const; - - /// Get and update current epoch and step counts - /// \return Status Code - Status UpdateCurrentRunInfo(); - - /// Decide whether warmup period is complete to start AT - /// \return the decision for skipping further or not - bool WarmupSkipCheck(); - - /// Save current worker and queue size configurations - /// \return Status code - Status RegisterWorkersQueue(); - - /// Reset values of workers and queue sizes for ops to saved best config - /// \return Status code - Status ResetWorkersQueue(); - - /// Compare current and previous metrics for memory performance in memory phase of - /// AT tuning. Logic can be changed without modification to primary function - /// \param prev_avg previous comparison value - normally pre-change in pipeline - /// \param cur_avg current comparison value - normally post-change in pipeline - /// \return decision on good (True) or bad (False) change in metric - bool MemoryPhaseCompareMetric(double prev_avg, double cur_avg); - - /// Pointer to the tree adapter to get tree info - TreeAdapter *tree_adapter_; - /// Pointer to the profiler manager to get statistics - ProfilingManager *profiling_manager_; - /// Unique_ptr to the tree modifier to handle change requests - std::unique_ptr tree_modifier_; - - /// mux to be used to sleep - std::mutex mux_; - /// Conditional variable used to sleep - CondVar cv_; - - /// a map from op_id to a pointer to the operator - std::map> ops_; - /// list of all map_ops - std::vector parallel_ops_ids_; - /// ID of the leaf op - int32_t leaf_op_id_; - /// vector of pipeline time per epoch - std::vector avg_pipeline_times_; - - /// the current epoch and step indices (starts from 1) - int32_t cur_epoch_running_; - int32_t last_epoch_autotuned_; - // step based auto-tuning specifics - int32_t cur_step_running_; - int64_t last_step_autotuned_; - - int32_t mode_; - int64_t step_gap_; - bool skip_flag_; - int32_t AT_phase_; - // tracking whether AT makes a change - bool AT_change_; - - // Phase 1 - Analyse Time - double phase_1_best_time_; - int32_t phase_1_no_improve_count_; - std::vector phase_1_best_workers; - std::vector phase_1_best_queue; - - // phase 2 - Analyse Memory - int32_t count_down_; - int32_t phase_3_state_; - int32_t phase_3_ID_; - double avg_batch_time; - double phase_3_prev_avg_; - std::vector OP_values; - - /// True if should save AutoTune configuration - bool save_autoconfig_; - - /// Flag to enable saving of intermediate autotune config to disk - bool save_intermediate_autoconfig_{false}; - - /// Filepath name of the final AutoTune Configuration JSON file - std::string autotune_json_filepath_; - - /// Serialized json of the optimized ir tree that holds the updated configuration (workers and queue size) - nlohmann::json autotune_config_json_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_AUTO_TUNE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/perf/connector_size.cc b/mindspore-lite/minddata/dataset/engine/perf/connector_size.cc deleted file mode 100644 index d9a16675c..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/connector_size.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/perf/connector_size.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -namespace platform = mindspore; -#else -#include "mindspore-lite/src/common/file_utils.h" -namespace platform = mindspore::lite; -#endif - -using json = nlohmann::json; -namespace mindspore { -namespace dataset { -using Qrow = std::vector; - -// Sample action -Status ConnectorSize::Sample() { - if (!active_) { - return Status::OK(); - } - Qrow cur_row; - (void)std::transform(tree_->begin(), tree_->end(), std::back_inserter(cur_row), - [](const DatasetOp &op) { return op.ConnectorSize(); }); - // Tree Iterator is in PostOrder (leaf first, e.g., 3,2,1) - // reverse the order of the vector to get the root first. - std::reverse(cur_row.begin(), cur_row.end()); - std::lock_guard guard(lock_); - // Push new row of sample - sample_table_.push_back(cur_row); - (void)ts_.emplace_back(ProfilingTime::GetCurMilliSecond()); - return Status::OK(); -} - -// JSON serializer helper function -json ConnectorSize::ParseOpInfo(const DatasetOp &node) const { - json json_node; - json_node["op_id"] = node.id(); - json_node["op_type"] = node.Name(); - json_node["num_workers"] = node.NumWorkers(); - json metrics; - // DataQueueOp is a special op,it is not inlined but its output queue is invalid. - // So we should not output its queue size. - if (!node.inlined() && node.Name() != "DataQueueOp") { - metrics["output_queue"] = {{"length", node.ConnectorCapacity()}}; - } - json_node["metrics"] = metrics; - - auto children = node.Children(); - std::vector children_id; - (void)std::transform(children.begin(), children.end(), std::back_inserter(children_id), - [](const std::shared_ptr &op) -> int32_t { return op->id(); }); - if (!children_id.empty()) { - json_node["children"] = children_id; - } - - return json_node; -} - -// Save profiling data to file -Status ConnectorSize::SaveToFile(const std::string &dir_path, const std::string &rank_id) { - Path path = GetFileName(dir_path, rank_id); - // Remove the file if it exists (from prior profiling usage) - RETURN_IF_NOT_OK(path.Remove()); - std::string file_path = path.ToString(); - - json output = initial_nodes_data; - output["sampling_interval"] = GlobalContext::config_manager()->monitor_sampling_interval(); - - // Traverse the JSON initialized in Init() to access each op's information - CHECK_FAIL_RETURN_UNEXPECTED(output.contains("op_info"), "JSON data does not include op_info!"); - for (uint32_t idx = 0; idx < output["op_info"].size(); idx++) { - std::vector cur_queue_size; - (void)std::transform(sample_table_.begin(), sample_table_.end(), std::back_inserter(cur_queue_size), - [&](const ConnectorSizeSample &sample) { return sample[idx]; }); - - auto &ops_data = output["op_info"]; - if (ops_data[idx]["metrics"].contains("output_queue") && ops_data[idx]["op_type"] != "DataQueueOp") { - ops_data[idx]["metrics"]["output_queue"]["size"] = cur_queue_size; - } - } - - // Discard the content of the file when opening. - std::ofstream os(file_path, std::ios::out | std::ios::trunc); - os << output; - os.close(); - platform::ChangeFileMode(file_path, S_IRUSR | S_IWUSR); - - return Status::OK(); -} - -Status ConnectorSize::Init() { - // Traverse the ExecutionTree for JSON node generation - for (auto &node : *tree_) { - json json_node = ParseOpInfo(node); - initial_nodes_data["op_info"].push_back(json_node); - } - // Tree Iterator is in PostOrder (leaf first, e.g., 3,2,1) - // reverse the order of the vector to get the root first. - std::reverse(initial_nodes_data["op_info"].begin(), initial_nodes_data["op_info"].end()); - - return Status::OK(); -} - -void ConnectorSize::Clear() { - ts_.clear(); - sample_table_.clear(); - initial_nodes_data.clear(); -} - -Status ConnectorSize::GetOpConnectorSize(int32_t op_id, uint64_t start_time, uint64_t end_time, - std::vector *result) { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "Op_id: " << op_id << " start_ts: " << start_time << " end_ts: " << end_time; - CHECK_FAIL_RETURN_UNEXPECTED(start_time < end_time, - "Expected start_time < end_time. Got start_ts: " + std::to_string(start_time) + - " end_ts: " + std::to_string(end_time)); - std::lock_guard guard(lock_); - CHECK_FAIL_RETURN_UNEXPECTED( - ts_.size() == sample_table_.size(), - "Expected ts_.size() == sample_table_.size(). Got ts_.size: " + std::to_string(ts_.size()) + - " sample_table_.size: " + std::to_string(sample_table_.size())); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_time); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_time); - // get ts_ indices - auto start_index = std::distance(ts_.begin(), lower); - auto end_index = std::distance(ts_.begin(), upper); - MS_LOG(INFO) << "start_index: " << start_index << " end_index: " << end_index; - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - // convert indices to sample_table_ iterator - auto first_iter = sample_table_.begin() + start_index; - auto last_iter = sample_table_.begin() + end_index; - // op_id corresponds to the index in sample vector - (void)std::transform(first_iter, last_iter, std::back_inserter(*result), - [&](const ConnectorSizeSample &sample) { return sample[static_cast(op_id)]; }); - - return Status::OK(); -} - -Path ConnectorSize::GetFileName(const std::string &dir_path, const std::string &rank_id) { - return Path(dir_path) / Path("pipeline_profiling_" + rank_id + ".json"); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/connector_size.h b/mindspore-lite/minddata/dataset/engine/perf/connector_size.h deleted file mode 100644 index b8e71e97f..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/connector_size.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CONNECTOR_SIZE_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CONNECTOR_SIZE_H - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -using json = nlohmann::json; - -namespace mindspore { -namespace dataset { -class ExecutionTree; - -// Connector size sampling samples the output connector size of each op in the pipeline. -// It support JSON serialization for external usage. -class ConnectorSize : public Sampling { - // Connector size sampling data is stored as a 2D vector - // op_0 ... op_m - // sample_0 size_0_0 ... size_m_0 - // ... ... ... ... - // sample_n size_0_m ... size_m_n - // - // A circular buffer will be implemented in the future to make this table more flexible. - using ConnectorSizeSample = std::vector; - using ConnectorSizeSampleTable = std::vector; - using Timestamps = std::vector; - - public: - explicit ConnectorSize(ExecutionTree *tree) : tree_(tree) {} - - ~ConnectorSize() override = default; - - // Driver function for connector size sampling. - // This function samples the connector size of every nodes within the ExecutionTree - Status Sample() override; - - std::string Name() const override { return kConnectorSizeSamplingName; } - - // Save sampling data to file - // @return Status The status code returned - Status SaveToFile(const std::string &dir_path, const std::string &rank_id) override; - - Status Init() override; - - // Parse op information and transform to json format - json ParseOpInfo(const DatasetOp &node) const; - - // Change file mode after save throughput data - Status ChangeFileMode(const std::string &dir_path, const std::string &rank_id) override { return Status::OK(); } - - // Get the vector of connector sizes of given op for samples taken between start and end time - Status GetOpConnectorSize(int32_t op_id, uint64_t start_time, uint64_t end_time, std::vector *result); - - // Clear all collected data - void Clear() override; - - protected: - Path GetFileName(const std::string &dir_path, const std::string &rank_id) override; - - private: - json initial_nodes_data; // store data when execution tree is running. (all information for ops except sampled data) - ExecutionTree *tree_ = nullptr; // ExecutionTree pointer - ConnectorSizeSampleTable sample_table_; // Dataset structure to store all samples of connector size sampling - Timestamps ts_; // time of sample -}; - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CONNECTOR_SIZE_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.cc b/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.cc deleted file mode 100644 index dee5e6c17..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.cc +++ /dev/null @@ -1,782 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h" - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/api/python/pybind_conversion.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "utils/file_utils.h" - -namespace mindspore { -namespace dataset { -using json = nlohmann::json; -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -#define USING_LINUX -#endif - -#if defined(USING_LINUX) -int32_t SystemInfo::num_cpu_ = get_nprocs_conf(); -#else -int32_t SystemInfo::num_cpu_ = 0; -#endif - -constexpr uint64_t kBInMB = 1024; // Constant for kByte to MByte division conversion - -Status SystemInfo::ParseCpuInfo(const std::string &str) { - SystemStat system_cpu_stat; - uint64_t nice = 0; - uint64_t irq = 0; - uint64_t softirq = 0; - if (sscanf_s(str.c_str(), "%*s %lu %lu %lu %lu %lu %lu %lu", &system_cpu_stat.user_stat, &nice, - &system_cpu_stat.sys_stat, &system_cpu_stat.idle_stat, &system_cpu_stat.io_stat, &irq, - &softirq) == EOF) { - return Status(StatusCode::kMDUnexpectedError, "Get System CPU failed."); - } - - system_cpu_stat.total_stat = system_cpu_stat.user_stat + nice + system_cpu_stat.sys_stat + system_cpu_stat.idle_stat + - system_cpu_stat.io_stat + irq + softirq; - SystemUtil system_cpu_util = {0, 0, 0, 0}; - // Calculate the utilization from the second sampling - if (!first_sample_) { - int one_hundred = 100; - system_cpu_util.user_utilization = - static_cast(round((system_cpu_stat.user_stat - prev_sys_stat_.user_stat) * 1.0 / - (system_cpu_stat.total_stat - prev_sys_stat_.total_stat) * one_hundred)); - system_cpu_util.sys_utilization = - static_cast(round((system_cpu_stat.sys_stat - prev_sys_stat_.sys_stat) * 1.0 / - (system_cpu_stat.total_stat - prev_sys_stat_.total_stat) * one_hundred)); - system_cpu_util.io_utilization = - static_cast(round((system_cpu_stat.io_stat - prev_sys_stat_.io_stat) * 1.0 / - (system_cpu_stat.total_stat - prev_sys_stat_.total_stat) * one_hundred)); - system_cpu_util.idle_utilization = - static_cast(round((system_cpu_stat.idle_stat - prev_sys_stat_.idle_stat) * 1.0 / - (system_cpu_stat.total_stat - prev_sys_stat_.total_stat) * one_hundred)); - } - // append the 0 util as well to maintain sys_cpu_util_.size == ts_.size - (void)sys_cpu_util_.emplace_back(system_cpu_util); - prev_sys_stat_ = system_cpu_stat; - return Status::OK(); -} - -Status SystemInfo::ParseCtxt(const std::string &str) { - uint64_t ctxt; - if (sscanf_s(str.c_str(), "%*s %lu", &ctxt) == EOF) { - return Status(StatusCode::kMDUnexpectedError, "Get context switch count failed."); - } - // first context switch count will be 0 - auto val = first_sample_ ? 0 : ctxt - prev_context_switch_count_; - context_switch_count_.push_back(val); - prev_context_switch_count_ = ctxt; - return Status::OK(); -} - -Status SystemInfo::ParseRunningProcess(const std::string &str) { - uint32_t running_process; - if (sscanf_s(str.c_str(), "%*s %ud", &running_process) == EOF) { - return Status(StatusCode::kMDUnexpectedError, "Get context switch count failed."); - } - running_process_.push_back(running_process); - return Status::OK(); -} - -Status SystemInfo::SampleAndGetCurrPrevStat(SystemStat *current_stat, SystemStat *previous_stat) { - RETURN_UNEXPECTED_IF_NULL(previous_stat); - std::ifstream file("/proc/stat", std::ios::in); - if (!file.is_open()) { - MS_LOG(INFO) << "Failed to open /proc/stat file."; - return {StatusCode::kMDUnexpectedError, "Failed to open /proc/stat file."}; - } - *previous_stat = prev_sys_stat_; - bool first_line = true; - std::string line; - Status s; - while (getline(file, line)) { - if (first_line) { - first_line = false; - s = ParseCpuInfo(line); - if (s != Status::OK()) { - file.close(); - return s; - } - s = ParseCpuInfo(line); - if (s.IsError()) { - file.close(); - return s; - } - } - if (line.find("ctxt") != std::string::npos) { - s = ParseCtxt(line); - if (s != Status::OK()) { - file.close(); - return s; - } - s = ParseCtxt(line); - if (s.IsError()) { - file.close(); - return s; - } - } - if (line.find("procs_running") != std::string::npos) { - s = ParseRunningProcess(line); - if (s != Status::OK()) { - file.close(); - return s; - } - s = ParseRunningProcess(line); - if (s.IsError()) { - file.close(); - return s; - } - } - } - // after the loop above, prev_sys_stat_ has the current value - *current_stat = prev_sys_stat_; - file.close(); - - first_sample_ = false; - RETURN_IF_NOT_OK(SampleSystemMemInfo()); - return Status::OK(); -} - -Status SystemInfo::SampleSystemMemInfo() { - std::ifstream file("/proc/meminfo", std::ios::in); - if (!file.is_open()) { - MS_LOG(INFO) << "Unable to open /proc/meminfo. Continue processing."; - last_mem_sampling_failed_ = true; - // Note: Return Status:OK() although failed to open /proc/meminfo file - return Status::OK(); - } - std::string line; - uint64_t total = 0; - uint64_t available = 0; - uint64_t used = 0; - uint64_t curr_val = 0; - - (void)getline(file, line); - if (sscanf_s(line.c_str(), "%*[MemTotal:] %lu %*[kB]", &curr_val) == 1) { - total = curr_val; - (void)getline(file, line); - (void)getline(file, line); - if (sscanf_s(line.c_str(), "%*[MemAvailable:] %lu %*[kB]", &curr_val) == 1) { - available = curr_val; - used = total - available; - last_mem_sampling_failed_ = false; - } else { - prev_system_memory_info_ = {0, 0, 0}; - last_mem_sampling_failed_ = true; - } - } else { - prev_system_memory_info_ = {0, 0, 0}; - last_mem_sampling_failed_ = true; - } - // Note: Must close file before returning from this function. - file.close(); - - if (last_mem_sampling_failed_) { - return Status::OK(); - } - - prev_system_memory_info_.total_mem = static_cast(total) / kBInMB; - prev_system_memory_info_.available_mem = static_cast(available) / kBInMB; - prev_system_memory_info_.used_mem = static_cast(used) / kBInMB; - - system_memory_info_.push_back(SystemMemInfo{ - prev_system_memory_info_.total_mem, prev_system_memory_info_.available_mem, prev_system_memory_info_.used_mem}); - - return Status::OK(); -} - -Status SystemInfo::GetUserCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << " sys_cpu_util_.size: " << sys_cpu_util_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= sys_cpu_util_.size(), - "Expected end_index <= sys_cpu_util_.size(). Got end_index: " + std::to_string(end_index) + - " sys_cpu_util_.size: " + std::to_string(sys_cpu_util_.size())); - (void)std::transform(sys_cpu_util_.begin() + start_index, sys_cpu_util_.begin() + end_index, - std::back_inserter(*result), [&](const SystemUtil &info) { return info.user_utilization; }); - return Status::OK(); -} - -Status SystemInfo::GetSysCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << "sys_cpu_util_.size: " << sys_cpu_util_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= sys_cpu_util_.size(), - "Expected end_index <= sys_cpu_util_.size(). Got end_index: " + std::to_string(end_index) + - " sys_cpu_util_.size: " + std::to_string(sys_cpu_util_.size())); - (void)std::transform(sys_cpu_util_.begin() + start_index, sys_cpu_util_.begin() + end_index, - std::back_inserter(*result), [&](const SystemUtil &info) { return info.sys_utilization; }); - return Status::OK(); -} - -std::vector SystemInfo::GetIOCpuUtil() const { - std::vector io_util; - (void)std::transform(sys_cpu_util_.begin(), sys_cpu_util_.end(), std::back_inserter(io_util), - [&](const SystemUtil &info) { return info.io_utilization; }); - return io_util; -} - -std::vector SystemInfo::GetIdleCpuUtil() const { - std::vector idle_util; - (void)std::transform(sys_cpu_util_.begin(), sys_cpu_util_.end(), std::back_inserter(idle_util), - [&](const SystemUtil &info) { return info.idle_utilization; }); - return idle_util; -} - -std::vector TaskCpuInfo::GetSysCpuUtil() const { - std::vector sys_util; - (void)std::transform(task_cpu_util_.begin(), task_cpu_util_.end(), std::back_inserter(sys_util), - [&](const TaskUtil &info) { - return static_cast(info.sys_utilization * static_cast(SystemInfo::num_cpu_)); - }); - return sys_util; -} - -std::vector TaskCpuInfo::GetUserCpuUtil() const { - std::vector user_util; - (void)std::transform(task_cpu_util_.begin(), task_cpu_util_.end(), std::back_inserter(user_util), - [&](const TaskUtil &info) { - return static_cast(info.user_utilization * static_cast(SystemInfo::num_cpu_)); - }); - return user_util; -} - -TaskUtil TaskCpuInfo::GetLatestCpuUtil() const { - TaskUtil ret = {0, 0}; - if (!task_cpu_util_.empty() && !last_sampling_failed_) { - ret = task_cpu_util_.back(); - } - return ret; -} - -Status ProcessInfo::SampleMemInfo() { - std::ifstream file("/proc/" + std::to_string(pid_) + "/smaps", std::ios::in); - if (!file.is_open()) { - MS_LOG(INFO) << "Unable to open /proc/" << pid_ << "/smaps file. Continue processing."; - last_mem_sampling_failed_ = true; - // Note: Return Status:OK() although failed to open /proc//smaps file - return Status::OK(); - } - std::string line; - uint64_t total_vss = 0; - uint64_t total_rss = 0; - uint64_t total_pss = 0; - uint64_t curr_val = 0; - while (getline(file, line)) { - if (sscanf_s(line.c_str(), "%*[Size:] %lu %*[kB]", &curr_val) == 1) { - total_vss += curr_val; - } else if (sscanf_s(line.c_str(), "%*[Rss:] %lu %*[kB]", &curr_val) == 1) { - total_rss += curr_val; - } else if (sscanf_s(line.c_str(), "%*[Pss:] %lu %*[kB]", &curr_val) == 1) { - total_pss += curr_val; - } - } - file.close(); - last_mem_sampling_failed_ = false; - - prev_memory_info_.vss = static_cast(total_vss) / kBInMB; - prev_memory_info_.rss = static_cast(total_rss) / kBInMB; - prev_memory_info_.pss = static_cast(total_pss) / kBInMB; - - // Sum the memory usage of all child processes and add to parent process - if (IsParent()) { - for (auto child : child_processes_) { - MemoryInfo child_mem_info = child->GetLatestMemoryInfo(); - prev_memory_info_.vss += child_mem_info.vss; - prev_memory_info_.rss += child_mem_info.rss; - prev_memory_info_.pss += child_mem_info.pss; - } - } - - // Append latest data to vector if we want to track history for this process - if (track_sampled_history_) { - process_memory_info_.push_back(MemoryInfo{prev_memory_info_.vss, prev_memory_info_.rss, prev_memory_info_.pss}); - } - - return Status::OK(); -} - -Status ProcessInfo::Sample(uint64_t total_time_elapsed) { - std::ifstream file("/proc/" + std::to_string(pid_) + "/stat", std::ios::in); - if (!file.is_open()) { - MS_LOG(INFO) << "Unable to open /proc/" << pid_ << "/stat file. Continue processing."; - last_sampling_failed_ = true; - RETURN_IF_NOT_OK(SampleMemInfo()); - // Note: Return Status:OK() although failed to open /proc//stat file - return Status::OK(); - } - std::string str; - (void)getline(file, str); - uint64_t utime = 0, stime = 0; - if (sscanf_s(str.c_str(), "%*d %*s %*s %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %lu %lu", &utime, &stime) == - EOF) { - file.close(); - last_sampling_failed_ = true; - return Status(StatusCode::kMDUnexpectedError, "Get device CPU failed."); - } - file.close(); - last_sampling_failed_ = false; - if (!first_sample_ && total_time_elapsed > 0) { - float user_util = (utime - prev_task_stat_.user_stat) * 1.0 / (total_time_elapsed)*100.0; - float sys_util = (stime - prev_task_stat_.sys_stat) * 1.0 / (total_time_elapsed)*100.0; - (void)task_cpu_util_.emplace_back(TaskUtil{user_util, sys_util}); - } - prev_task_stat_.user_stat = utime; - prev_task_stat_.sys_stat = stime; - first_sample_ = false; - RETURN_IF_NOT_OK(SampleMemInfo()); - return Status::OK(); -} - -Status ThreadCpuInfo::Sample(uint64_t total_time_elapsed) { - if (last_sampling_failed_) { - // thread is probably terminated - return Status::OK(); - } - std::ifstream file("/proc/" + std::to_string(pid_) + "/task/" + std::to_string(tid_) + "/stat", std::ios::in); - if (!file.is_open()) { - MS_LOG(INFO) << "Unable to open /proc/" << pid_ << "/task/" << tid_ << "/stat file. Continue processing."; - last_sampling_failed_ = true; - return Status::OK(); - } - std::string str; - (void)getline(file, str); - uint64_t utime; - uint64_t stime; - if (sscanf_s(str.c_str(), "%*d %*s %*s %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %*lu %lu %lu", &utime, &stime) == - EOF) { - file.close(); - last_sampling_failed_ = true; - return Status(StatusCode::kMDUnexpectedError, "Get thread CPU failed."); - } - file.close(); - last_sampling_failed_ = false; - if (!first_sample_) { - float user_util = ((utime - prev_task_stat_.user_stat) * 1.0 / total_time_elapsed) * 100.0; - float sys_util = ((stime - prev_task_stat_.sys_stat) * 1.0 / total_time_elapsed) * 100.0; - (void)task_cpu_util_.emplace_back(TaskUtil{user_util, sys_util}); - } - prev_task_stat_.user_stat = utime; - prev_task_stat_.sys_stat = stime; - first_sample_ = false; - return Status::OK(); -} - -bool MDOperatorCpuInfo::TaskExists(pid_t id) const { return task_by_id_.find(id) != task_by_id_.end(); } - -void MDOperatorCpuInfo::AddTask(const std::shared_ptr &task_ptr) { - auto id = task_ptr->GetId(); - if (!TaskExists(id)) { - (void)task_by_id_.emplace(id, task_ptr); - } -} - -void MDOperatorCpuInfo::CalculateOperatorUtilization() { - OpUtil op_util{0, 0}; - for (auto const &[task_id, task_ptr] : task_by_id_) { - MS_LOG(DEBUG) << "Processing task_id: " << task_id; - auto task_util = task_ptr->GetLatestCpuUtil(); - op_util.user_utilization += task_util.user_utilization; - op_util.sys_utilization += task_util.sys_utilization; - } - (void)op_cpu_util_.emplace_back(op_util); -} - -Status MDOperatorCpuInfo::GetUserCpuUtil(uint64_t start_index, uint64_t end_index, - std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << " op_cpu_util_.size: " << op_cpu_util_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= op_cpu_util_.size(), - "Expected end_index <= op_cpu_util_.size(). Got end_index: " + std::to_string(end_index) + - " op_cpu_util_.size: " + std::to_string(op_cpu_util_.size())); - auto first_iter = op_cpu_util_.begin() + start_index; - auto last_iter = op_cpu_util_.begin() + end_index; - (void)std::transform(first_iter, last_iter, std::back_inserter(*result), [&](const OpUtil &info) { - return static_cast(info.user_utilization * static_cast(SystemInfo::num_cpu_)); - }); - return Status::OK(); -} - -Status MDOperatorCpuInfo::GetSysCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << " op_cpu_util_.size: " << op_cpu_util_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= op_cpu_util_.size(), - "Expected end_index <= op_cpu_util_.size(). Got end_index: " + std::to_string(end_index) + - " op_cpu_util_.size: " + std::to_string(op_cpu_util_.size())); - auto first_iter = op_cpu_util_.begin() + start_index; - auto last_iter = op_cpu_util_.begin() + end_index; - (void)std::transform(first_iter, last_iter, std::back_inserter(*result), [&](const OpUtil &info) { - return static_cast(info.sys_utilization * static_cast(SystemInfo::num_cpu_)); - }); - return Status::OK(); -} - -Status CpuSampler::Sample() { - if (active_ == false) { - return Status::OK(); - } - std::lock_guard guard(lock_); - // Function to Update TaskList - // Loop through all tasks to find any new threads - // Get all multi-processing Ops from Python only if fetched_all_process = False - // Create new TaskCpuInfo as required and update OpInfo - RETURN_IF_NOT_OK(UpdateTaskList()); - - // Sample SystemInfo - Update current and move current to previous stat and calc Util - SystemStat current_sys_stat; - SystemStat previous_sys_stat; - RETURN_IF_NOT_OK(sys_info_.SampleAndGetCurrPrevStat(¤t_sys_stat, &previous_sys_stat)); - auto total_time_elapsed = current_sys_stat.total_stat - previous_sys_stat.total_stat; - - // Call Sample on all - // Read /proc/ files and get stat, calculate util - for (auto &task_ptr : tasks_) { - (void)task_ptr->Sample(total_time_elapsed); - } - - // Call after Sample is called on all child processes - (void)main_process_info_->Sample(total_time_elapsed); - - // Calculate OperatorCpuInfo - for (auto &[op_id, op_info] : op_info_by_id_) { - MS_LOG(DEBUG) << "Calculate operator cpu utilization for OpId: " << op_id; - op_info.CalculateOperatorUtilization(); - } - - // Get sampling time. - (void)ts_.emplace_back(ProfilingTime::GetCurMilliSecond()); - - return Status::OK(); -} - -Status CpuSampler::UpdateTaskList() { - List allTasks = tree->AllTasks()->GetTask(); - for (auto &task : allTasks) { - int32_t op_id = task.get_operator_id(); - // check if the op_info was initialized in Init - auto iter = op_info_by_id_.find(op_id); - if (iter != op_info_by_id_.end()) { - int32_t tid = task.get_linux_id(); - if (!iter->second.TaskExists(tid)) { - auto task_cpu_info_ptr = std::make_shared(main_pid_, tid); - (void)tasks_.emplace_back(task_cpu_info_ptr); - iter->second.AddTask(task_cpu_info_ptr); - } - } - } - for (const auto &op : *tree) { - std::vector pids = op.GetMPWorkerPIDs(); - int32_t op_id = op.id(); - auto iter = op_info_by_id_.find(op_id); - if (iter != op_info_by_id_.end()) { - for (auto pid : pids) { - if (!iter->second.TaskExists(pid)) { - auto task_cpu_info_ptr = std::make_shared(pid); - (void)tasks_.emplace_back(task_cpu_info_ptr); - main_process_info_->AddChildProcess(task_cpu_info_ptr); - iter->second.AddTask(task_cpu_info_ptr); - } - } - } - } - - if (!fetched_all_python_multiprocesses_ && tree->IsPython()) { - py::gil_scoped_acquire gil_acquire; - py::module ds = py::module::import("mindspore.dataset.engine.datasets"); - py::tuple process_info = ds.attr("_get_operator_process")(); - auto sub_process = py::reinterpret_borrow(process_info[0]); - fetched_all_python_multiprocesses_ = py::reinterpret_borrow(process_info[1]); - // parse dict value - auto op_to_process = toIntMap(sub_process); - for (auto const &[op_id, process_list] : op_to_process) { - for (auto pid : process_list) { - auto iter = op_info_by_id_.find(op_id); - if (iter != op_info_by_id_.end()) { - if (!iter->second.TaskExists(pid)) { - auto task_cpu_info_ptr = std::make_shared(pid); - (void)tasks_.emplace_back(task_cpu_info_ptr); - main_process_info_->AddChildProcess(task_cpu_info_ptr); - iter->second.AddTask(task_cpu_info_ptr); - } - } - } - } - } - - return Status::OK(); -} - -Status CpuSampler::Init() { -#if defined(USING_LINUX) - main_pid_ = syscall(SYS_getpid); -#endif - for (auto iter = tree->begin(); iter != tree->end(); (void)iter++) { - auto op_id = iter->id(); - (void)op_info_by_id_.emplace(std::make_pair(op_id, MDOperatorCpuInfo(op_id))); - } - // thread id of main thread is same as the process ID - main_thread_cpu_info_ = std::make_shared(main_pid_, main_pid_); - (void)tasks_.emplace_back(main_thread_cpu_info_); - main_process_info_ = std::make_shared(main_pid_, true); - return Status::OK(); -} - -void CpuSampler::Clear() { - ts_.clear(); - tasks_.clear(); - main_thread_cpu_info_.reset(); - main_process_info_.reset(); - op_info_by_id_.clear(); - fetched_all_python_multiprocesses_ = false; -} - -Status CpuSampler::ChangeFileMode(const std::string &dir_path, const std::string &rank_id) { - Path path = GetFileName(dir_path, rank_id); - std::string file_path = path.ToString(); - mindspore::ChangeFileMode(file_path, S_IRUSR | S_IWUSR); - return Status::OK(); -} - -Status CpuSampler::SaveToFile(const std::string &dir_path, const std::string &rank_id) { - Path path = GetFileName(dir_path, rank_id); - // Remove the file if it exists (from prior profiling usage) - RETURN_IF_NOT_OK(path.Remove()); - std::string file_path = path.ToString(); - - // construct json obj to write to file - json output; - output["cpu_processor_num"] = SystemInfo::num_cpu_; - std::vector system_user_util, system_sys_util; - // end_index = ts_.size() essentially means to get all sampled points - (void)sys_info_.GetUserCpuUtil(0, ts_.size(), &system_user_util); - (void)sys_info_.GetSysCpuUtil(0, ts_.size(), &system_sys_util); - output["device_info"] = {{"context_switch_count", sys_info_.GetContextSwitchCount()}, - {"idle_utilization", sys_info_.GetIdleCpuUtil()}, - {"io_utilization", sys_info_.GetIOCpuUtil()}, - {"sys_utilization", system_sys_util}, - {"user_utilization", system_user_util}, - {"runnable_process", sys_info_.GetRunningProcess()}}; - // array of op_info json objects - json op_infos; - for (auto &[op_id, op_info] : op_info_by_id_) { - MS_LOG(INFO) << "Processing op_id: " << op_id; - std::vector user_util, sys_util; - (void)op_info.GetSysCpuUtil(0, ts_.size(), &sys_util); - (void)op_info.GetUserCpuUtil(0, ts_.size(), &user_util); - json op_info_json = {{"metrics", {{"user_utilization", user_util}, {"sys_utilization", sys_util}}}, - {"op_id", op_id}}; - (void)op_infos.emplace_back(op_info_json); - } - output["op_info"] = op_infos; - - output["process_info"] = {{"user_utilization", main_process_info_->GetUserCpuUtil()}, - {"sys_utilization", main_process_info_->GetSysCpuUtil()}}; - - output["sampling_interval"] = GlobalContext::config_manager()->monitor_sampling_interval(); - output["time_stamp"] = ts_; - - std::vector vss, rss, pss; - (void)main_process_info_->GetMemoryInfo(ProcessMemoryMetric::kVSS, 0, ts_.size(), &vss); - (void)main_process_info_->GetMemoryInfo(ProcessMemoryMetric::kRSS, 0, ts_.size(), &rss); - (void)main_process_info_->GetMemoryInfo(ProcessMemoryMetric::kPSS, 0, ts_.size(), &pss); - output["process_memory_info"] = {{"vss_mbytes", vss}, {"rss_mbytes", rss}, {"pss_mbytes", pss}}; - - std::vector mem_total, mem_avail, mem_used; - (void)sys_info_.GetSystemMemInfo(SystemMemoryMetric::kMemoryTotal, 0, ts_.size(), &mem_total); - (void)sys_info_.GetSystemMemInfo(SystemMemoryMetric::kMemoryAvailable, 0, ts_.size(), &mem_avail); - (void)sys_info_.GetSystemMemInfo(SystemMemoryMetric::kMemoryUsed, 0, ts_.size(), &mem_used); - output["system_memory_info"] = {{"total_sys_memory_mbytes", mem_total}, - {"available_sys_memory_mbytes", mem_avail}, - {"used_sys_memory_mbytes", mem_used}}; - - // Discard the content of the file when opening. - std::ofstream os(file_path, std::ios::out | std::ios::trunc); - os << output; - os.close(); - - mindspore::ChangeFileMode(file_path, S_IRUSR | S_IWUSR); - - return Status::OK(); -} - -Status CpuSampler::GetOpUserCpuUtil(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::lock_guard guard(lock_); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_ts); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_ts); - // std::distance is O(1) since vector allows random access - auto start_index = std::distance(ts_.begin(), lower); - auto end_index = std::distance(ts_.begin(), upper); - auto op_info = op_info_by_id_.find(op_id); - CHECK_FAIL_RETURN_UNEXPECTED(op_info != op_info_by_id_.end(), "Op Id: " + std::to_string(op_id) + " not found."); - return op_info->second.GetUserCpuUtil(start_index, end_index, result); -} - -Status CpuSampler::GetOpSysCpuUtil(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::lock_guard guard(lock_); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_ts); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_ts); - // std::distance is O(1) since vector allows random access - auto start_index = std::distance(ts_.begin(), lower); - auto end_index = std::distance(ts_.begin(), upper); - auto op_info = op_info_by_id_.find(op_id); - CHECK_FAIL_RETURN_UNEXPECTED(op_info != op_info_by_id_.end(), "Op Id: " + std::to_string(op_id) + " not found."); - return op_info->second.GetSysCpuUtil(start_index, end_index, result); -} - -Status CpuSampler::GetSystemUserCpuUtil(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::lock_guard guard(lock_); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_ts); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_ts); - // std::distance is O(1) since vector allows random access - auto start_index = std::distance(ts_.begin(), lower); - auto end_index = std::distance(ts_.begin(), upper); - return sys_info_.GetUserCpuUtil(start_index, end_index, result); -} - -Status CpuSampler::GetSystemSysCpuUtil(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::lock_guard guard(lock_); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_ts); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_ts); - // std::distance is O(1) since vector allows random access - auto start_index = std::distance(ts_.begin(), lower); - auto end_index = std::distance(ts_.begin(), upper); - return sys_info_.GetSysCpuUtil(start_index, end_index, result); -} - -Path CpuSampler::GetFileName(const std::string &dir_path, const std::string &rank_id) { - return Path(dir_path) / Path("minddata_cpu_utilization_" + rank_id + ".json"); -} - -MemoryInfo ProcessInfo::GetLatestMemoryInfo() const { - MemoryInfo ret = {0, 0, 0}; - if (!last_mem_sampling_failed_) { - ret = prev_memory_info_; - } - return ret; -} - -Status ProcessInfo::GetMemoryInfo(ProcessMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << "process_memory_info_.size: " << process_memory_info_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= process_memory_info_.size(), - "Expected end_index <= process_memory_info_.size(). Got end_index: " + std::to_string(end_index) + - " process_memory_info_.size: " + std::to_string(process_memory_info_.size())); - if (metric == ProcessMemoryMetric::kVSS) { - (void)std::transform(process_memory_info_.begin() + start_index, process_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const MemoryInfo &info) { return static_cast(info.vss); }); - } else if (metric == ProcessMemoryMetric::kRSS) { - (void)std::transform(process_memory_info_.begin() + start_index, process_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const MemoryInfo &info) { return static_cast(info.rss); }); - } else if (metric == ProcessMemoryMetric::kPSS) { - (void)std::transform(process_memory_info_.begin() + start_index, process_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const MemoryInfo &info) { return static_cast(info.pss); }); - } - return Status::OK(); -} - -Status CpuSampler::GetProcessMemoryInfo(ProcessMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) { - return (main_process_info_->GetMemoryInfo(metric, start_index, end_index, result)); -} - -void ProcessInfo::AddChildProcess(const std::shared_ptr &child_ptr) { - (void)child_processes_.emplace_back(child_ptr); -} - -bool ProcessInfo::IsParent() { return !(child_processes_.empty()); } - -Status SystemInfo::GetSystemMemInfo(SystemMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) const { - RETURN_UNEXPECTED_IF_NULL(result); - MS_LOG(DEBUG) << "start_index: " << start_index << " end_index: " << end_index - << "system_memory_info_.size: " << system_memory_info_.size(); - CHECK_FAIL_RETURN_UNEXPECTED(start_index <= end_index, - "Expected start_index <= end_index. Got start_index: " + std::to_string(start_index) + - " end_index: " + std::to_string(end_index)); - CHECK_FAIL_RETURN_UNEXPECTED( - end_index <= system_memory_info_.size(), - "Expected end_index <= system_memory_info_.size(). Got end_index: " + std::to_string(end_index) + - " system_memory_info_.size: " + std::to_string(system_memory_info_.size())); - if (metric == SystemMemoryMetric::kMemoryTotal) { - (void)std::transform(system_memory_info_.begin() + start_index, system_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const SystemMemInfo &info) { return static_cast(info.total_mem); }); - } else if (metric == SystemMemoryMetric::kMemoryAvailable) { - (void)std::transform(system_memory_info_.begin() + start_index, system_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const SystemMemInfo &info) { return static_cast(info.available_mem); }); - } else if (metric == SystemMemoryMetric::kMemoryUsed) { - (void)std::transform(system_memory_info_.begin() + start_index, system_memory_info_.begin() + end_index, - std::back_inserter(*result), - [&](const SystemMemInfo &info) { return static_cast(info.used_mem); }); - } - return Status::OK(); -} - -Status CpuSampler::GetSystemMemoryInfo(SystemMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) { - return (sys_info_.GetSystemMemInfo(metric, start_index, end_index, result)); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h b/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h deleted file mode 100644 index 21f533885..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_CPU_SAMPLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_CPU_SAMPLER_H_ - -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/dataset_op.h" - -namespace mindspore { -namespace dataset { - -class ExecutionTree; - -typedef struct SystemStat_s { - uint64_t user_stat; - uint64_t sys_stat; - uint64_t io_stat; - uint64_t idle_stat; - uint64_t total_stat; -} SystemStat; - -typedef struct SystemUtil_s { - uint8_t user_utilization; - uint8_t sys_utilization; - uint8_t io_utilization; - uint8_t idle_utilization; -} SystemUtil; - -typedef struct TaskStat_s { - uint64_t user_stat; - uint64_t sys_stat; -} TaskStat; - -struct TaskUtil_s { - float user_utilization; - float sys_utilization; -}; - -typedef struct MemoryInfo_s { - float vss; - float rss; - float pss; -} MemoryInfo; - -typedef struct SystemMemInfo_s { - float total_mem; - float available_mem; - float used_mem; -} SystemMemInfo; - -typedef struct TaskUtil_s TaskUtil; -typedef struct TaskUtil_s OpUtil; - -class SystemInfo { - public: - SystemInfo() - : first_sample_(true), - prev_context_switch_count_(0), - last_mem_sampling_failed_(false), - prev_system_memory_info_({0, 0, 0}) {} - // Read in current stats and return previous and currently read stats - Status SampleAndGetCurrPrevStat(SystemStat *current_stat, SystemStat *previous_stat); - static int32_t num_cpu_; - const std::vector &GetRunningProcess() const { return running_process_; } - const std::vector &GetContextSwitchCount() const { return context_switch_count_; } - Status GetUserCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const; - Status GetSysCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const; - std::vector GetIOCpuUtil() const; - std::vector GetIdleCpuUtil() const; - Status GetSystemMemInfo(SystemMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) const; - - private: - Status ParseCpuInfo(const std::string &str); - Status ParseCtxt(const std::string &str); - Status ParseRunningProcess(const std::string &str); - Status SampleSystemMemInfo(); - SystemStat prev_sys_stat_{}; // last read data /proc/stat file - std::vector sys_cpu_util_; // vector of system cpu utilization - std::vector running_process_; // vector of running processes in system - std::vector context_switch_count_; // vector of number of context switches between two sampling points - bool first_sample_; // flag to indicate first time sampling - uint64_t prev_context_switch_count_; // last read context switch count from /proc/stat file - std::vector system_memory_info_; - SystemMemInfo prev_system_memory_info_; - bool last_mem_sampling_failed_; -}; - -class TaskCpuInfo { - public: - explicit TaskCpuInfo(pid_t pid) - : pid_(pid), prev_task_stat_(TaskStat{0, 0}), first_sample_(true), last_sampling_failed_(false) {} - virtual ~TaskCpuInfo() = default; - virtual Status Sample(uint64_t total_time_elapsed) = 0; - virtual pid_t GetId() = 0; - TaskUtil GetLatestCpuUtil() const; - std::vector GetSysCpuUtil() const; - std::vector GetUserCpuUtil() const; - - protected: - pid_t pid_; - TaskStat prev_task_stat_; - std::vector task_cpu_util_; - bool first_sample_; - bool last_sampling_failed_; -}; - -class ProcessInfo : public TaskCpuInfo { - public: - explicit ProcessInfo(pid_t pid, bool track_history = false) - : TaskCpuInfo(pid), - prev_memory_info_(MemoryInfo{0.0, 0.0, 0.0}), - last_mem_sampling_failed_(false), - track_sampled_history_(track_history) {} - ~ProcessInfo() override = default; - Status Sample(uint64_t total_time_elapsed) override; - pid_t GetId() override { return pid_; } - Status GetMemoryInfo(ProcessMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result) const; - void AddChildProcess(const std::shared_ptr &child_ptr); - - private: - Status SampleMemInfo(); - MemoryInfo GetLatestMemoryInfo() const; - MemoryInfo prev_memory_info_; - std::vector process_memory_info_; - std::vector> child_processes_; - bool IsParent(); - bool last_mem_sampling_failed_; - bool track_sampled_history_; -}; - -class ThreadCpuInfo : public TaskCpuInfo { - public: - explicit ThreadCpuInfo(pid_t pid, pid_t tid) : TaskCpuInfo(pid), tid_(tid) {} - ~ThreadCpuInfo() override = default; - Status Sample(uint64_t total_time_elapsed) override; - pid_t GetId() override { return tid_; } - - private: - pid_t tid_; -}; - -class MDOperatorCpuInfo { - public: - void AddTask(const std::shared_ptr &task_ptr); - bool TaskExists(pid_t id) const; - explicit MDOperatorCpuInfo(const int32_t op_id) : id_(op_id) {} - void CalculateOperatorUtilization(); - Status GetUserCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const; - Status GetSysCpuUtil(uint64_t start_index, uint64_t end_index, std::vector *result) const; - - private: - int32_t id_; - // tid is key for threadinfo, pid is key for processinfo - std::unordered_map> task_by_id_; - std::vector op_cpu_util_; -}; - -class CpuSampler : public Sampling { - using Timestamps = std::vector; - - public: - explicit CpuSampler(ExecutionTree *tree) : fetched_all_python_multiprocesses_(false), tree(tree) {} - ~CpuSampler() = default; - Status Sample() override; - Status Init() override; - Status ChangeFileMode(const std::string &dir_path, const std::string &rank_id) override; - Status SaveToFile(const std::string &dir_path, const std::string &rank_id) override; - std::string Name() const override { return kCpuSamplerName; } - Status GetSystemUserCpuUtil(uint64_t start_ts, uint64_t end_ts, std::vector *result); - Status GetSystemSysCpuUtil(uint64_t start_ts, uint64_t end_ts, std::vector *result); - Status GetOpUserCpuUtil(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result); - Status GetOpSysCpuUtil(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result); - Status GetProcessMemoryInfo(ProcessMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result); - Status GetSystemMemoryInfo(SystemMemoryMetric metric, uint64_t start_index, uint64_t end_index, - std::vector *result); - - // Clear all collected data - void Clear() override; - - private: - Status UpdateTaskList(); - bool fetched_all_python_multiprocesses_{}; - ExecutionTree *tree = nullptr; - pid_t main_pid_{}; - Timestamps ts_; - SystemInfo sys_info_; // stores the system cpu utilization - std::vector> tasks_; // vector of all process and thread tasks - std::shared_ptr main_thread_cpu_info_; - std::shared_ptr main_process_info_; - std::unordered_map op_info_by_id_; - Path GetFileName(const std::string &dir_path, const std::string &rank_id) override; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_CPU_SAMPLER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/perf/cyclic_array.h b/mindspore-lite/minddata/dataset/engine/perf/cyclic_array.h deleted file mode 100644 index 426316834..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/cyclic_array.h +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_CYCLIC_ARRAY_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_CYCLIC_ARRAY_H - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -/// \class CyclicArray "include/cyclic_array.h -/// \brief This is a container with a contiguous memory layout that pnly keeps N last entries, -/// when the number of entries exceeds the capacity -/// Must be preallocated -template -class CyclicArray { - public: - using value_type = T; - class Iterator { - // Add operator[] and make fully compliant with random access iterator - // and add a const iterator - // add resize(), empty() - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = CyclicArray::value_type; - using difference_type = std::ptrdiff_t; - using pointer = CyclicArray::value_type *; - using reference = CyclicArray::value_type &; - - Iterator() = default; - - Iterator(dsize_t idx, pointer ptr, dsize_t capacity, dsize_t head) - : cur_idx_(idx), ptr_(ptr), capacity_(capacity), head_(head) {} - - Iterator(const Iterator &rhs) = default; - - ~Iterator() = default; - - Iterator &operator++() { - cur_idx_ = (cur_idx_ + 1) % (capacity_ + 1); - return *this; - } - - Iterator operator++(int) { - Iterator tmp(*this); - cur_idx_ = (cur_idx_ + 1) % (capacity_ + 1); - return tmp; - } - - Iterator &operator--() { - cur_idx_ = (cur_idx_ + capacity_) % (capacity_ + 1); - return *this; - } - - Iterator operator--(int) { - Iterator tmp(*this); - cur_idx_ = (cur_idx_ + capacity_) % (capacity_ + 1); - return tmp; - } - - Iterator operator+(dsize_t x) { return Iterator((cur_idx_ + x) % (capacity_ + 1), ptr_, capacity_, head_); } - - Iterator operator-(dsize_t x) { - return Iterator((cur_idx_ + (capacity_ + 1 - x)) % (capacity_ + 1), ptr_, capacity_, head_); - } - - bool operator<(const Iterator &rhs) { - return (head_ + cur_idx_) % (capacity_ + 1) < (rhs.head_ + rhs.cur_idx_) % (capacity_ + 1); - } - - bool operator>(const Iterator &rhs) { - return (head_ + cur_idx_) % (capacity_ + 1) > (rhs.head_ + rhs.cur_idx_) % (capacity_ + 1); - } - - bool operator>=(const Iterator &rhs) { - return (head_ + cur_idx_) % (capacity_ + 1) >= (rhs.head_ + rhs.cur_idx_) % (capacity_ + 1); - } - - bool operator<=(const Iterator &rhs) { - return (head_ + cur_idx_) % (capacity_ + 1) <= (rhs.head_ + rhs.cur_idx_) % (capacity_ + 1); - } - - difference_type operator-(const Iterator &rhs) { - return (cur_idx_ - rhs.cur_idx_ + capacity_ + 1) % (capacity_ + 1); - } - - reference operator*() { return ptr_[cur_idx_]; } - - pointer operator->() { return &(ptr_[cur_idx_]); } - - bool operator==(const Iterator &rhs) { return cur_idx_ == rhs.cur_idx_; } - - bool operator!=(const Iterator &rhs) { return cur_idx_ != rhs.cur_idx_; } - - private: - dsize_t cur_idx_; - pointer ptr_; - dsize_t capacity_; - dsize_t head_; - }; - - /// \brief Default constructor - CyclicArray() : buf_(nullptr), head_(0), tail_(0), size_(0), capacity_(0) {} - - /// \brief Constructor - /// \param[in] capacity - explicit CyclicArray(dsize_t capacity) - : buf_(std::make_unique(capacity + 1)), head_(0), tail_(0), size_(0), capacity_(capacity) {} - - CyclicArray(const CyclicArray &rhs) - : buf_(std::make_unique(rhs.capacity_ + 1)), - head_(rhs.head_), - tail_(rhs.tail_), - size_(rhs.size_), - capacity_(rhs.capacity_) { - (void)std::copy(rhs.begin(), rhs.end(), begin()); - } - - CyclicArray(CyclicArray &&rhs) = default; - - ~CyclicArray() = default; - - /// \brief Iterator begin() - Iterator begin() { return Iterator(head_, buf_.get(), capacity_, head_); } - - /// \brief Iterator end() - Iterator end() { return Iterator(tail_, buf_.get(), capacity_, head_); } - - // not really const. - Iterator begin() const { return Iterator(head_, buf_.get(), capacity_, head_); } - - Iterator end() const { return Iterator(tail_, buf_.get(), capacity_, head_); } - - /// \brief clear the array. Does not deallocate memory, capacity remains the same - void clear() { - head_ = 0; - tail_ = 0; - size_ = 0; - } - - /// \brief returns current size - dsize_t size() { return size_; } - - /// \brief returns capacity - dsize_t capacity() { return capacity_; } - - /// \brief pushes a value - /// \param[in] val value - void push_back(const T &val) { - buf_[tail_] = val; - if (size_ >= capacity_) { - (tail_ != capacity_) ? tail_++ : (tail_ = 0); - (head_ != capacity_) ? head_++ : (head_ = 0); - } else { - tail_++; - size_++; - } - } - - /// \brief returns const reference to an element of the array - /// \param[in] idx index of the element - /// \param[out] const T& reference to an element of the array - const T &operator[](dsize_t idx) const { return buf_[(head_ + idx) % (capacity_ + 1)]; } - - /// \brief returns non-const reference to an element of the array - /// \param[in] idx index of the element - /// \param[out] T& reference to an element of the array - T &operator[](dsize_t idx) { return buf_[(head_ + idx) % (capacity_ + 1)]; } - - private: - std::unique_ptr buf_; - dsize_t head_; - dsize_t tail_; - dsize_t size_; - dsize_t capacity_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_CYCLIC_ARRAY_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.cc b/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.cc deleted file mode 100644 index 52e9e9669..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.cc +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h" - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" - -namespace mindspore { -namespace dataset { -Path DatasetIteratorTracing::GetFileName(const std::string &dir_path, const std::string &rank_id) { - return Path(dir_path) / Path("dataset_iterator_profiling_" + rank_id + ".txt"); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h b/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h deleted file mode 100644 index 5e179fc65..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_DATASET_ITERATOR_TRACING_H -#define MINDSPORE_DATASET_ITERATOR_TRACING_H - -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" - -namespace mindspore { -namespace dataset { -class DatasetIteratorTracing : public Tracing { - public: - // Constructor - DatasetIteratorTracing() = default; - - // Destructor - ~DatasetIteratorTracing() override = default; - - std::string Name() const override { return kDatasetIteratorTracingName; }; - - protected: - Path GetFileName(const std::string &dir_path, const std::string &rank_id) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_DATASET_ITERATOR_TRACING_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.cc b/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.cc deleted file mode 100644 index 3e2ea183f..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h" - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" - -namespace mindspore { -namespace dataset { -Path DeviceQueueTracing::GetFileName(const std::string &dir_path, const std::string &rank_id) { - return Path(dir_path) / Path("device_queue_profiling_" + rank_id + ".txt"); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h b/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h deleted file mode 100644 index fb54c1ac6..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/device_queue_tracing.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_DEVICE_QUEUE_TRACING_H -#define MINDSPORE_DEVICE_QUEUE_TRACING_H - -#include -#include -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/util/path.h" - -namespace mindspore { -namespace dataset { -class DeviceQueueTracing : public Tracing { - public: - // Constructor - DeviceQueueTracing() = default; - - // Destructor - ~DeviceQueueTracing() override = default; - - std::string Name() const override { return kDeviceQueueTracingName; }; - - protected: - Path GetFileName(const std::string &dir_path, const std::string &rank_id) override; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_DEVICE_QUEUE_TRACING_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/info_collector.cc b/mindspore-lite/minddata/dataset/engine/perf/info_collector.cc deleted file mode 100644 index 3809c3b93..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/info_collector.cc +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#include "mindspore/mindspore/ccsrc/tools/profiler/profiling.h" - -namespace mindspore::dataset { - -uint64_t GetSyscnt() { - uint64_t time_cnt = 0; -#if !defined(ENABLE_ANDROID) - time_cnt = profiler::GetClockSyscnt(); -#endif - return time_cnt; -} - -double GetMilliTimeStamp() { - auto now = std::chrono::high_resolution_clock::now(); - int64_t us = std::chrono::duration_cast(now.time_since_epoch()).count(); - return static_cast(us) / 1000.; -} - -Status CollectPipelineInfo(const std::string &event, const std::string &stage, const uint64_t &start_time, - const std::map &custom_info) { -#if !defined(ENABLE_ANDROID) - (void)profiler::CollectHostInfo("Dataset", event, stage, start_time, profiler::GetClockSyscnt(), InfoLevel::kUser, - custom_info); -#endif - return Status::OK(); -} - -Status CollectOpInfo(const std::string &event, const std::string &stage, const uint64_t &start_time, - const std::map &custom_info) { -#if !defined(ENABLE_ANDROID) - (void)profiler::CollectHostInfo("Dataset", event, stage, start_time, profiler::GetClockSyscnt(), - InfoLevel::kDeveloper, custom_info); -#endif - return Status::OK(); -} -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/perf/info_collector.h b/mindspore-lite/minddata/dataset/engine/perf/info_collector.h deleted file mode 100644 index f814b2ce3..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/info_collector.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_INFO_COLLECTOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_INFO_COLLECTOR_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore::dataset { -enum InfoLevel : uint8_t { kDeveloper = 0, kUser = 1 }; -enum InfoType : uint8_t { kAll = 0, kMemory = 1, kTime = 2 }; -enum TimeType : uint8_t { kStart = 0, kEnd = 1, kStamp = 2 }; - -double GetMilliTimeStamp(); - -uint64_t GetSyscnt(); - -Status CollectPipelineInfo(const std::string &event, const std::string &stage, const uint64_t &start_time, - const std::map &custom_info = {}); - -Status CollectOpInfo(const std::string &event, const std::string &stage, const uint64_t &start_time, - const std::map &custom_info = {}); -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_INFO_COLLECTOR_H_ diff --git a/mindspore-lite/minddata/dataset/engine/perf/monitor.cc b/mindspore-lite/minddata/dataset/engine/perf/monitor.cc deleted file mode 100644 index 1722630c4..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/monitor.cc +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/perf/monitor.h" -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" - -namespace mindspore { -namespace dataset { - -Monitor::Monitor(ProfilingManager *profiling_manager) : Monitor(profiling_manager, GlobalContext::config_manager()) {} - -Monitor::Monitor(ProfilingManager *profiling_manager, const std::shared_ptr &cfg) - : profiling_manager_(profiling_manager), sampling_interval_(cfg->monitor_sampling_interval()) { - if (profiling_manager_ != nullptr) { - tree_ = profiling_manager_->tree_; - } -} - -Monitor::~Monitor() { - // just set the pointer to nullptr, it's not be released here - if (profiling_manager_) { - profiling_manager_ = nullptr; - } - - if (tree_) { - tree_ = nullptr; - } -} - -Status Monitor::operator()() { - // Register this thread with TaskManager to receive proper interrupt signal. - TaskManager::FindMe()->Post(); - std::unique_lock _lock(mux_); - - // Keep sampling if - // 1) Monitor Task is not interrupted by TaskManager AND - // 2) Iterator has not received EOF - - while (!this_thread::is_interrupted() && !(tree_->isFinished())) { - if (tree_->IsEpochEnd()) { - tree_->SetExecuting(); - } - for (auto &node : profiling_manager_->GetSamplingNodes()) { - RETURN_IF_NOT_OK(node.second->Sample()); - } - RETURN_IF_NOT_OK(cv_.WaitFor(&_lock, sampling_interval_)); - } - MS_LOG(INFO) << "Monitor Thread terminating..."; - return Status::OK(); -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/monitor.h b/mindspore-lite/minddata/dataset/engine/perf/monitor.h deleted file mode 100644 index 95022d892..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/monitor.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_MONITOR_H -#define MINDSPORE_MONITOR_H - -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" -#include "mindspore-lite/minddata/dataset/util/cond_var.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ExecutionTree; -class ProfilingManager; -class ConfigManager; - -class Monitor { - public: - // Monitor object constructor - explicit Monitor(ProfilingManager *profiling_manager); - - ~Monitor(); - - // Functor for Perf Monitor main loop. - // This function will be the entry point of mindspore::Dataset::Task - Status operator()(); - - // Setter for execution tree pointer - void SetTree(ExecutionTree *tree) { tree_ = tree; } - - private: - // private constructor - Monitor(ProfilingManager *profiling_manager, const std::shared_ptr &cfg); - - ProfilingManager *profiling_manager_; - int64_t sampling_interval_; - ExecutionTree *tree_; - std::mutex mux_; - CondVar cv_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_MONITOR_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/perf_data.h b/mindspore-lite/minddata/dataset/engine/perf/perf_data.h deleted file mode 100644 index 480ee7a16..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/perf_data.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_PERF_DATA_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_PERF_DATA_H - -#include -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { - -// PerfData is a convenience class to record and store the data produced by Monitor -// and represents a 2D column major table with every column storing samples -// for an operator. The number of rows equals to the number of samples, -// the number of columns equals to the number of operators. -// The capacity is determined on construction and cannot be changed. -// ColumnType can be std::vector or CyclicArray. In case of the latter data can be added -// indefinitely without the risk of overflowing otherwise the capacity must not be exceeded. -// Given PerfData pd(n_rows, n_cols) an element in the column i and row j can be accessed as -// pd[i][j] - -template -class PerfData { - public: - PerfData() = default; - ~PerfData() = default; - PerfData(dsize_t max_rows, dsize_t n_cols) : counter_(0), max_rows_(max_rows), n_cols_(n_cols) { - for (auto i = 0; i < n_cols_; i++) { - data_.push_back(ColumnType(max_rows_)); - } - } - PerfData(const PerfData &rhs) = default; - PerfData(PerfData &&rhs) = default; - - // Adds a row of data - // T must be any container working with range based loops - template - void AddSample(const T &row) { - size_t i = 0; - for (const auto &e : row) { - if (data_.size() > i) { - data_[i++].push_back(e); - } - } - counter_++; - } - - // Fetches a row of data by copy - template - auto Row(dsize_t idx) { - std::vector row(n_cols_); - for (auto i = 0; i < n_cols_; i++) { - if (data_.size() > i && data_[i].size() > idx) { - row[i] = data_[i][idx]; - } - } - return row; - } - - // returns a column of data - ColumnType &operator[](size_t idx) { return data_[idx]; } - - const ColumnType &operator[](size_t idx) const { return data_[idx]; } - - dsize_t size() { return counter_ < max_rows_ ? counter_ : max_rows_; } - - dsize_t capacity() { return max_rows_; } - - private: - std::vector data_; - dsize_t counter_; - dsize_t max_rows_; - int n_cols_; -}; - -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_PERF_DATA_H diff --git a/mindspore-lite/minddata/dataset/engine/perf/profiling.cc b/mindspore-lite/minddata/dataset/engine/perf/profiling.cc deleted file mode 100644 index b82d1e0a2..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/profiling.cc +++ /dev/null @@ -1,876 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/perf/profiling.h" - -#include - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/perf/connector_size.h" -#include "mindspore-lite/minddata/dataset/engine/perf/cpu_sampler.h" -#include "mindspore-lite/minddata/dataset/engine/perf/monitor.h" -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#ifdef WITH_BACKEND -#include "utils/ms_context.h" -#endif -#include "utils/ms_utils.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -namespace platform = mindspore; -#else -#include "mindspore-lite/src/common/file_utils.h" -namespace platform = mindspore::lite; -#endif - -namespace mindspore { -namespace dataset { -constexpr int32_t PUSH_TIME_OFFSET = 0; -constexpr int32_t BATCH_TIME_OFFSET = 1; -constexpr int32_t PIPELINE_TIME_OFFSET = 2; -constexpr int32_t CONNECTOR_DEPTH_OFFSET = 3; - -Status Profiling::Start() { - CHECK_FAIL_RETURN_UNEXPECTED(active_ == false, "Profiling node is already active."); - active_ = true; - return Status::OK(); -} - -Status Profiling::Stop() { - CHECK_FAIL_RETURN_UNEXPECTED(active_ == true, "Profiling node is already deactivated."); - active_ = false; - return Status::OK(); -} - -Status Tracing::SaveToFile(const std::string &dir_path, const std::string &rank_id) { - if (value_.empty()) { - return Status::OK(); - } - - Path path = GetFileName(dir_path, rank_id); - // Remove the file if it exists (from prior profiling usage) - RETURN_IF_NOT_OK(path.Remove()); - std::string file_path = path.ToString(); - - MS_LOG(INFO) << "Start to save profiling data for a tracing node."; - std::ofstream handle(file_path, std::ios::out | std::ios::trunc); - if (!handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Profiling file can not be opened."); - } - for (const auto &value : value_) { - handle << value << "\n"; - } - handle.close(); - - platform::ChangeFileMode(file_path, S_IRUSR | S_IWUSR); - - return Status::OK(); -} - -Status Tracing::ChangeFileMode(const std::string &dir_path, const std::string &rank_id) { - if (value_.empty()) { - return Status::OK(); - } - - Path path = GetFileName(dir_path, rank_id); - std::string file_path = path.ToString(); - if (chmod(common::SafeCStr(file_path), S_IRUSR | S_IWUSR) == -1) { - std::string err_str = "Change file mode failed," + file_path; - return Status(StatusCode::kMDUnexpectedError, err_str); - } - return Status::OK(); -} - -void Tracing::Record(const int32_t type, const int32_t extra_info, const int32_t batch_num, const int32_t value, - const uint64_t time_stamp) { - // Format: "type extra-info batch-num value" - // type: 0: time, 1: connector size - // extra-info: if type is 0 - 0: pipeline time, 1: push tdt time, 2: batch time - // if type is 1 - connector capacity - // batch-num: batch number - // value: if type is 0 - value is time(ms) - // if type is 1 - value is connector size - // time-stamp: time stamp - // Examples: - // 0 0 20 10 xxx- The 20th batch took 10ms to get data from pipeline. - // 1 64 20 5 xxx- Connector size is 5 when get the 20th batch.Connector capacity is 64. - if (!active_) { - return; - } - TracingRecord record = {type, extra_info, batch_num, value, time_stamp}; - std::lock_guard guard(lock_); - (void)records_.emplace_back(record); - (void)value_.emplace_back(record.ToString()); - // save timestamp per batch - const constexpr int32_t RECORDS_PER_STEP = 4; - if (records_.size() % RECORDS_PER_STEP == 0) { - (void)ts_.emplace_back(time_stamp); - } -} - -Status Tracing::TimeIntervalForStepRange(int32_t start_step, int32_t end_step, uint64_t *start_ts, uint64_t *end_ts) { - RETURN_UNEXPECTED_IF_NULL(start_ts); - RETURN_UNEXPECTED_IF_NULL(end_ts); - std::lock_guard guard(lock_); - MS_LOG(DEBUG) << "start_step: " << start_step << " end_step: " << end_step; - CHECK_FAIL_RETURN_UNEXPECTED(start_step > 0, - "Expected start_step > 0. Got start_step: " + std::to_string(start_step)); - CHECK_FAIL_RETURN_UNEXPECTED(end_step >= start_step, - "Expected end_step >= start_step. Got start_step: " + std::to_string(start_step) + - " end_step: " + std::to_string(end_step)); - CHECK_FAIL_RETURN_UNEXPECTED(end_step < static_cast(ts_.size()), - "Expected end_step < ts_.size(). Got end_step: " + std::to_string(end_step) + - " ts_.size: " + std::to_string(ts_.size())); - // end timestamp of (start_step - 1) step - *start_ts = ts_[start_step - 1]; - *end_ts = ts_[end_step]; - return Status::OK(); -} - -Status Tracing::StepIntervalForTimeRange(uint64_t start_ts, uint64_t end_ts, int32_t *start_step, int32_t *end_step) { - RETURN_UNEXPECTED_IF_NULL(start_step); - RETURN_UNEXPECTED_IF_NULL(end_step); - CHECK_FAIL_RETURN_UNEXPECTED(start_ts < end_ts, "Expected start_ts < end_ts. Got start_ts: " + - std::to_string(start_ts) + " end_ts: " + std::to_string(end_ts)); - std::lock_guard guard(lock_); - CHECK_FAIL_RETURN_UNEXPECTED(ts_.size() > 1, "No tracing data available yet."); - // find first ts that is not less than start_ts - auto lower = std::lower_bound(ts_.begin(), ts_.end(), start_ts); - CHECK_FAIL_RETURN_UNEXPECTED(lower != ts_.end(), - "No data available for time >= start_ts. start_ts: " + std::to_string(start_ts)); - // there is no 0th step. If start_ts == 0, then lower == ts_.begin() - *start_step = std::max(1, static_cast(std::distance(ts_.begin(), lower))); - // find first ts that is greater than end_ts - auto upper = std::upper_bound(ts_.begin(), ts_.end(), end_ts); - if (upper == ts_.end()) { - *end_step = std::max(1, static_cast(std::distance(ts_.begin(), upper) - 1)); - } else { - *end_step = std::max(1, static_cast(std::distance(ts_.begin(), upper))); - } - return Status::OK(); -} - -Status Tracing::GetRecordEntryFieldValue(int32_t start_step, int32_t end_step, int32_t record_offset, - const std::string &field, std::vector *result) { - RETURN_UNEXPECTED_IF_NULL(result); - std::lock_guard guard(lock_); - const constexpr int32_t RECORDS_PER_STEP = 4; - auto total_steps = records_.size() / RECORDS_PER_STEP; - MS_LOG(DEBUG) << "start_step: " << start_step << " end_step: " << end_step; - CHECK_FAIL_RETURN_UNEXPECTED(start_step <= static_cast(total_steps), - "Expected start_step <= total_steps. Got start_step: " + std::to_string(start_step) + - " total_steps: " + std::to_string(total_steps)); - CHECK_FAIL_RETURN_UNEXPECTED(end_step <= static_cast(total_steps), - "Expected end_step <= total_steps. Got end_step: " + std::to_string(end_step) + - " total_steps: " + std::to_string(total_steps)); - CHECK_FAIL_RETURN_UNEXPECTED(start_step <= end_step, - "Expected start_step <= end_step. Got start_step: " + std::to_string(start_step) + - " end_step: " + std::to_string(end_step)); - - for (auto step_num = start_step; step_num <= end_step; step_num++) { - auto idx = (step_num - 1) * RECORDS_PER_STEP + record_offset; - CHECK_FAIL_RETURN_UNEXPECTED(idx >= 0, "Expected idx >= 0. Got idx: " + std::to_string(idx)); - if (field == "value") { - (void)result->emplace_back(records_[static_cast(idx)].value); - } else if (field == "extra_info") { - (void)result->emplace_back(records_[static_cast(idx)].extra_info); - } else { - return {StatusCode::kMDUnexpectedError, - "Received unexpected field: " + field + R"(. Expected: ["value", "extra_info"].)"}; - } - } - return Status::OK(); -} - -Status Tracing::GetPipelineTime(int32_t start_step, int32_t end_step, std::vector *result) { - return GetRecordEntryFieldValue(start_step, end_step, PIPELINE_TIME_OFFSET, "value", result); -} - -Status Tracing::GetPushTime(int32_t start_step, int32_t end_step, std::vector *result) { - return GetRecordEntryFieldValue(start_step, end_step, PUSH_TIME_OFFSET, "value", result); -} - -Status Tracing::GetBatchTime(int32_t start_step, int32_t end_step, std::vector *result) { - return GetRecordEntryFieldValue(start_step, end_step, BATCH_TIME_OFFSET, "value", result); -} - -Status Tracing::GetConnectorSize(int32_t start_step, int32_t end_step, std::vector *result) { - return GetRecordEntryFieldValue(start_step, end_step, CONNECTOR_DEPTH_OFFSET, "value", result); -} - -Status Tracing::GetConnectorCapacity(int32_t start_step, int32_t end_step, std::vector *result) { - return GetRecordEntryFieldValue(start_step, end_step, CONNECTOR_DEPTH_OFFSET, "extra_info", result); -} - -Status Tracing::GetEmptyQueueFrequency(int32_t start_step, int32_t end_step, float_t *empty_queue_freq) { - RETURN_UNEXPECTED_IF_NULL(empty_queue_freq); - std::vector sizes; - RETURN_IF_NOT_OK(GetConnectorSize(start_step, end_step, &sizes)); - int32_t total = end_step - start_step + 1; - CHECK_FAIL_RETURN_UNEXPECTED(total > 0, "Start step is greater than end step."); - uint32_t count = static_cast(std::count(sizes.begin(), sizes.end(), 0)); - *empty_queue_freq = static_cast(count) / static_cast(total); - return Status::OK(); -} - -Status Tracing::Init() { - (void)ts_.emplace_back(0); - return Status::OK(); -} - -size_t Tracing::GetNumberSteps() { return ts_.size(); } - -void Tracing::Clear() { - value_.clear(); - records_.clear(); - ts_.clear(); -} - -// Constructor -ProfilingManager::ProfilingManager() - : profiling_state_(ProfilingState::kProfilingStateUnBegun), tree_(nullptr), autotuning_(false), profiling_(false) {} - -bool ProfilingManager::IsProfilingEnable(const ExecutionTree *tree) const { - auto external_state = GetProfilerTreeState(tree); - return (external_state == kEnabledTreeNotRegistered || external_state == kEnabledTreeRegistered); -} - -Status ProfilingManager::RegisterTree(ExecutionTree *tree) { - CHECK_FAIL_RETURN_UNEXPECTED(tree_ == nullptr, "Another tree is already registered."); - CHECK_FAIL_RETURN_UNEXPECTED((autotuning_ || profiling_) == true, - "MD Profiler is disabled. Cannot register the tree."); - tree_ = tree; - MS_LOG(INFO) << "Registering tree: " + tree_->GetUniqueId(); - perf_monitor_ = std::make_unique(this); - // Register all sampling nodes here. - // Tracing node registration is the responsibility of the Consumer - std::shared_ptr connector_size_sampling = std::make_shared(tree_); - RETURN_IF_NOT_OK(RegisterSamplingNode(connector_size_sampling)); - -#ifndef ENABLE_ANDROID - std::shared_ptr cpu_sampler = std::make_shared(tree_); - RETURN_IF_NOT_OK(RegisterSamplingNode(cpu_sampler)); -#endif - // can insert a correct timestamp so that we can ignore the samples that were taken - // during start up of the pipeline. - (void)epoch_end_ts_.emplace_back(0); - (void)epoch_end_step_.emplace_back(0); - return Status::OK(); -} - -// Launch monitoring thread. -Status ProfilingManager::LaunchMonitor() { - RETURN_IF_NOT_OK(tree_->AllTasks()->CreateAsyncTask("Monitor Thread launched", std::ref(*perf_monitor_))); - return Status::OK(); -} - -// Profiling node registration -Status ProfilingManager::RegisterTracingNode(const std::shared_ptr &node) { - // Check if node with the same name has already been registered. - auto exist = tracing_nodes_.find(node->Name()); - if (exist != tracing_nodes_.end()) { - return Status(StatusCode::kMDProfilingError, "Profiling node already exist: " + node->Name()); - } - // Register the node with its name as key. - RETURN_IF_NOT_OK(node->Init()); - tracing_nodes_[node->Name()] = node; - - // the user may have already started profiling. - if (profiling_state_ == ProfilingState::kProfilingStateRunning) { - RETURN_IF_NOT_OK(node->Start()); - } - return Status::OK(); -} - -// Profiling node getter -Status ProfilingManager::GetTracingNode(const std::string &name, std::shared_ptr *node) { - // Check if node with the same name has already been registered. - auto exist = tracing_nodes_.find(name); - if (exist == tracing_nodes_.end()) { - return Status(StatusCode::kMDProfilingError, "Profiling node does not exist: " + name); - } - // Fetch node. - *node = tracing_nodes_[name]; - return Status::OK(); -} - -// Profiling node registration -Status ProfilingManager::RegisterSamplingNode(const std::shared_ptr &node) { - // Check if node with the same name has already been registered. - auto exist = sampling_nodes_.find(node->Name()); - if (exist != sampling_nodes_.end()) { - return Status(StatusCode::kMDProfilingError, "Profiling node already exist: " + node->Name()); - } - // Register the node with its name as key. - RETURN_IF_NOT_OK(node->Init()); - sampling_nodes_[node->Name()] = node; - - // the user may have already started profiling. - if (profiling_state_ == ProfilingState::kProfilingStateRunning) { - RETURN_IF_NOT_OK(node->Start()); - } - return Status::OK(); -} - -// Profiling node getter -Status ProfilingManager::GetSamplingNode(const std::string &name, std::shared_ptr *node) { - // Check if node with the same name has already been registered. - auto exist = sampling_nodes_.find(name); - if (exist == sampling_nodes_.end()) { - return Status(StatusCode::kMDProfilingError, "Profiling node does not exist: " + name); - } - // Fetch node. - *node = sampling_nodes_[name]; - return Status::OK(); -} - -Status ProfilingManager::SaveProfilingData(const std::string &dir_path, const std::string &rank_id) { - MS_LOG(INFO) << "Start to save profiling data."; - for (const auto &node : tracing_nodes_) { - RETURN_IF_NOT_OK(node.second->SaveToFile(dir_path, rank_id)); - } - for (const auto &node : sampling_nodes_) { - RETURN_IF_NOT_OK(node.second->SaveToFile(dir_path, rank_id)); - } - MS_LOG(INFO) << "Save profiling data end."; - return Status::OK(); -} - -Status ProfilingManager::ChangeFileMode(const std::string &dir_path, const std::string &rank_id) { - MS_LOG(INFO) << "Start to change file mode."; - for (const auto &node : tracing_nodes_) { - RETURN_IF_NOT_OK(node.second->ChangeFileMode(dir_path, rank_id)); - } - for (const auto &node : sampling_nodes_) { - RETURN_IF_NOT_OK(node.second->ChangeFileMode(dir_path, rank_id)); - } - MS_LOG(INFO) << "Change file mode end."; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ProfilingManager::GetUserCpuUtilByEpoch(int32_t epoch_num, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetUserCpuUtilByTime(start_ts, end_ts, result); -} - -Status ProfilingManager::GetUserCpuUtilByStep(int32_t start_step, int32_t end_step, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetUserCpuUtilByTime(start_ts, end_ts, result); -} - -Status ProfilingManager::GetUserCpuUtilByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetSystemUserCpuUtil(start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByEpoch(int32_t epoch_num, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetSysCpuUtilByTime(start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByStep(int32_t start_step, int32_t end_step, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetSysCpuUtilByTime(start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetSystemSysCpuUtil(start_ts, end_ts, result); -} - -Status ProfilingManager::GetUserCpuUtilByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetUserCpuUtilByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetUserCpuUtilByStep(int32_t op_id, int32_t start_step, int32_t end_step, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetUserCpuUtilByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetUserCpuUtilByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, - std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetOpUserCpuUtil(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetSysCpuUtilByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByStep(int32_t op_id, int32_t start_step, int32_t end_step, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetSysCpuUtilByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSysCpuUtilByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, - std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetOpSysCpuUtil(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetMainProcessMemoryInfoByEpoch(ProcessMemoryMetric metric, int32_t epoch_num, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetMainProcessMemoryInfoByTime(metric, start_ts, end_ts, result); -} - -Status ProfilingManager::GetMainProcessMemoryInfoByStep(ProcessMemoryMetric metric, int32_t start_step, - int32_t end_step, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetMainProcessMemoryInfoByTime(metric, start_ts, end_ts, result); -} - -Status ProfilingManager::GetMainProcessMemoryInfoByTime(ProcessMemoryMetric metric, uint64_t start_ts, uint64_t end_ts, - std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetProcessMemoryInfo(metric, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSystemMemoryInfoByEpoch(SystemMemoryMetric metric, int32_t epoch_num, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetSystemMemoryInfoByTime(metric, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSystemMemoryInfoByStep(SystemMemoryMetric metric, int32_t start_step, int32_t end_step, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetSystemMemoryInfoByTime(metric, start_ts, end_ts, result); -} - -Status ProfilingManager::GetSystemMemoryInfoByTime(SystemMemoryMetric metric, uint64_t start_ts, uint64_t end_ts, - std::vector *result) { - std::shared_ptr sampling_node; - RETURN_IF_NOT_OK(GetSamplingNode(kCpuSamplerName, &sampling_node)); - auto node = std::dynamic_pointer_cast(sampling_node); - return node->GetSystemMemoryInfo(metric, start_ts, end_ts, result); -} -#endif - -Status ProfilingManager::EpochToTimeInterval(int32_t epoch_num, uint64_t *start_ts, uint64_t *end_ts) { - RETURN_UNEXPECTED_IF_NULL(start_ts); - RETURN_UNEXPECTED_IF_NULL(end_ts); - if (epoch_num <= 0 || epoch_num >= static_cast(epoch_end_ts_.size())) { - std::string err = "Epoch: " + std::to_string(epoch_num) + " is invalid."; - MS_LOG(INFO) << err; - return {StatusCode::kMDUnexpectedError, err}; - } - *start_ts = epoch_end_ts_[epoch_num - 1]; - *end_ts = epoch_end_ts_[epoch_num]; - return Status::OK(); -} - -Status ProfilingManager::EpochToStepInterval(int32_t epoch_num, uint32_t *start_step, uint32_t *end_step) { - RETURN_UNEXPECTED_IF_NULL(start_step); - RETURN_UNEXPECTED_IF_NULL(end_step); - if (epoch_num <= 0 || epoch_num >= static_cast(epoch_end_step_.size())) { - std::string err = "Epoch: " + std::to_string(epoch_num) + " is invalid."; - return {StatusCode::kMDUnexpectedError, err}; - } - *start_step = epoch_end_step_[epoch_num - 1] + 1; - *end_step = epoch_end_step_[epoch_num]; - return Status::OK(); -} - -Status ProfilingManager::StepToTimeInterval(int32_t start_step, int32_t end_step, uint64_t *start_ts, - uint64_t *end_ts) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->TimeIntervalForStepRange(start_step, end_step, start_ts, end_ts); - } else { - return {StatusCode::kMDUnexpectedError, - "Cannot find appropriate tracing node to convert step range to time interval."}; - } -} - -Status ProfilingManager::TimeToStepInterval(uint64_t start_ts, uint64_t end_ts, int32_t *start_step, - int32_t *end_step) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->StepIntervalForTimeRange(start_ts, end_ts, start_step, end_step); - } else { - return {StatusCode::kMDUnexpectedError, - "Cannot find appropriate tracing node to convert time interval to step range."}; - } -} - -Status ProfilingManager::GetConnectorSizeByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(EpochToTimeInterval(epoch_num, &start_ts, &end_ts)); - return GetConnectorSizeByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetConnectorSizeByStep(int32_t op_id, int32_t start_step, int32_t end_step, - std::vector *result) { - uint64_t start_ts = 0, end_ts = 0; - RETURN_IF_NOT_OK(StepToTimeInterval(start_step, end_step, &start_ts, &end_ts)); - return GetConnectorSizeByTime(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetConnectorSizeByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, - std::vector *result) { - std::shared_ptr node; - RETURN_IF_NOT_OK(GetSamplingNode(kConnectorSizeSamplingName, &node)); - auto connector_node = std::dynamic_pointer_cast(node); - return connector_node->GetOpConnectorSize(op_id, start_ts, end_ts, result); -} - -Status ProfilingManager::GetPipelineTimeByEpoch(int32_t epoch_num, std::vector *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetPipelineTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetPipelineTimeByStep(int32_t start_step, int32_t end_step, std::vector *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetPipelineTime(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetPipelineTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetPipelineTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetPushTimeByEpoch(int32_t epoch_num, std::vector *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetPushTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetPushTimeByStep(int32_t start_step, int32_t end_step, std::vector *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetPushTime(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetPushTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetPushTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetBatchTimeByEpoch(int32_t epoch_num, std::vector *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetBatchTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetBatchTimeByStep(int32_t start_step, int32_t end_step, std::vector *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetBatchTime(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetBatchTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetBatchTimeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetConnectorSizeByEpoch(int32_t epoch_num, std::vector *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetConnectorSizeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetConnectorSizeByStep(int32_t start_step, int32_t end_step, std::vector *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetConnectorSize(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetConnectorSizeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetConnectorSizeByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetEmptyQueueFrequencyByEpoch(int32_t epoch_num, float_t *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetEmptyQueueFrequencyByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetEmptyQueueFrequencyByStep(int32_t start_step, int32_t end_step, float_t *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetEmptyQueueFrequency(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetEmptyQueueFrequencyByTime(uint64_t start_ts, uint64_t end_ts, float_t *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetEmptyQueueFrequencyByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetConnectorCapacityByEpoch(int32_t epoch_num, std::vector *result) { - uint32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(EpochToStepInterval(epoch_num, &start_step, &end_step)); - return GetConnectorCapacityByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetConnectorCapacityByStep(int32_t start_step, int32_t end_step, - std::vector *result) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - return node->GetConnectorCapacity(start_step, end_step, result); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -Status ProfilingManager::GetConnectorCapacityByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result) { - int32_t start_step = 0, end_step = 0; - RETURN_IF_NOT_OK(TimeToStepInterval(start_ts, end_ts, &start_step, &end_step)); - return GetConnectorCapacityByStep(start_step, end_step, result); -} - -Status ProfilingManager::GetNumberOfProfiledSteps(int32_t *steps) { - std::shared_ptr node; - if (GetTracingNode(kDeviceQueueTracingName, &node).IsOk() || - GetTracingNode(kDatasetIteratorTracingName, &node).IsOk()) { - *steps = node->GetNumberSteps(); - return Status::OK(); - } else { - return {StatusCode::kMDUnexpectedError, "Cannot find appropriate tracing node"}; - } -} - -void ProfilingManager::RecordEndOfEpoch(uint32_t step_num) { - if (profiling_state_ != ProfilingState::kProfilingStateRunning) { - return; - } - MS_LOG(INFO) << "Recording end of epoch. step_num: " << step_num; - (void)epoch_end_ts_.emplace_back(ProfilingTime::GetCurMilliSecond()); - (void)epoch_end_step_.emplace_back(step_num); -} - -Status ProfilingManager::Reset() { - for (const auto &node : tracing_nodes_) { - node.second->Clear(); - } - for (const auto &node : sampling_nodes_) { - node.second->Clear(); - } - epoch_end_ts_.clear(); - epoch_end_step_.clear(); - profiling_state_ = ProfilingState::kProfilingStateUnBegun; - autotuning_ = false; - profiling_ = false; - return Status::OK(); -} - -Status ProfilingManager::Init(const bool for_autotune) { - // Reinitialization should only be done in case of UT with sequential pipelines and should not be used externally. - // Reinitialization with parallel data pipelines can have unexpected consequences. - CHECK_FAIL_RETURN_UNEXPECTED(!autotuning_, "Stop MD Autotune before initializing the MD Profiler."); - CHECK_FAIL_RETURN_UNEXPECTED(!profiling_, "Stop MD Profiler before initializing it."); - CHECK_FAIL_RETURN_UNEXPECTED(profiling_state_ != ProfilingState::kProfilingStateRunning, - "Stop MD Profiler before reinitializing it."); - RETURN_IF_NOT_OK(Reset()); - tracing_nodes_.clear(); - sampling_nodes_.clear(); - tree_ = nullptr; - CHECK_FAIL_RETURN_UNEXPECTED(profiling_state_ == ProfilingState::kProfilingStateUnBegun, - "MD Profiler is in an unexpected state."); - if (for_autotune) { - autotuning_ = true; - MS_LOG(INFO) << "MD profiler is initialized successfully for autotuning."; - } else { - profiling_ = true; - MS_LOG(INFO) << "MD profiler is initialized successfully for profiling."; - } - return Status::OK(); -} - -Status ProfilingManager::Start() { - CHECK_FAIL_RETURN_UNEXPECTED(profiling_state_ != ProfilingState::kProfilingStateRunning, - "MD ProfilingManager is already running."); - if (profiling_state_ == ProfilingState::kProfilingStateFinished) { - // This scenario (start, stop, and then start again) only happens in profiling, not autotune. - MS_LOG(INFO) << "MD ProfilingManager had already stopped. Resetting..."; - RETURN_IF_NOT_OK(Reset()); - for (const auto &node : sampling_nodes_) { - RETURN_IF_NOT_OK(node.second->Init()); - } - for (const auto &node : tracing_nodes_) { - RETURN_IF_NOT_OK(node.second->Init()); - } - profiling_ = true; - MS_LOG(INFO) << "MD profiler is reset successfully for profiling."; - } - - profiling_state_ = ProfilingState::kProfilingStateRunning; - for (const auto &node : tracing_nodes_) { - RETURN_IF_NOT_OK(node.second->Start()); - } - for (const auto &node : sampling_nodes_) { - RETURN_IF_NOT_OK(node.second->Start()); - } - MS_LOG(INFO) << "MD profiler is started."; - return Status::OK(); -} - -Status ProfilingManager::Stop() { - CHECK_FAIL_RETURN_UNEXPECTED(profiling_state_ != ProfilingState::kProfilingStateUnBegun, - "MD ProfilingManager has not started yet."); - // It's OK if we are in kProfilingStateFinished state. We allow user to call Stop twice. - if (profiling_state_ == ProfilingState::kProfilingStateFinished) { - MS_LOG(WARNING) << "MD ProfilingManager had already stopped."; - return Status::OK(); - } - - for (const auto &node : tracing_nodes_) { - RETURN_IF_NOT_OK(node.second->Stop()); - } - for (const auto &node : sampling_nodes_) { - RETURN_IF_NOT_OK(node.second->Stop()); - } - profiling_state_ = ProfilingState::kProfilingStateFinished; - if (autotuning_) { - autotuning_ = false; - MS_LOG(INFO) << "MD Autotune is stopped."; - } - if (profiling_) { - profiling_ = false; - MS_LOG(INFO) << "MD Profiler is stopped."; - } - return Status::OK(); -} - -Status ProfilingManager::Save(const std::string &profile_data_path) { - // Validate input profile data path - CHECK_FAIL_RETURN_UNEXPECTED(!profile_data_path.empty(), "Invalid parameter, Profiling directory is not set."); - CHECK_FAIL_RETURN_UNEXPECTED(profile_data_path.size() < PATH_MAX, "Invalid file, Profiling directory is invalid."); - - // profiling file: /filename_rank_id.suffix - char real_path[PATH_MAX] = {0}; -#if defined(_WIN32) || defined(_WIN64) - if (_fullpath(real_path, common::SafeCStr(profile_data_path), PATH_MAX) == nullptr) { - RETURN_STATUS_UNEXPECTED("Profiling dir is invalid."); - } -#else - if (realpath(common::SafeCStr(profile_data_path), real_path) == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid file, can not get realpath of Profiling directory."); - } -#endif - - std::string rank_id = GetRankID(); - // Output all profiling data upon request. - RETURN_IF_NOT_OK(SaveProfilingData(std::string(profile_data_path), rank_id)); - RETURN_IF_NOT_OK(ChangeFileMode(std::string(profile_data_path), rank_id)); - return Status::OK(); -} - -ProfilingManager::ProfilingRegistrationState ProfilingManager::GetProfilerTreeState(const ExecutionTree *tree) const { - auto enabled = (profiling_ || autotuning_); - if (!enabled) { - return kNotEnabled; - } - if (tree_ == nullptr) { - return kEnabledTreeNotRegistered; - } else { - return tree_ == tree ? kEnabledTreeRegistered : kEnabledDifferentTreeRegistered; - } -} - -std::string ProfilingManager::GetRankID() const { - std::string rank_id = common::GetEnv("RANK_ID"); -#ifdef WITH_BACKEND - MS_EXCEPTION_IF_NULL(MsContext::GetInstance()); - if (MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET) == kGPUDevice) { - std::shared_ptr cfg = GlobalContext::config_manager(); - int32_t rank_id_int = cfg->rank_id(); - // If DEVICE_ID is not set, default value is 0 - if (rank_id_int < 0) { - rank_id = common::GetEnv("DEVICE_ID"); - } else { - rank_id = std::to_string(rank_id_int); - } - } -#endif - // If RANK_ID is not set, default value is 0 - if (rank_id.empty()) { - rank_id = "0"; - } - return rank_id; -} - -uint64_t ProfilingTime::GetCurMilliSecond() { - // because cpplint does not allow using namespace - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::steady_clock; - return static_cast(duration_cast(steady_clock::now().time_since_epoch()).count()); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/perf/profiling.h b/mindspore-lite/minddata/dataset/engine/perf/profiling.h deleted file mode 100644 index de3c98bd3..000000000 --- a/mindspore-lite/minddata/dataset/engine/perf/profiling.h +++ /dev/null @@ -1,624 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_PROFILING_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_PROFILING_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/engine/perf/monitor.h" - -namespace mindspore { -namespace dataset { - -class Monitor; -class ExecutionTree; -class TreeConsumer; -class CpuSampler; -class TreeAdapter; - -const char kDeviceQueueTracingName[] = "Device_Queue_Tracing"; -const char kDatasetIteratorTracingName[] = "Dataset_Iterator_Tracing"; -const char kConnectorSizeSamplingName[] = "Connector_Size_Sampling"; -const char kCpuSamplerName[] = "Cpu_Sampler"; - -// Values for process memory metrics - common for profiling and cpu_sampler -enum ProcessMemoryMetric { kPSS, kRSS, kVSS }; - -// Values for system memory metrics - common for profiling and cpu_sampler -enum SystemMemoryMetric { kMemoryAvailable, kMemoryTotal, kMemoryUsed }; - -// Profiling is a class of basic unit of profiling action -// This base class encapsulate the serialization output logic -class Profiling : public std::enable_shared_from_this { - public: - // Constructor - Profiling() : active_(false) {} - - // Destructor - virtual ~Profiling() = default; - - virtual Status Init() = 0; - - // Default serialization file generator - virtual Status SaveToFile(const std::string &dir_path, const std::string &rank_id) = 0; - - // Profiling name - virtual std::string Name() const = 0; - - virtual Status ChangeFileMode(const std::string &dir_path, const std::string &rank_id) = 0; - - // Start collecting data - Status Start(); - - // Stop collecting data - Status Stop(); - - // Clear all collected data - virtual void Clear() = 0; - - protected: - bool active_; // show current state of ProfilingManager (running, or paused) - std::mutex lock_; - virtual Path GetFileName(const std::string &dir_path, const std::string &rank_id) = 0; -}; - -// Sampling is a class of profiling which generate samples periodically. -class Sampling : public Profiling { - public: - // Sampling action function. This function will be invoked by performance monitor thread. - virtual Status Sample() = 0; - - ~Sampling() override = default; -}; - -typedef struct TracingRecord_s { - int32_t type; - int32_t extra_info; - int32_t batch_num; - int32_t value; - uint64_t ts; - - std::string ToString() const { - return std::to_string(type) + " " + std::to_string(extra_info) + " " + std::to_string(batch_num) + " " + - std::to_string(value) + " " + std::to_string(ts); - } -} TracingRecord; - -// Tracing is class of profiling which record samples upon request. -class Tracing : public Profiling { - public: - // Tracing has minimal interface to provide flexible on data recording. - // It only includes some common routines. - Status SaveToFile(const std::string &dir_path, const std::string &rank_id) override; - Status ChangeFileMode(const std::string &dir_path, const std::string &rank_id) override; - Status Init() override; - Status GetPipelineTime(int32_t start_step, int32_t end_step, std::vector *result); - Status GetPushTime(int32_t start_step, int32_t end_step, std::vector *result); - Status GetBatchTime(int32_t start_step, int32_t end_step, std::vector *result); - Status GetConnectorSize(int32_t start_step, int32_t end_step, std::vector *result); - Status GetConnectorCapacity(int32_t start_step, int32_t end_step, std::vector *result); - Status GetEmptyQueueFrequency(int32_t start_step, int32_t end_step, float_t *empty_queue_freq); - void Record(const int32_t type, const int32_t extra_info, const int32_t batch_num, const int32_t value, - const uint64_t time_stamp); - Status TimeIntervalForStepRange(int32_t start_step, int32_t end_step, uint64_t *start_ts, uint64_t *end_ts); - Status StepIntervalForTimeRange(uint64_t start_ts, uint64_t end_ts, int32_t *start_step, int32_t *end_step); - size_t GetNumberSteps(); - - // Clear all collected data - void Clear() override; - - protected: - Tracing() = default; - std::vector value_; - std::vector records_; - std::vector ts_; // End time of each step or batch - Status GetRecordEntryFieldValue(int32_t start_step, int32_t end_step, int32_t record_offset, const std::string &field, - std::vector *result); -}; - -// ProfilingManager is a class manages all profiling infrastructure -// It serves the following purposes: -// 1) Fetch profiling configs from global contexts -// 2) Setup all profiling node based on config -// 3) Provide access of profiling nodes for profiling actions -// 4) Manage profiling data serialization process -class ProfilingManager { - friend Monitor; - - public: - ProfilingManager(); - - ~ProfilingManager() = default; - - /// Register the given tree to be profiled. - /// This method should be called once, calling it for another tree without resetting the ProfilingManager would fail. - /// \param tree_adapter pointer the adapter that owns the ExecutionTree - /// \return Status the status code returned - Status RegisterTree(ExecutionTree *tree); - - /// Reset the ProfilingManager. This method is sued when we want to profile another tree in the same process. - /// \return Status the status code returned - Status Reset(); - - // Save profile data to file - // @param dir_path_ The path to the directory where the profiling data will be saved. - // @return Status The status code returned - Status SaveProfilingData(const std::string &dir_path, const std::string &rank_id); - - // Sampling node getter - // @param name - The name of the requested node - // @param node - Pointer to the shared pointer for the Sampling node - // @return Status The status code returned - Status GetSamplingNode(const std::string &name, std::shared_ptr *node); - - // Tracing node getter - // @param name - The name of the requested node - // @param node - Pointer to the shared pointer for the Tracing node - // @return Status The status code returned - Status GetTracingNode(const std::string &name, std::shared_ptr *node); - - // return true if enabled_ is set to true, namely if Init() has been called successfully - // @param tree - Execution tree pointer - bool IsProfilingEnable(const ExecutionTree *tree = nullptr) const; - - // Record end of epoch information - // @param step_num - The number of steps - void RecordEndOfEpoch(uint32_t step_num); - - const std::unordered_map> &GetSamplingNodes() const { return sampling_nodes_; } - - // Launch monitoring thread. - Status LaunchMonitor(); - - // @return Status The status code returned - Status ChangeFileMode(const std::string &dir_path, const std::string &rank_id); - -#ifndef ENABLE_ANDROID - /// \brief API to get User CPU utilization for the system - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the sampled User CPU Utilization for the entire system - /// \return Status object with the error code - Status GetUserCpuUtilByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get User CPU utilization for the system - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the sampled User CPU Utilization for the entire system - /// \return Status object with the error code - Status GetUserCpuUtilByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get User CPU utilization for the system - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the sampled User CPU Utilization for the entire system - /// \return Status object with the error code - Status GetUserCpuUtilByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get System CPU utilization for the system - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the sampled System CPU Utilization for the entire system - /// \return Status object with the error code - Status GetSysCpuUtilByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get System CPU utilization for the system - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the sampled System CPU Utilization for the entire system - /// \return Status object with the error code - Status GetSysCpuUtilByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get System CPU utilization for the system - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the sampled System CPU Utilization for the entire system - /// \return Status object with the error code - Status GetSysCpuUtilByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get User CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the sampled User CPU Utilization of the operator. - /// \return Status object with the error code - Status GetUserCpuUtilByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result); - - /// \brief API to get User CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the sampled User CPU Utilization of the operator. - /// \return Status object with the error code - Status GetUserCpuUtilByStep(int32_t op_id, int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get User CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the sampled User CPU Utilization of the operator. - /// \return Status object with the error code - Status GetUserCpuUtilByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get System CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the sampled System CPU Utilization of the operator. - /// \return Status object with the error code - Status GetSysCpuUtilByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result); - - /// \brief API to get System CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the sampled System CPU Utilization of the operator. - /// \return Status object with the error code - Status GetSysCpuUtilByStep(int32_t op_id, int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get System CPU Utilization of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the sampled System CPU Utilization of the operator. - /// \return Status object with the error code - Status GetSysCpuUtilByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get information on main process memory usage - /// \param [in] metric The requested memory set usage. One of these values: - /// - ProcessMemoryMetric::kVSS - virtual set size, virtual memory usage - /// - ProcessMemoryMetric::kPSS - proportional set size, physical memory usage with proportional allocation of - /// shared libraries - /// - ProcessMemoryMetric::kRSS - resident set size, physical memory usage (includes shared libraries) - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetMainProcessMemoryInfoByEpoch(ProcessMemoryMetric metric, int32_t epoch_num, std::vector *result); - - /// \brief API to get information on main process memory usage - /// \param [in] metric The requested memory set usage. One of these values: - /// - ProcessMemoryMetric::kVSS - virtual set size, virtual memory usage - /// - ProcessMemoryMetric::kPSS - proportional set size, physical memory usage with proportional allocation of - /// shared libraries - /// - ProcessMemoryMetric::kRSS - resident set size, physical memory usage (includes shared libraries) - /// \param [in] end_ts The time interval end range in ms - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetMainProcessMemoryInfoByStep(ProcessMemoryMetric metric, int32_t start_step, int32_t end_step, - std::vector *result); - - /// \brief API to get information on main process memory usage - /// \param [in] metric The requested memory set usage. One of these values: - /// - ProcessMemoryMetric::kVSS - virtual set size, virtual memory usage - /// - ProcessMemoryMetric::kPSS - proportional set size, physical memory usage with proportional allocation of - /// shared libraries - /// - ProcessMemoryMetric::kRSS - resident set size, physical memory usage (includes shared libraries) - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetMainProcessMemoryInfoByTime(ProcessMemoryMetric metric, uint64_t start_ts, uint64_t end_ts, - std::vector *result); - - /// \brief API to get information on system memory usage - /// \param [in] metric The requested memory metric. One of these values: - /// - SystemMemoryMetric::kMemoryAvailable - /// - SystemMemoryMetric::kMemoryTotal - /// - SystemMemoryMetric::kMemoryUsed - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetSystemMemoryInfoByEpoch(SystemMemoryMetric metric, int32_t epoch_num, std::vector *result); - - /// \brief API to get information on system memory usage - /// \param [in] metric The requested memory metric. One of these values: - /// - SystemMemoryMetric::kMemoryAvailable - /// - SystemMemoryMetric::kMemoryTotal - /// - SystemMemoryMetric::kMemoryUsed - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetSystemMemoryInfoByStep(SystemMemoryMetric metric, int32_t start_step, int32_t end_step, - std::vector *result); - - /// \brief API to get information on system memory usage - /// \param [in] metric The requested memory metric. One of these values: - /// - SystemMemoryMetric::kMemoryAvailable - /// - SystemMemoryMetric::kMemoryTotal - /// - SystemMemoryMetric::kMemoryUsed - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result The desired value in MB - /// \return Status object with the error code - Status GetSystemMemoryInfoByTime(SystemMemoryMetric metric, uint64_t start_ts, uint64_t end_ts, - std::vector *result); -#endif - - /// \brief API to get the connector size of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the sampled connector sizes of the operator - /// \return Status object with the error code - Status GetConnectorSizeByEpoch(int32_t op_id, int32_t epoch_num, std::vector *result); - - /// \brief API to get the connector size of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the sampled connector sizes of the operator - /// \return Status object with the error code - Status GetConnectorSizeByStep(int32_t op_id, int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the connector size of an MD operator - /// \param [in] op_id The id of the operator - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the sampled connector sizes of the operator - /// \return Status object with the error code - Status GetConnectorSizeByTime(int32_t op_id, uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get the connector size of DatasetIterator or DataQueueOp - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with connector size at each step - /// \return Status object with the error code - Status GetConnectorSizeByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get the connector size of DatasetIterator or DataQueueOp - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with connector size at each step - /// \return Status object with the error code - Status GetConnectorSizeByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the connector size of DatasetIterator or DataQueueOp - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with connector size at each step - /// \return Status object with the error code - Status GetConnectorSizeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get the connector capacity of DatasetIterator or DataQueueOp - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with connector capacity at each step - /// \return Status object with the error code - Status GetConnectorCapacityByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get the connector capacity of DatasetIterator or DataQueueOp - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with connector capacity at each step - /// \return Status object with the error code - Status GetConnectorCapacityByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the connector capacity of DatasetIterator or DataQueueOp - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with connector capacity for steps in the given time range - /// \return Status object with the error code - Status GetConnectorCapacityByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get the pipeline time of batches - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the pipeline time for each step - /// \return Status object with the error code - Status GetPipelineTimeByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get the pipeline time of batches - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the pipeline time for each step - /// \return Status object with the error code - Status GetPipelineTimeByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the pipeline time of batches - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the pipeline time for steps in the given time range - /// \return Status object with the error code - Status GetPipelineTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get the push time of batches - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the push time for each each step - /// \return Status object with the error code - Status GetPushTimeByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get the push time of batches - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the push time for each each step - /// \return Status object with the error code - Status GetPushTimeByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the push time of batches - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the push time for steps in the given time range - /// \return Status object with the error code - Status GetPushTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get the batch time of batches - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result A vector with the batch time for each step - /// \return Status object with the error code - Status GetBatchTimeByEpoch(int32_t epoch_num, std::vector *result); - - /// \brief API to get the batch time of batches - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result A vector with the batch time for each step - /// \return Status object with the error code - Status GetBatchTimeByStep(int32_t start_step, int32_t end_step, std::vector *result); - - /// \brief API to get the batch time of batches - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result A vector with the batch time for steps in the given time range - /// \return Status object with the error code - Status GetBatchTimeByTime(uint64_t start_ts, uint64_t end_ts, std::vector *result); - - /// \brief API to get fraction of steps that DatasetIterator or DataQueueOp connector was empty - /// \param [in] epoch_num The epoch number for which results are requested - /// \param [out] result The empty queue frequency - /// \return Status object with the error code - Status GetEmptyQueueFrequencyByEpoch(int32_t epoch_num, float_t *result); - - /// \brief API to get fraction of steps that DatasetIterator or DataQueueOp connector was empty - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] result The empty queue frequency - /// \return Status object with the error code - Status GetEmptyQueueFrequencyByStep(int32_t start_step, int32_t end_step, float_t *result); - - /// \brief API to get fraction of steps that DatasetIterator or DataQueueOp connector was empty - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] result The empty queue frequency - /// \return Status object with the error code - Status GetEmptyQueueFrequencyByTime(uint64_t start_ts, uint64_t end_ts, float_t *result); - - // Register profile node to tree - // @param node - Profiling node - // @return Status The status code returned - Status RegisterTracingNode(const std::shared_ptr &node); - - /// \brief API to initialize profiling manager - /// \param for_autotune flag to indicate if Profiler is initialized for autotuning or profiling purposes - /// \return Status object with the error code - Status Init(const bool for_autotune = false); - - /// \brief API to signal the profiling nodes to start collecting data - /// \return Status object with the error code - Status Start(); - - /// \brief API to signal profiling nodes to stop collecting data - /// \return Status object with the error code - Status Stop(); - - /// \brief API to save to file all the collected data between Start and Stop calls - /// \return Status object with the error code - Status Save(const std::string &profile_data_path); - - /// \brief Helper to get the rank id. Currently being used for appending rank id to files - /// \return String The rank id - std::string GetRankID() const; - - /// Get number of epochs that have been already profiled - /// \return number of epochs - int32_t GetNumOfProfiledEpochs() const { return static_cast(epoch_end_step_.size()) - 1; } - - // Get number of steps taken in pipeline - /// \return number of steps - Status GetNumberOfProfiledSteps(int32_t *steps); - - /// Determine if the Profiler is being used for autotuning. - /// \return boolean - bool IsAutotuning() const { return autotuning_; } - - /// Determine if the Profiler is being used for profiling. - /// \return boolean - bool IsProfiling() const { return profiling_; } - - // Registration state for the profiler - enum ProfilingRegistrationState { - kNotEnabled, - kEnabledTreeNotRegistered, - kEnabledTreeRegistered, - kEnabledDifferentTreeRegistered, - }; - - /// \brief Getter for the profiling and tree registration state - /// \param tree Execution Tree pointer - /// \return ProfilingRegistrationState - ProfilingRegistrationState GetProfilerTreeState(const ExecutionTree *tree) const; - - protected: - std::unique_ptr perf_monitor_; - - // State flags for profiling - enum ProfilingState { - kProfilingStateUnBegun, - kProfilingStateRunning, - kProfilingStateFinished, - }; - ProfilingState profiling_state_; // show current state of ProfilingManager (running, or paused) - std::unordered_map> tracing_nodes_; - std::unordered_map> sampling_nodes_; - ExecutionTree *tree_; // ExecutionTree pointer - std::vector epoch_end_ts_; // End of epoch timestamp - std::vector epoch_end_step_; // End of epoch step number - std::atomic autotuning_; // flag to indicate if ProfilingManager is being used for auto-tuning the pipeline - std::atomic profiling_; // flag to indicate if ProfilingManager is being used for profiling the pipeline - - // Register profile node to tree - // @param node - Profiling node - // @return Status The status code returned - Status RegisterSamplingNode(const std::shared_ptr &node); - - /// \brief Helper to convert a given epoch number to a step interval - /// \param [in] epoch_num The epoch number to be converted - /// \param [out] start_step The corresponding start step for the given epoch - /// \param [out] end_step The corresponding end step for the given epoch - /// \return Status object with the error code - Status EpochToStepInterval(int32_t epoch_num, uint32_t *start_step, uint32_t *end_step); - - /// \brief Helper to convert a given epoch number to a time interval - /// \param [in] epoch_num The epoch number to be converted - /// \param [out] start_ts The corresponding starting timestamp in ms for the given epoch - /// \param [out] end_ts The corresponding ending timestamp in ms for the given epoch - /// \return Status object with the error code - Status EpochToTimeInterval(int32_t epoch_num, uint64_t *start_ts, uint64_t *end_ts); - - /// \brief Helper to convert step interval to a time interval - /// \param [in] start_step The step interval start range - /// \param [in] end_step The step interval end range - /// \param [out] start_ts The corresponding starting timestamp in ms for the given step interval - /// \param [out] end_ts The corresponding ending timestamp in ms for the given step interval - /// \return Status object with the error code - Status StepToTimeInterval(int32_t start_step, int32_t end_step, uint64_t *start_ts, uint64_t *end_ts); - - /// \brief Helper to convert time interval to a step interval - /// \param [in] start_ts The time interval start range in ms - /// \param [in] end_ts The time interval end range in ms - /// \param [out] start_step The corresponding start step for the given time interval - /// \param [out] end_step The corresponding end step for the given time interval - /// \return Status object with the error code - Status TimeToStepInterval(uint64_t start_ts, uint64_t end_ts, int32_t *start_step, int32_t *end_step); -}; - -enum ProfilingType { TIME, CONNECTOR_DEPTH }; - -enum ProfilingTimeSubType { - PIPELINE_TIME, - TDT_PUSH_TIME, - BATCH_TIME, - INVALID_TIME, -}; - -class ProfilingTime { - public: - static uint64_t GetCurMilliSecond(); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PERF_PROFILING_H_ diff --git a/mindspore-lite/minddata/dataset/engine/python_runtime_context.cc b/mindspore-lite/minddata/dataset/engine/python_runtime_context.cc deleted file mode 100644 index 16acbc66b..000000000 --- a/mindspore-lite/minddata/dataset/engine/python_runtime_context.cc +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/python_runtime_context.h" -#include "pybind11/pybind11.h" - -namespace mindspore::dataset { -Status PythonRuntimeContext::Terminate() { - MS_LOG(INFO) << "Terminating a Dataset PythonRuntime."; - if (tree_consumer_ != nullptr) { - return TerminateImpl(); - } - MS_LOG(INFO) << "Dataset TreeConsumer was not initialized."; - return Status::OK(); -} - -Status PythonRuntimeContext::TerminateImpl() { - CHECK_FAIL_RETURN_UNEXPECTED(tree_consumer_ != nullptr, "Dataset TreeConsumer is not initialized."); - // Release GIL before joining all threads - py::gil_scoped_release gil_release; - return tree_consumer_->Terminate(); -} - -PythonRuntimeContext::~PythonRuntimeContext() { - Status rc = PythonRuntimeContext::Terminate(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error while terminating the consumer. Message:" << rc; - } - if (tree_consumer_) { - tree_consumer_.reset(); - } -} - -TreeConsumer *PythonRuntimeContext::GetPythonConsumer() { - if (GlobalContext::config_manager()->get_debug_mode()) { - return dynamic_cast(tree_consumer_.get()); - } else { - return dynamic_cast(tree_consumer_.get()); - } -} -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/python_runtime_context.h b/mindspore-lite/minddata/dataset/engine/python_runtime_context.h deleted file mode 100644 index 7f5df6194..000000000 --- a/mindspore-lite/minddata/dataset/engine/python_runtime_context.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PYTHON_RUNTIME_CONTEXT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PYTHON_RUNTIME_CONTEXT_H_ - -#include "mindspore-lite/minddata/dataset/core/client.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/python_tree_consumer.h" -#include "mindspore-lite/minddata/dataset/engine/runtime_context.h" - -namespace mindspore::dataset { -class NativeRuntimeContext; - -/// Class that represents Python single runtime instance which can consume data from a data pipeline -class PythonRuntimeContext : public RuntimeContext { - public: - /// Method to terminate the runtime, this will not release the resources - /// \return Status error code - Status Terminate() override; - - /// Safe destructing the tree that includes python objects - ~PythonRuntimeContext() override; - - TreeConsumer *GetPythonConsumer(); - - private: - /// Internal function to perform the termination - /// \return Status error code - Status TerminateImpl(); -}; - -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_PYTHON_RUNTIME_CONTEXT_H_ diff --git a/mindspore-lite/minddata/dataset/engine/runtime_context.cc b/mindspore-lite/minddata/dataset/engine/runtime_context.cc deleted file mode 100644 index 3e9361de3..000000000 --- a/mindspore-lite/minddata/dataset/engine/runtime_context.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/runtime_context.h" -#include - -namespace mindspore::dataset { -void RuntimeContext::AssignConsumer(std::shared_ptr tree_consumer) { - tree_consumer_ = std::move(tree_consumer); -} -Status NativeRuntimeContext::Terminate() { - MS_LOG(INFO) << "Terminating a Dataset NativeRuntime."; - if (tree_consumer_ != nullptr) { - return TerminateImpl(); - } - MS_LOG(INFO) << "Dataset TreeConsumer was not initialized."; - return Status::OK(); -} - -Status NativeRuntimeContext::TerminateImpl() { - CHECK_FAIL_RETURN_UNEXPECTED(tree_consumer_ != nullptr, "Dataset TreeConsumer is not initialized."); - return tree_consumer_->Terminate(); -} - -NativeRuntimeContext::~NativeRuntimeContext() { - Status rc = NativeRuntimeContext::Terminate(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error while terminating the consumer. Message:" << rc; - } -} - -TreeConsumer *RuntimeContext::GetConsumer() { return tree_consumer_.get(); } - -Status RuntimeContext::Init() const { return GlobalInit(); } -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/engine/runtime_context.h b/mindspore-lite/minddata/dataset/engine/runtime_context.h deleted file mode 100644 index 7d5d76394..000000000 --- a/mindspore-lite/minddata/dataset/engine/runtime_context.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_RUNTIME_CONTEXT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_RUNTIME_CONTEXT_H_ - -#include -#include "mindspore-lite/minddata/dataset/core/client.h" -#include "mindspore-lite/minddata/dataset/engine/consumers/tree_consumer.h" - -namespace mindspore::dataset { -class TreeConsumer; -/// Class that represents single runtime instance which can consume data from a data pipeline -class RuntimeContext { - public: - /// Default constructor - RuntimeContext() = default; - - /// Initialize the runtime, for now we just call the global init - /// \return Status error code - Status Init() const; - - /// Set the tree consumer - /// \param tree_consumer to be assigned - void AssignConsumer(std::shared_ptr tree_consumer); - - /// Get the tree consumer - /// \return Raw pointer to the tree consumer. - TreeConsumer *GetConsumer(); - - /// Method to terminate the runtime, this will not release the resources - /// \return Status error code - virtual Status Terminate() = 0; - - virtual ~RuntimeContext() = default; - - std::shared_ptr tree_consumer_; -}; - -/// Class that represents C++ single runtime instance which can consume data from a data pipeline -class NativeRuntimeContext : public RuntimeContext { - public: - /// Method to terminate the runtime, this will not release the resources - /// \return Status error code - Status Terminate() override; - - ~NativeRuntimeContext() override; - - private: - /// Internal function to perform the termination - /// \return Status error code - Status TerminateImpl(); -}; - -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_RUNTIME_CONTEXT_H_ diff --git a/mindspore-lite/minddata/dataset/engine/serdes.cc b/mindspore-lite/minddata/dataset/engine/serdes.cc deleted file mode 100644 index 25746a45f..000000000 --- a/mindspore-lite/minddata/dataset/engine/serdes.cc +++ /dev/null @@ -1,487 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/engine/serdes.h" - -#include -#include -#include - -#include "include/utils/utils.h" -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -#else -#include "mindspore-lite/src/common/file_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" - -namespace mindspore { -namespace dataset { -std::map *operation)> - Serdes::func_ptr_ = Serdes::InitializeFuncPtr(); - -Status Serdes::SaveToJSON(std::shared_ptr node, const std::string &filename, nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(node); - RETURN_UNEXPECTED_IF_NULL(out_json); - // If an optimized IR Tree is sent (use-case for MD AutoTune), ignore Top and EpochCtrl nodes - if (node->Name() == "Top" || node->Name() == "EpochCtrl") { - CHECK_FAIL_RETURN_UNEXPECTED( - node->Children().size() == 1, - "Expected " + node->Name() + " to have exactly 1 child but it has " + std::to_string(node->Children().size())); - return SaveToJSON(node->Children()[0], filename, out_json); - } - // Dump attributes of current node to json string - nlohmann::json args; - RETURN_IF_NOT_OK(node->to_json(&args)); - args["op_type"] = node->Name(); - - // If the current node isn't leaf node, visit all its children and get all attributes - std::vector children_pipeline; - if (!node->IsLeaf()) { - for (auto child : node->Children()) { - nlohmann::json child_args; - RETURN_IF_NOT_OK(SaveToJSON(child, "", &child_args)); - children_pipeline.push_back(child_args); - } - } - args["children"] = children_pipeline; - - // Save json string into file if filename is given. - if (!filename.empty()) { - RETURN_IF_NOT_OK(SaveJSONToFile(args, filename)); - } - - *out_json = args; - return Status::OK(); -} - -Status Serdes::SaveJSONToFile(const nlohmann::json &json_string, const std::string &file_name, bool pretty) { - constexpr int field_width = 4; - std::optional dir = ""; - std::optional local_file_name = ""; - FileUtils::SplitDirAndFileName(file_name, &dir, &local_file_name); - if (!dir.has_value()) { - dir = "."; - } - auto realpath = FileUtils::GetRealPath(dir.value().c_str()); - if (!realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file, get real path failed, path=" << file_name; - RETURN_STATUS_UNEXPECTED("Invalid file, get real path failed, path=" + file_name); - } - - std::optional whole_path = ""; - FileUtils::ConcatDirAndFileName(&realpath, &local_file_name, &whole_path); - - std::ofstream file(whole_path.value(), std::ios::out); - try { - if (pretty) { - file << std::setw(field_width); - } - file << json_string << std::endl; - file.close(); - - ChangeFileMode(whole_path.value(), S_IRUSR | S_IWUSR); - } catch (const std::exception &err) { - if (file.is_open()) { - file.close(); - } - RETURN_STATUS_UNEXPECTED("Invalid data, failed to save json string into file: " + file_name + - ", error message: " + err.what()); - } - return Status::OK(); -} - -Status Serdes::Deserialize(const std::string &json_filepath, std::shared_ptr *ds) { - nlohmann::json json_obj; - CHECK_FAIL_RETURN_UNEXPECTED(json_filepath.size() != 0, "Json path is null"); - std::ifstream json_in(json_filepath, std::ios::in); - CHECK_FAIL_RETURN_UNEXPECTED(json_in, "Invalid file, failed to open json file: " + json_filepath); - try { - json_in >> json_obj; - } catch (const std::exception &e) { - json_in.close(); - return Status(StatusCode::kMDSyntaxError, - "Invalid file, failed to parse json file: " + json_filepath + ", error message: " + e.what()); - } - json_in.close(); - // Handle config generated by dataset autotune - if (json_obj.find("tree") != json_obj.end()) { - json_obj = json_obj["tree"]; - } - RETURN_IF_NOT_OK(ConstructPipeline(json_obj, ds)); - return Status::OK(); -} - -Status Serdes::ConstructPipeline(nlohmann::json json_obj, std::shared_ptr *ds) { - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("children") != json_obj.end(), "Failed to find children"); - std::shared_ptr child_ds; - - if (json_obj["children"].size() == 0) { - // If the JSON object has no child, then this node is a leaf node. Call create node to construct the corresponding - // leaf node - RETURN_IF_NOT_OK(CreateNode(nullptr, json_obj, ds)); - } else if (json_obj["children"].size() == 1) { - // This node only has one child, construct the sub-tree under it first, and then call create node to construct the - // corresponding node - RETURN_IF_NOT_OK(ConstructPipeline(json_obj["children"][0], &child_ds)); - RETURN_IF_NOT_OK(CreateNode(child_ds, json_obj, ds)); - } else { - std::vector> datasets; - for (const auto &child_json_obj : json_obj["children"]) { - RETURN_IF_NOT_OK(ConstructPipeline(child_json_obj, &child_ds)); - datasets.push_back(child_ds); - } - if (json_obj["op_type"] == "Zip") { - CHECK_FAIL_RETURN_UNEXPECTED(datasets.size() > 1, "Should zip more than 1 dataset"); - RETURN_IF_NOT_OK(ZipNode::from_json(datasets, ds)); - } else if (json_obj["op_type"] == "Concat") { - CHECK_FAIL_RETURN_UNEXPECTED(datasets.size() > 1, "Should concat more than 1 dataset"); - RETURN_IF_NOT_OK(ConcatNode::from_json(json_obj, datasets, ds)); - } else { - return Status(StatusCode::kMDUnexpectedError, - "Invalid data, unsupported operation type: " + std::string(json_obj["op_type"])); - } - } - return Status::OK(); -} - -Status Serdes::CreateNode(const std::shared_ptr &child_ds, nlohmann::json json_obj, - std::shared_ptr *ds) { - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("op_type") != json_obj.end(), "Failed to find op_type in json."); - std::string op_type = json_obj["op_type"]; - if (child_ds == nullptr) { - // if dataset doesn't have any child, then create a source dataset IR. e.g., ImageFolderNode, CocoNode - RETURN_IF_NOT_OK(CreateDatasetNode(json_obj, op_type, ds)); - } else { - // if the dataset has at least one child, then create an operation dataset IR, e.g., BatchNode, MapNode - RETURN_IF_NOT_OK(CreateDatasetOperationNode(child_ds, json_obj, op_type, ds)); - } - return Status::OK(); -} - -Status Serdes::CreateDatasetNode(const nlohmann::json &json_obj, const std::string &op_type, - std::shared_ptr *ds) { - if (op_type == kAlbumNode) { - RETURN_IF_NOT_OK(AlbumNode::from_json(json_obj, ds)); - } else if (op_type == kCelebANode) { - RETURN_IF_NOT_OK(CelebANode::from_json(json_obj, ds)); - } else if (op_type == kCifar10Node) { - RETURN_IF_NOT_OK(Cifar10Node::from_json(json_obj, ds)); - } else if (op_type == kCifar100Node) { - RETURN_IF_NOT_OK(Cifar100Node::from_json(json_obj, ds)); - } else if (op_type == kCLUENode) { - RETURN_IF_NOT_OK(CLUENode::from_json(json_obj, ds)); - } else if (op_type == kCocoNode) { - RETURN_IF_NOT_OK(CocoNode::from_json(json_obj, ds)); - } else if (op_type == kCSVNode) { - RETURN_IF_NOT_OK(CSVNode::from_json(json_obj, ds)); - } else if (op_type == kFlickrNode) { - RETURN_IF_NOT_OK(FlickrNode::from_json(json_obj, ds)); - } else if (op_type == kImageFolderNode) { - RETURN_IF_NOT_OK(ImageFolderNode::from_json(json_obj, ds)); - } else if (op_type == kManifestNode) { - RETURN_IF_NOT_OK(ManifestNode::from_json(json_obj, ds)); - } else if (op_type == kMnistNode) { - RETURN_IF_NOT_OK(MnistNode::from_json(json_obj, ds)); - } else if (op_type == kTextFileNode) { - RETURN_IF_NOT_OK(TextFileNode::from_json(json_obj, ds)); - } else if (op_type == kTFRecordNode) { - RETURN_IF_NOT_OK(TFRecordNode::from_json(json_obj, ds)); - } else if (op_type == kVOCNode) { - RETURN_IF_NOT_OK(VOCNode::from_json(json_obj, ds)); - } else { - return Status(StatusCode::kMDUnexpectedError, "Invalid data, unsupported operation type: " + op_type); - } - return Status::OK(); -} - -Status Serdes::CreateDatasetOperationNode(const std::shared_ptr &ds, const nlohmann::json &json_obj, - const std::string &op_type, std::shared_ptr *result) { - if (op_type == kBatchNode) { - RETURN_IF_NOT_OK(BatchNode::from_json(json_obj, ds, result)); - } else if (op_type == kMapNode) { - RETURN_IF_NOT_OK(MapNode::from_json(json_obj, ds, result)); - } else if (op_type == kProjectNode) { - RETURN_IF_NOT_OK(ProjectNode::from_json(json_obj, ds, result)); - } else if (op_type == kRenameNode) { - RETURN_IF_NOT_OK(RenameNode::from_json(json_obj, ds, result)); - } else if (op_type == kRepeatNode) { - RETURN_IF_NOT_OK(RepeatNode::from_json(json_obj, ds, result)); - } else if (op_type == kShuffleNode) { - RETURN_IF_NOT_OK(ShuffleNode::from_json(json_obj, ds, result)); - } else if (op_type == kSkipNode) { - RETURN_IF_NOT_OK(SkipNode::from_json(json_obj, ds, result)); - } else if (op_type == kTransferNode) { - RETURN_IF_NOT_OK(DataQueueNode::from_json(json_obj, ds, result)); - } else if (op_type == kTakeNode) { - RETURN_IF_NOT_OK(TakeNode::from_json(json_obj, ds, result)); - } else { - return Status(StatusCode::kMDUnexpectedError, "Invalid data, unsupported operation type: " + op_type); - } - return Status::OK(); -} - -Status Serdes::ConstructSampler(nlohmann::json json_obj, std::shared_ptr *sampler) { - if (json_obj["sampler_name"] == "SkipFirstEpochSampler") { - RETURN_IF_NOT_OK(SkipFirstEpochSamplerObj::from_json(json_obj, sampler)); - return Status::OK(); - } - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("num_samples") != json_obj.end(), "Failed to find num_samples"); - CHECK_FAIL_RETURN_UNEXPECTED(json_obj.find("sampler_name") != json_obj.end(), "Failed to find sampler_name"); - int64_t num_samples = json_obj["num_samples"]; - std::string sampler_name = json_obj["sampler_name"]; - if (sampler_name == "DistributedSampler") { - RETURN_IF_NOT_OK(DistributedSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "PKSampler") { - RETURN_IF_NOT_OK(PKSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "RandomSampler") { - RETURN_IF_NOT_OK(RandomSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "SequentialSampler") { - RETURN_IF_NOT_OK(SequentialSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "SubsetSampler") { - RETURN_IF_NOT_OK(SubsetSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "SubsetRandomSampler") { - RETURN_IF_NOT_OK(SubsetRandomSamplerObj::from_json(json_obj, num_samples, sampler)); - } else if (sampler_name == "WeightedRandomSampler") { - RETURN_IF_NOT_OK(WeightedRandomSamplerObj::from_json(json_obj, num_samples, sampler)); - } else { - return Status(StatusCode::kMDUnexpectedError, "Invalid data, unsupported sampler type: " + sampler_name); - } - return Status::OK(); -} - -Status Serdes::ConstructTensorOps(nlohmann::json json_obj, std::vector> *result) { - std::vector> output; - for (nlohmann::json item : json_obj) { - if (item.find("python_module") != item.end()) { - if (Py_IsInitialized() != 0) { - std::vector> tmp_res; - RETURN_IF_NOT_OK(PyFuncOp::from_json(item, &tmp_res)); - output.insert(output.end(), tmp_res.begin(), tmp_res.end()); - } else { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR( - "Python module is not initialized or Pyfunction is not supported on this platform."); - } - } else { - CHECK_FAIL_RETURN_UNEXPECTED(item.find("tensor_op_name") != item.end(), "Failed to find tensor_op_name"); - CHECK_FAIL_RETURN_UNEXPECTED(item.find("tensor_op_params") != item.end(), "Failed to find tensor_op_params"); - std::string op_name = item["tensor_op_name"]; - nlohmann::json op_params = item["tensor_op_params"]; - std::shared_ptr operation = nullptr; - CHECK_FAIL_RETURN_UNEXPECTED(func_ptr_.find(op_name) != func_ptr_.end(), - "Invalid data, unsupported operation: " + op_name); - RETURN_IF_NOT_OK(func_ptr_[op_name](op_params, &operation)); - output.push_back(operation); - } - } - *result = output; - return Status::OK(); -} - -std::map *operation)> -Serdes::InitializeFuncPtr() { - std::map *operation)> ops_ptr; - ops_ptr[vision::kAdjustGammaOperation] = &(vision::AdjustGammaOperation::from_json); - ops_ptr[vision::kAffineOperation] = &(vision::AffineOperation::from_json); - ops_ptr[vision::kAutoContrastOperation] = &(vision::AutoContrastOperation::from_json); - ops_ptr[vision::kBoundingBoxAugmentOperation] = &(vision::BoundingBoxAugmentOperation::from_json); - ops_ptr[vision::kCenterCropOperation] = &(vision::CenterCropOperation::from_json); - ops_ptr[vision::kCropOperation] = &(vision::CropOperation::from_json); - ops_ptr[vision::kCutMixBatchOperation] = &(vision::CutMixBatchOperation::from_json); - ops_ptr[vision::kCutOutOperation] = &(vision::CutOutOperation::from_json); - ops_ptr[vision::kDecodeOperation] = &(vision::DecodeOperation::from_json); -#if defined(WITH_BACKEND) || defined(ENABLE_ACL) - if (AclAdapter::GetInstance().HasAclPlugin()) { - ops_ptr[vision::kDvppCropJpegOperation] = &(vision::DvppCropJpegOperation::from_json); - ops_ptr[vision::kDvppDecodeResizeOperation] = &(vision::DvppDecodeResizeOperation::from_json); - ops_ptr[vision::kDvppDecodeResizeCropOperation] = &(vision::DvppDecodeResizeCropOperation::from_json); - ops_ptr[vision::kDvppNormalizeOperation] = &(vision::DvppNormalizeOperation::from_json); - ops_ptr[vision::kDvppResizeJpegOperation] = &(vision::DvppResizeJpegOperation::from_json); - } -#endif - ops_ptr[vision::kEqualizeOperation] = &(vision::EqualizeOperation::from_json); - ops_ptr[vision::kGaussianBlurOperation] = &(vision::GaussianBlurOperation::from_json); - ops_ptr[vision::kHorizontalFlipOperation] = &(vision::HorizontalFlipOperation::from_json); - ops_ptr[vision::kHwcToChwOperation] = &(vision::HwcToChwOperation::from_json); - ops_ptr[vision::kInvertOperation] = &(vision::InvertOperation::from_json); - ops_ptr[vision::kMixUpBatchOperation] = &(vision::MixUpBatchOperation::from_json); - ops_ptr[vision::kNormalizeOperation] = &(vision::NormalizeOperation::from_json); - ops_ptr[vision::kNormalizePadOperation] = &(vision::NormalizePadOperation::from_json); - ops_ptr[vision::kPadOperation] = &(vision::PadOperation::from_json); - ops_ptr[vision::kRandomAffineOperation] = &(vision::RandomAffineOperation::from_json); - ops_ptr[vision::kRandomColorOperation] = &(vision::RandomColorOperation::from_json); - ops_ptr[vision::kRandomColorAdjustOperation] = &(vision::RandomColorAdjustOperation::from_json); - ops_ptr[vision::kRandomCropDecodeResizeOperation] = &(vision::RandomCropDecodeResizeOperation::from_json); - ops_ptr[vision::kRandomCropOperation] = &(vision::RandomCropOperation::from_json); - ops_ptr[vision::kRandomCropWithBBoxOperation] = &(vision::RandomCropWithBBoxOperation::from_json); - ops_ptr[vision::kRandomHorizontalFlipOperation] = &(vision::RandomHorizontalFlipOperation::from_json); - ops_ptr[vision::kRandomHorizontalFlipWithBBoxOperation] = &(vision::RandomHorizontalFlipWithBBoxOperation::from_json); - ops_ptr[vision::kRandomPosterizeOperation] = &(vision::RandomPosterizeOperation::from_json); - ops_ptr[vision::kRandomResizeOperation] = &(vision::RandomResizeOperation::from_json); - ops_ptr[vision::kRandomResizeWithBBoxOperation] = &(vision::RandomResizeWithBBoxOperation::from_json); - ops_ptr[vision::kRandomResizedCropOperation] = &(vision::RandomResizedCropOperation::from_json); - ops_ptr[vision::kRandomResizedCropWithBBoxOperation] = &(vision::RandomResizedCropWithBBoxOperation::from_json); - ops_ptr[vision::kRandomRotationOperation] = &(vision::RandomRotationOperation::from_json); - ops_ptr[vision::kRandomSelectSubpolicyOperation] = &(vision::RandomSelectSubpolicyOperation::from_json); - ops_ptr[vision::kRandomSharpnessOperation] = &(vision::RandomSharpnessOperation::from_json); - ops_ptr[vision::kRandomSolarizeOperation] = &(vision::RandomSolarizeOperation::from_json); - ops_ptr[vision::kRandomVerticalFlipOperation] = &(vision::RandomVerticalFlipOperation::from_json); - ops_ptr[vision::kRandomVerticalFlipWithBBoxOperation] = &(vision::RandomVerticalFlipWithBBoxOperation::from_json); - ops_ptr[vision::kRandomSharpnessOperation] = &(vision::RandomSharpnessOperation::from_json); - ops_ptr[vision::kRandomSolarizeOperation] = &(vision::RandomSolarizeOperation::from_json); - ops_ptr[vision::kRescaleOperation] = &(vision::RescaleOperation::from_json); - ops_ptr[vision::kResizeOperation] = &(vision::ResizeOperation::from_json); - ops_ptr[vision::kResizePreserveAROperation] = &(vision::ResizePreserveAROperation::from_json); - ops_ptr[vision::kResizeWithBBoxOperation] = &(vision::ResizeWithBBoxOperation::from_json); - ops_ptr[vision::kRgbaToBgrOperation] = &(vision::RgbaToBgrOperation::from_json); - ops_ptr[vision::kRgbaToRgbOperation] = &(vision::RgbaToRgbOperation::from_json); - ops_ptr[vision::kRgbToBgrOperation] = &(vision::RgbToBgrOperation::from_json); - ops_ptr[vision::kRgbToGrayOperation] = &(vision::RgbToGrayOperation::from_json); - ops_ptr[vision::kRotateOperation] = &(vision::RotateOperation::from_json); - ops_ptr[vision::kSlicePatchesOperation] = &(vision::SlicePatchesOperation::from_json); - ops_ptr[vision::kSwapRedBlueOperation] = &(vision::SwapRedBlueOperation::from_json); - ops_ptr[vision::kToTensorOperation] = &(vision::ToTensorOperation::from_json); - ops_ptr[vision::kUniformAugOperation] = &(vision::UniformAugOperation::from_json); - ops_ptr[vision::kVerticalFlipOperation] = &(vision::VerticalFlipOperation::from_json); - ops_ptr[transforms::kFillOperation] = &(transforms::FillOperation::from_json); - ops_ptr[transforms::kOneHotOperation] = &(transforms::OneHotOperation::from_json); - ops_ptr[transforms::kTypeCastOperation] = &(transforms::TypeCastOperation::from_json); - ops_ptr[text::kToNumberOperation] = &(text::ToNumberOperation::from_json); - return ops_ptr; -} - -Status Serdes::ParseMindIRPreprocess(const std::vector &map_json_string, - std::vector> *data_graph) { - CHECK_FAIL_RETURN_UNEXPECTED(!map_json_string.empty(), "Invalid data, no json data in map_json_string."); - - const std::string process_column = "[\"image\"]"; - MS_LOG(WARNING) << "Only supports parse \"image\" column from dataset object."; - - nlohmann::json map_json; - try { - for (auto &json : map_json_string) { - map_json = nlohmann::json::parse(json); - if (map_json["input_columns"].dump() == process_column) { - break; - } - } - } catch (const std::exception &err) { - MS_LOG(ERROR) << "Invalid json content, failed to parse JSON data, error message: " << err.what(); - RETURN_STATUS_UNEXPECTED("Invalid json content, failed to parse JSON data."); - } - - if (map_json.empty()) { - MS_LOG(ERROR) << "Invalid json content, no JSON data found for given input column: " + process_column; - RETURN_STATUS_UNEXPECTED("Invalid json content, no JSON data found for given input column: " + process_column); - } - - while (map_json != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(map_json["op_type"] == "Map", "Invalid json content, this is not a MapOp."); - - std::vector> tensor_ops; - RETURN_IF_NOT_OK(ConstructTensorOps(map_json["operations"], &tensor_ops)); - if (map_json["input_columns"].dump() == process_column) { - std::vector op_names; - std::transform(tensor_ops.begin(), tensor_ops.end(), std::back_inserter(op_names), - [](const auto &op) { return op->Name(); }); - MS_LOG(INFO) << "Find valid preprocess operations: " << op_names; - data_graph->push_back(std::make_shared(tensor_ops)); - } - map_json = map_json["children"]; - } - - if (!data_graph->size()) { - MS_LOG(WARNING) << "Can not find any valid preprocess operation."; - } - - return Status::OK(); -} - -Status Serdes::UpdateOptimizedIRTreeJSON(nlohmann::json *serialized_json, - const std::map> &op_map) { - RETURN_UNEXPECTED_IF_NULL(serialized_json); - int32_t op_id = 0; - return RecurseUpdateOptimizedIRTreeJSON(serialized_json, &op_id, op_map); -} - -bool IsDatasetOpMatchIRNode(std::string_view ir_node_name, std::string_view dataset_op_name) { - // Helper function to match IR Node name to its dataset op name - if (ir_node_name == kSyncWaitNode) { - return dataset_op_name == kBarrierOp; - } else if (ir_node_name == kCifar10Node || ir_node_name == kCifar100Node) { - return dataset_op_name == "CifarOp"; - } else if (ir_node_name == kMindDataNode) { - return dataset_op_name == "MindRecordOp"; - } else if (ir_node_name == kRandomNode) { - return dataset_op_name == "RandomDataOp"; - } else if (ir_node_name == kTFRecordNode) { - return dataset_op_name == "TFReaderOp"; - } else if (ir_node_name == kIWSLT2016Node || ir_node_name == kIWSLT2017Node) { - return dataset_op_name == "IWSLTOp"; - } else { - // Generic way of matching, special cases handled above. Special cases will evolve over time. - return ir_node_name.substr(0, ir_node_name.find("Dataset")) == - dataset_op_name.substr(0, dataset_op_name.find("Op")); - } -} - -Status Serdes::RecurseUpdateOptimizedIRTreeJSON(nlohmann::json *serialized_json, int32_t *op_id, - const std::map> &op_map) { - RETURN_UNEXPECTED_IF_NULL(serialized_json); - RETURN_UNEXPECTED_IF_NULL(op_id); - - std::string ir_node_name = (*serialized_json)["op_type"]; - MS_LOG(INFO) << "Visiting IR Node: " << ir_node_name; - // Each IR Node should have a corresponding dataset node in the execution tree but the reverse is not necessarily true - while (!IsDatasetOpMatchIRNode(ir_node_name, op_map.find(*op_id)->second->Name())) { - // During the construction of execution tree, extra dataset nodes may have been inserted - // Skip dataset ops unless we get to the expected node - MS_LOG(INFO) << "\tSkipping dataset op: " << op_map.find(*op_id)->second->NameWithID(); - ++(*op_id); - CHECK_FAIL_RETURN_UNEXPECTED(*op_id < op_map.size(), "op_id is out of bounds"); - } - MS_LOG(INFO) << "\tMatch found for IR Node: " << ir_node_name - << " with dataset op: " << op_map.find(*op_id)->second->NameWithID(); - if (!op_map.find(*op_id)->second->inlined() && serialized_json->contains("num_parallel_workers") && - serialized_json->contains("connector_queue_size")) { - (*serialized_json)["num_parallel_workers"] = op_map.find(*op_id)->second->NumWorkers(); - (*serialized_json)["connector_queue_size"] = op_map.find(*op_id)->second->ConnectorCapacity(); - } - ++(*op_id); - auto num_children = (*serialized_json)["children"].size(); - for (int i = 0; i < static_cast(num_children); ++i) { - RETURN_IF_NOT_OK(RecurseUpdateOptimizedIRTreeJSON(&(*serialized_json)["children"][i], op_id, op_map)); - } - return Status::OK(); -} - -// In the current stage, there is a cyclic dependency between libmindspore.so and c_dataengine.so, -// we make a C function here and dlopen by libminspore.so to avoid linking explicitly, -// will be fix after decouling libminspore.so into multi submodules -extern "C" { -// ParseMindIRPreprocess_C has C-linkage specified, but returns user-defined type 'mindspore::Status' -// which is incompatible with C -void ParseMindIRPreprocess_C(const std::vector &dataset_json, - std::vector> *data_graph, Status *s) { - if (s != nullptr) { - Status ret = Serdes::ParseMindIRPreprocess(dataset_json, data_graph); - *s = Status(ret); - } -} -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/serdes.h b/mindspore-lite/minddata/dataset/engine/serdes.h deleted file mode 100644 index 46b33d5d0..000000000 --- a/mindspore-lite/minddata/dataset/engine/serdes.h +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_SERDES_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_SERDES_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/batch_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/concat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/project_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/rename_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/repeat_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/shuffle_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/skip_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/data_queue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/take_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/zip_node.h" - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/album_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/celeba_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar10_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/cifar100_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/clue_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/coco_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/csv_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/flickr_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/image_folder_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/manifest_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/mnist_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/text_file_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/tf_record_node.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/voc_node.h" - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" -#include "mindspore-lite/minddata/dataset/include/dataset/execute.h" -#include "mindspore-lite/minddata/dataset/include/dataset/iterator.h" -#include "mindspore-lite/minddata/dataset/include/dataset/samplers.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/include/dataset/vision.h" - -#include "mindspore-lite/minddata/dataset/kernels/py_func_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h" -#include "mindspore-lite/minddata/dataset/text/ir/kernels/text_ir.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \brief The Serdes class is used to serialize an IR tree into JSON string and dump into file if file name -/// specified. -class Serdes { - public: - /// \brief Constructor - Serdes() {} - - /// \brief default destructor - ~Serdes() = default; - - /// \brief function to serialize IR tree into JSON string and/or JSON file - /// \param[in] node IR node to be transferred - /// \param[in] filename The file name. If specified, save the generated JSON string into the file - /// \param[out] out_json The result json string - /// \return Status The status code returned - static Status SaveToJSON(std::shared_ptr node, const std::string &filename, nlohmann::json *out_json); - - /// \brief Function to update the parameters [num_parallel_workers, connector_queue_size] in the serialized JSON - /// object of the optimized IR tree - /// \param[in, out] serialized_json The optimized ir tree json node - /// \param[in] op_map An ID to DatasetOp mapping - static Status UpdateOptimizedIRTreeJSON(nlohmann::json *serialized_json, - const std::map> &op_map); - - /// \brief function to de-serialize JSON file to IR tree - /// \param[in] json_filepath input path of json file - /// \param[out] ds The deserialized dataset - /// \return Status The status code returned - static Status Deserialize(const std::string &json_filepath, std::shared_ptr *ds); - - /// \brief Helper function to construct IR tree, separate zip and other operations - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] ds Shared pointer of a DatasetNode object containing the deserialized IR tree - /// \return Status The status code returned - static Status ConstructPipeline(nlohmann::json json_obj, std::shared_ptr *ds); - - /// \brief Helper functions for creating sampler, separate different samplers and call the related function - /// \param[in] json_obj The JSON object to be deserialized - /// \param[out] sampler Deserialized sampler - /// \return Status The status code returned - static Status ConstructSampler(nlohmann::json json_obj, std::shared_ptr *sampler); - - /// \brief helper function to construct tensor operations - /// \param[in] json_obj json object of operations to be deserilized - /// \param[out] vector of tensor operation pointer - /// \return Status The status code returned - static Status ConstructTensorOps(nlohmann::json json_obj, std::vector> *result); - - /// \brief helper function to load tensor operations from dataset JSON and construct Execute object. - /// \param[in] map_json_string JSON string of dataset. - /// \param[out] data_graph Execute object contains tensor operations of map. - /// \return Status The status code returned. - static Status ParseMindIRPreprocess(const std::vector &map_json_string, - std::vector> *data_graph); - - /// \brief Helper function to save JSON to a file - /// \param[in] json_string The JSON string to be saved to the file - /// \param[in] file_name The file name - /// \param[in] pretty Flag to control pretty printing of JSON string to the file - /// \return Status The status code returned - static Status SaveJSONToFile(const nlohmann::json &json_string, const std::string &file_name, bool pretty = false); - - protected: - /// \brief Function to determine type of the node - dataset node if no dataset exists or operation node - /// \param[in] child_ds children datasets that is already created - /// \param[in] json_obj json object to read out type of the node - /// \param[out] ds Shared pointer of a DatasetNode object containing the deserialized IR tree - /// \return create new node based on the input dataset and type of the operation - static Status CreateNode(const std::shared_ptr &child_ds, nlohmann::json json_obj, - std::shared_ptr *ds); - - /// \brief Helper functions for creating dataset nodes, separate different datasets and call the related function - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] op_type type of dataset - /// \param[out] ds Shared pointer of a DatasetNode object containing the deserialized IR tree - /// \return Status The status code returned - static Status CreateDatasetNode(const nlohmann::json &json_obj, const std::string &op_type, - std::shared_ptr *ds); - - /// \brief Helper functions for creating operation nodes, separate different operations and call the related function - /// \param[in] json_obj The JSON object to be deserialized - /// \param[in] op_type type of dataset - /// \param[out] result Shared pointer of a DatasetNode object containing the deserialized IR tree - /// \return Status The status code returned - static Status CreateDatasetOperationNode(const std::shared_ptr &ds, const nlohmann::json &json_obj, - const std::string &op_type, std::shared_ptr *result); - - /// \brief Helper function to map the function pointers - /// \return map of key to function pointer - static std::map *operation)> - InitializeFuncPtr(); - - /// \brief Helper function to perform recursive DFS on the optimized IR tree and to match each IR node with its - /// corresponding dataset op - /// \param [in, out] serialized_json The optimized ir tree json node - /// \param [in, out] op_id The id in execution tree from where to continue the IR Node - DatasetOp matching search - /// \param [in] op_map An ID to DatasetOp mapping - static Status RecurseUpdateOptimizedIRTreeJSON(nlohmann::json *serialized_json, int32_t *op_id, - const std::map> &op_map); - - private: - static std::map *operation)> - func_ptr_; -}; - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_SERDES_H_ diff --git a/mindspore-lite/minddata/dataset/engine/tree_adapter.cc b/mindspore-lite/minddata/dataset/engine/tree_adapter.cc deleted file mode 100644 index a31b2e28d..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_adapter.cc +++ /dev/null @@ -1,841 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -#include -#endif - -#include "mindspore-lite/minddata/dataset/core/client.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/opt/optional/tensor_op_fusion_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/cache_transform_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/node_offload_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/post/repeat_pass.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/post/auto_worker_pass.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/engine/opt/post/generator_node_pass.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/opt/pre/add_skip_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/cache_validation_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/skip_pushdown_pass.h" -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -#include "mindspore-lite/minddata/dataset/engine/datasetops/send_bridge_op.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/receive_bridge_op.h" -#include "mindspore-lite/minddata/dataset/core/shared_memory_queue.h" -#include "mindspore-lite/minddata/dataset/core/message_queue.h" -#include "mindspore-lite/minddata/dataset/util/gil_scoped.h" -#include "mindspore-lite/minddata/dataset/util/ftok_key.h" -#endif -#ifdef WITH_BACKEND -#include "runtime/hardware_abstract/device_context/device_context.h" -#include "runtime/hardware_abstract/device_context/device_context_manager.h" -#include "utils/ms_context.h" -#endif - -namespace mindspore { -namespace dataset { -TreeAdapter::TreeAdapter(UsageFlag usage) - : usage_(usage), - launched_(false), - tree_state_(kCompileStateInit), - optimize_(common::GetEnv("OPTIMIZE") == "true"), - - // Initialize profiling parameters - cur_batch_num_(0), - cur_connector_size_(0), - cur_connector_capacity_(0) { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - parent_process_id_ = -1; - process_id_ = getpid(); - sub_process_id_ = -1; - - std::string env_independent_dataset = common::GetEnv("MS_INDEPENDENT_DATASET"); - transform(env_independent_dataset.begin(), env_independent_dataset.end(), env_independent_dataset.begin(), ::tolower); - - if (env_independent_dataset == "true") { - independent_dataset_ = true; - } else { - independent_dataset_ = false; - } - - if (independent_dataset_ && GlobalContext::config_manager()->get_debug_mode()) { - MS_LOG(WARNING) << "The independent dataset process mode does not support debugging. " - << "The independent dataset process mode will be disabled."; - independent_dataset_ = false; - } -#endif -} - -Status TreeAdapter::PrePass(const std::shared_ptr &ir) { - RETURN_UNEXPECTED_IF_NULL(ir); - // Vector of actions in pre-pass phase - std::vector> actions; - - MS_LOG(INFO) << "Running pre pass loops."; - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - if (usage_ == kDeReset) { - (void)actions.emplace_back(std::make_unique()); - if (GlobalContext::config_manager()->fast_recovery()) { - (void)actions.emplace_back(std::make_unique()); - } - } - (void)actions.emplace_back(std::make_unique()); - if (usage_ == kDeGetter) { - (void)actions.emplace_back(std::make_unique()); - } -#ifndef ENABLE_ANDROID - (void)actions.emplace_back(std::make_unique()); - - std::unique_ptr offload = std::make_unique(); - // Checks nodes for offload removal - bool offload_mod = false; - // Checks ir_tree nodes for offload removal - RETURN_IF_NOT_OK(offload->Run(ir, &offload_mod)); - // Creates JSON object of offload nodes. - offload_json_ = offload->GetOffloadJson(); -#endif - // Apply pre-pass actions - for (auto &action : actions) { - auto m = false; - RETURN_IF_NOT_OK(action->Run(ir, &m)); - } - - MS_LOG(INFO) << "Pre pass offload complete."; - return Status::OK(); -} - -Status TreeAdapter::Optimize(const std::shared_ptr &ir) { - RETURN_UNEXPECTED_IF_NULL(ir); - // Vector of optimizations - std::vector> optimizations; - MS_LOG(INFO) << "Running optimization pass loops"; -#ifndef ENABLE_ANDROID - (void)optimizations.emplace_back(std::make_unique()); -#endif - // Apply optimization pass actions - for (auto &optimization : optimizations) { - bool modified = false; - RETURN_IF_NOT_OK(optimization->Run(ir, &modified)); - } - MS_LOG(INFO) << "Optimization pass complete."; - return Status::OK(); -} - -Status TreeAdapter::PostPass(const std::shared_ptr &ir) { - RETURN_UNEXPECTED_IF_NULL(ir); - // Vector of actions in post-pass phase - std::vector> actions; - MS_LOG(INFO) << "Running post pass loops."; - - // AutoWorkerPass should ideally precede CacheTransForm Pass to avoid complications of the setting - if (GlobalContext::config_manager()->auto_num_workers() && usage_ == kDeIterator) { - // skip this for getter pass - (void)actions.emplace_back(std::make_unique()); - } -#ifdef ENABLE_PYTHON - (void)actions.emplace_back(std::make_unique()); -#endif -#ifndef ENABLE_ANDROID - (void)actions.emplace_back(std::make_unique()); -#endif - // We will gradually move RepeatPass from ExecutionTree::PrepareTreePostAction to here. - - for (auto &action : actions) { - auto m = false; - RETURN_IF_NOT_OK(action->Run(ir, &m)); - } - MS_LOG(INFO) << "Post passes complete."; - return Status::OK(); -} - -Status TreeAdapter::BuildExecutionTreeRecur(const std::shared_ptr &ir, - std::shared_ptr *const op) { - RETURN_UNEXPECTED_IF_NULL(ir); - RETURN_UNEXPECTED_IF_NULL(op); - RETURN_UNEXPECTED_IF_NULL(tree_); - // Build the DatasetOp ExecutionTree from the optimized IR tree - std::vector> ops; - RETURN_IF_NOT_OK(ir->Build(&ops)); - - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build node: " + ir->Name()); - - (*op) = ops.front(); // return the first op to be added as child by the caller of this function - RETURN_IF_NOT_OK(tree_->AssociateNode(*op)); - - for (size_t i = 1; i < ops.size(); i++) { - RETURN_IF_NOT_OK(tree_->AssociateNode(ops[i])); - RETURN_IF_NOT_OK(ops[i - 1]->AddChild(ops[i])); - } - - // Build the children of IR, once they return, add the return value to *op - for (const std::shared_ptr &child_ir : ir->Children()) { - std::shared_ptr child_op; - RETURN_IF_NOT_OK(BuildExecutionTreeRecur(child_ir, &child_op)); - RETURN_IF_NOT_OK(ops.back()->AddChild(child_op)); // append children to the last of ops - } - - return Status::OK(); -} - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -Status TreeAdapter::InsertSendReceiveOp() { - RETURN_UNEXPECTED_IF_NULL(tree_); - - // get the key - key_t key = -1; - RETURN_IF_NOT_OK(GetKey(&key)); - - // create shared memory queue - SharedMemoryQueue send_queue(key); - send_queue.SetReleaseFlag(false); - MS_LOG(INFO) << "Create shared memory queue by key: " << key; - - // create message queue id first - int msg_queue_id = msgget(key, IPC_CREAT | 0600); - if (msg_queue_id < 0) { - RETURN_STATUS_UNEXPECTED("Create send message by key: " + std::to_string(key) + - " failed. Errno: " + std::to_string(errno)); - } - - // create message queue - MessageQueue msg_queue(key, msg_queue_id); - msg_queue.SetReleaseFlag(false); - MS_LOG(INFO) << "Create send message queue id: " << std::to_string(msg_queue_id) << " by key: " << std::to_string(key) - << " success."; - - std::shared_ptr data_queue_op = nullptr; - std::shared_ptr data_queue_op_next = nullptr; - // the tree is reversed by begin iterator, get the data_queue_op and its next op - for (auto itr = tree_->begin(); itr != tree_->end(); ++itr) { - if (itr->Name() == kDeviceQueueOp) { - data_queue_op = itr.get(); - data_queue_op_next = (--itr).get(); - break; - } - } - - if (data_queue_op != nullptr) { - // dataset sink mode, the tree will be converted - // from: - // xxDataset -> map -> batch -> data_queue - // to: - // xxDataset -> map -> batch -> send -> receive -> data_queue - CHECK_FAIL_RETURN_UNEXPECTED(data_queue_op_next != nullptr, "The next op of DataQueueOp is nullptr."); - auto send_bridge_op = std::make_shared(3, send_queue, msg_queue); - RETURN_IF_NOT_OK(tree_->AssociateNode(send_bridge_op)); - RETURN_IF_NOT_OK(send_bridge_op->AddChild(data_queue_op_next)); - RETURN_IF_NOT_OK(tree_->AssignRoot(send_bridge_op)); - - auto receive_bridge_op = std::make_shared(3, send_queue, msg_queue); - RETURN_IF_NOT_OK(tree_->AssociateNode(receive_bridge_op)); - RETURN_IF_NOT_OK(receive_bridge_op->AddChild(tree_->root())); - RETURN_IF_NOT_OK(tree_->AssignRoot(receive_bridge_op)); - - RETURN_IF_NOT_OK(data_queue_op->RemoveChildren()); - RETURN_IF_NOT_OK(data_queue_op->AddChild(tree_->root())); - RETURN_IF_NOT_OK(tree_->AssignRoot(data_queue_op)); - } else { - // dataset feed mode, the tree will be converted - // from: - // xxDataset -> map -> ... -> batch - // to: - // xxDataset -> map -> ... -> batch -> send -> receive - auto send_bridge_op = std::make_shared(3, send_queue, msg_queue); - RETURN_IF_NOT_OK(tree_->AssociateNode(send_bridge_op)); - RETURN_IF_NOT_OK(send_bridge_op->AddChild(tree_->root())); - RETURN_IF_NOT_OK(tree_->AssignRoot(send_bridge_op)); - - auto receive_bridge_op = std::make_shared(3, send_queue, msg_queue); - RETURN_IF_NOT_OK(tree_->AssociateNode(receive_bridge_op)); - RETURN_IF_NOT_OK(receive_bridge_op->AddChild(tree_->root())); - RETURN_IF_NOT_OK(tree_->AssignRoot(receive_bridge_op)); - } - - return Status::OK(); -} - -Status TreeAdapter::SplitBySendReceiveOp() { - // split the tree_ to two part - // part1. xxDataset -> map -> ... -> batch -> send - // part2. receive -> iter / data_queue - receive_tree_ = std::make_unique(); - send_tree_ = std::make_unique(); - - // dis-assign the root from tree_ and assign the root to the receive tree - std::shared_ptr op = tree_->root(); - RETURN_IF_NOT_OK(tree_->root()->Remove()); // del the head from tree_ - RETURN_IF_NOT_OK(receive_tree_->AssociateNode(op)); // add the head to receive_tree_ - RETURN_IF_NOT_OK(receive_tree_->AssignRoot(op)); // assign the receive_tree_ root - - // current op in receive_tree_ - std::shared_ptr receive_op = receive_tree_->root(); - - while (tree_ != nullptr && tree_->root() && tree_->root()->Children().size() != 0) { - // move the last tree to send_tree_ - if (tree_->root()->Name() == kSendBridgeOp) { - send_tree_ = std::move(tree_); - break; - } - - op = tree_->root(); - RETURN_IF_NOT_OK(tree_->root()->Remove()); - - // continue add the op to the receive tree - RETURN_IF_NOT_OK(receive_tree_->AssociateNode(op)); - RETURN_IF_NOT_OK(receive_op->AddChild(op)); - receive_op = op; - } - - return Status::OK(); -} -#endif - -Status TreeAdapter::Build(const std::shared_ptr &root_ir, int64_t init_epoch) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - uint64_t start_time = GetSyscnt(); - // Create ExecutionTree - tree_ = std::make_unique(); - - // Build the Execution tree from the child of the IR root node, which represent the root of the input IR tree - std::shared_ptr root_op; - RETURN_IF_NOT_OK(BuildExecutionTreeRecur(root_ir->Children()[0], &root_op)); - RETURN_IF_NOT_OK(tree_->AssignRoot(root_op)); - - if (usage_ == kDeReset) { - RETURN_IF_NOT_OK(AdjustReset(init_epoch)); - } - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (independent_dataset_) { - MS_LOG(INFO) << "The original execution tree: " << *tree_; - - // add the SendBridgeOp and ReceiveBridgeOp to the tree - RETURN_IF_NOT_OK(InsertSendReceiveOp()); - - MS_LOG(INFO) << "After add SendBridgeOp and ReceiveBridgeOp, the execution tree: " << *tree_; - - // split the tree to send_tree_ and receive_tree_ - RETURN_IF_NOT_OK(SplitBySendReceiveOp()); - - MS_LOG(INFO) << "After split the execution tree, the send tree: " << *send_tree_; - MS_LOG(INFO) << "After split the execution tree, the receive tree: " << *receive_tree_; - - // Prepare the tree - RETURN_IF_NOT_OK(send_tree_->Prepare()); - RETURN_IF_NOT_OK(receive_tree_->Prepare()); - - // After the tree is prepared, the col_name_id_map can safely be obtained - column_name_map_ = receive_tree_->root()->column_name_id_map(); - } else { - // Prepare the tree - RETURN_IF_NOT_OK(tree_->Prepare()); - - // After the tree is prepared, the col_name_id_map can safely be obtained - column_name_map_ = tree_->root()->column_name_id_map(); - } -#else - // Prepare the tree - RETURN_IF_NOT_OK(tree_->Prepare()); - - // After the tree is prepared, the col_name_id_map can safely be obtained - column_name_map_ = tree_->root()->column_name_id_map(); -#endif - RETURN_IF_NOT_OK(CollectPipelineInfo("Pipeline", "Build", start_time)); - return Status::OK(); -} - -Status TreeAdapter::Compile(const std::shared_ptr &input_ir, int32_t num_epochs, int64_t global_step, - int64_t dataset_size, bool independent_dataset) { - RETURN_UNEXPECTED_IF_NULL(input_ir); - VLOG_FLOW("Dataset Pipeline TreeAdapter Compile started."); - uint64_t start_time = GetSyscnt(); - input_ir_ = input_ir; - tree_state_ = kCompileStateIRGraphBuilt; - MS_LOG(INFO) << "Input plan:" << '\n' << *input_ir << '\n'; - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - // update the independent dataset flag - // MS_INDEPENDENT_DATASET : parameter -> flag - // true true true - // true false false - // false true false - // false false false - independent_dataset_ = independent_dataset && independent_dataset_; - if (independent_dataset_ == true) { - MS_LOG(INFO) << "Environment MS_INDEPENDENT_DATASET is true, dataset will be ran in subprocess."; - } else { - MS_LOG(INFO) << "Environment MS_INDEPENDENT_DATASET is false, dataset will be ran in main process."; - } -#endif - - // Clone the input IR tree and insert under the root node - // Create a root node to host the new copy of the input IR tree - // This is done so that the compilation will process and modify the tree - // without changing the tree associated with the user code. - // The tree from the user code is permitted to form a graph where any node - // is consumed by more than one parent. However, this cloning process here - // will break the graph into a tree by copying each consumption of a node into a new copy. - bool m = false; - DeepCopyPass cloning_tree; - RETURN_IF_NOT_OK(cloning_tree.Run(input_ir, &m)); - std::shared_ptr root_ir = cloning_tree.Root(); - root_ir->SetNumEpochs(num_epochs); - root_ir->SetStep(global_step); - root_ir->SetDatasetSize(dataset_size); - - tree_state_ = kCompileStateIRTreeCloned; - MS_LOG(INFO) << "Plan before optimization:" << '\n' << *root_ir << '\n'; - - // Pre-pass of the IR tree - RETURN_IF_NOT_OK(PrePass(root_ir)); - - // Optional phase of optimization - if (optimize_) { - RETURN_IF_NOT_OK(Optimize(root_ir)); - } - - // Post-pass of the IR tree - RETURN_IF_NOT_OK(PostPass(root_ir)); - - tree_state_ = kCompileStateOptimized; - MS_LOG(INFO) << "Plan after optimization:" << '\n' << *root_ir << '\n'; - // Remember the root node - root_ir_ = root_ir; - - int64_t init_epoch = dataset_size != -1 ? global_step / dataset_size : 0; - RETURN_IF_NOT_OK(Build(root_ir_, init_epoch)); - tree_state_ = kCompileStateReady; - RETURN_IF_NOT_OK(CollectPipelineInfo("Pipeline", "Compile", start_time)); - VLOG_FLOW("Dataset Pipeline TreeAdapter Compile finished."); - return Status::OK(); -} - -Status TreeAdapter::AdjustReset(const int64_t epoch_num) { - if (GlobalContext::config_manager()->fast_recovery() && epoch_num > 0) { - MS_LOG(INFO) << "Adjusting dataset pipeline for failover reset to start on epoch: " << (epoch_num + 1); - for (auto op = tree_->begin(); op != tree_->end(); ++op) { - RETURN_IF_NOT_OK(op->SetEpoch(epoch_num)); - } - } - return Status::OK(); -} - -Status TreeAdapter::CheckTreeIfNull() { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (tree_ == nullptr && (send_tree_ == nullptr || receive_tree_ == nullptr)) { - RETURN_STATUS_UNEXPECTED("Tree tree_ && (send_tree_ || receive_tree_) is a nullptr."); - } -#else - if (tree_ == nullptr) { - RETURN_STATUS_UNEXPECTED("Tree tree_ is a nullptr."); - } -#endif - return Status::OK(); -} - -Status TreeAdapter::GetNext(TensorRow *row) { - RETURN_IF_NOT_OK(CheckTreeIfNull()); - RETURN_UNEXPECTED_IF_NULL(row); - uint64_t build_start_time = GetSyscnt(); - row->clear(); // make sure row is empty - - // When cur_db_ is a nullptr, it means this is the first call to get_next, launch ExecutionTree - if (!launched_) { - RETURN_IF_NOT_OK(Launch()); - } - // Record profiling info - uint64_t start_time = 0; - if (tracing_ != nullptr) { - start_time = ProfilingTime::GetCurMilliSecond(); - } - - RETURN_IF_NOT_OK(tree_->root()->GetNextRow(row)); // first buf can't be eof or empty buf with none flag - if (row->eoe()) { // return empty tensor if 1st buf is a ctrl buf (no rows) - MS_LOG(INFO) << "End of data iteration. cur_batch_num_: " << cur_batch_num_; - if (profiling_manager_ != nullptr) { - tree_->SetEpochEnd(); - profiling_manager_->RecordEndOfEpoch(cur_batch_num_); - } - RETURN_IF_NOT_OK( - CollectPipelineInfo("Pipeline", "GetNext", build_start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); - } - if (row->eof()) { - tree_->SetFinished(); - std::string err = "EOF buffer encountered. User tries to fetch data beyond the specified number of epochs."; - RETURN_STATUS_UNEXPECTED(err); - } - - // Record profiling info - if (tracing_ != nullptr) { - uint64_t end_time = ProfilingTime::GetCurMilliSecond(); - cur_batch_num_++; - cur_connector_size_ = tree_->root()->ConnectorSize(); - cur_connector_capacity_ = tree_->root()->ConnectorCapacity(); - // push time is 0ms in dataset iterator since no devices are involved - tracing_->Record(TIME, TDT_PUSH_TIME, cur_batch_num_, 0, end_time); - tracing_->Record(TIME, BATCH_TIME, cur_batch_num_, end_time - start_time, end_time); - tracing_->Record(TIME, PIPELINE_TIME, cur_batch_num_, end_time - start_time, end_time); - tracing_->Record(CONNECTOR_DEPTH, cur_connector_capacity_, cur_batch_num_, cur_connector_size_, end_time); - } - RETURN_IF_NOT_OK(CollectPipelineInfo("Pipeline", "GetNext", build_start_time, {{"TensorRowFlags", row->FlagName()}})); - return Status::OK(); -} - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -void TreeAdapter::SubprocessExit(int exit_code) { - // get the newest message queue and shared memory queue - auto message_queue = dynamic_cast(tree_->root().get())->GetMessageQueue(); - auto shared_memmory_queue = dynamic_cast(tree_->root().get())->GetSharedMemoryQueue(); - - // If the main process is killed, it will cause the SendBridgeOp MsgRcv to hang, - // so it is necessary to release the message queue first. - // release the message queue - message_queue.SetReleaseFlag(true); - - // this will break hung by MsgRcv which is in SendBridgeOp / ReceiveBridgeOp - message_queue.ReleaseQueue(); - - // interrupt all the pipeline thread - (void)tree_->AllTasks()->interrupt_all(); - - // wait all the thread exit - MS_LOG(INFO) << "[Independent Dataset Process] Begin waiting for all pipeline threads exit."; - auto ret = tree_->AllTasks()->join_all(Task::WaitFlag::kBlocking); - if (ret != Status::OK()) { - MS_LOG(ERROR) << ret.ToString(); - } - MS_LOG(INFO) << "[Independent Dataset Process] End waiting for all pipeline threads exit."; - - // wait for python multiprocessing exit - for (auto itr = tree_->begin(); itr != tree_->end(); ++itr) { - if (itr->Name() == "GeneratorOp") { - auto generator_op = dynamic_cast(itr.get().get()); - if (generator_op->Terminate() != Status::OK()) { - MS_LOG(ERROR) << "Terminate GeneratorOp python multiprocessing failed."; - } - } - if (itr->Name() == kMapOp) { - auto map_op = dynamic_cast(itr.get().get()); - if (map_op->Terminate() != Status::OK()) { - MS_LOG(ERROR) << "Terminate MapOp python multiprocessing failed."; - } - } - if (itr->Name() == kBatchOp) { - auto batch_op = dynamic_cast(itr.get().get()); - if (batch_op->Terminate() != Status::OK()) { - MS_LOG(ERROR) << "Terminate BatchOp python multiprocessing failed."; - } - } - } - MS_LOG(INFO) << "[Independent Dataset Process] The child processes of the independent process have all exited."; - - // the message queue should be released in main process ReceiveBridgeOp, so just release the shared memory queue - ret = shared_memmory_queue.ReleaseCurrentShm(); - if (ret != Status::OK()) { - MS_LOG(ERROR) << ret.ToString(); - } - - MS_LOG(INFO) << "[Independent Dataset Process] The shared memory had been released."; - - // need acquire gil before destroy device - GilAcquireWithCheck gil_acquire_with_check; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // If the main process has exited, the independent dataset process does not need to release the device. - auto ms_context = MsContext::GetInstance(); - if (ms_context != nullptr) { - device::DeviceContextKey device_context_key = {ms_context->get_param(MS_CTX_DEVICE_TARGET), - ms_context->get_param(MS_CTX_DEVICE_ID)}; - auto device_context = device::DeviceContextManager::GetInstance().GetDeviceContext(device_context_key.device_name_); - - // destroy the device context when independent dataset exit - if (ms_context->get_param(MS_CTX_DEVICE_TARGET) == kAscendDevice && device_context && - device_context->initialized()) { - // Destroy the device context - device_context->Destroy(); - } - MS_LOG(INFO) << "Destroy device context successful."; - } else { - MS_LOG(ERROR) << "Get ms context failed by MsContext::GetInstance()"; - } -#endif - - // release the gil - py::gil_scoped_release release; - - // independent will exit - _exit(exit_code); -} - -Status TreeAdapter::LaunchSubprocess() { - // get the message queue id - if (tree_->root()->Name() != kSendBridgeOp) { - MS_LOG(EXCEPTION) << "The send_tree_ root is not SendBridgeOp."; - } - auto message_queue = dynamic_cast(tree_->root().get())->GetMessageQueue(); - - parent_process_id_ = getppid(); - process_id_ = getpid(); - sub_process_id_ = -1; - auto tid = std::this_thread::get_id(); - std::stringstream ss; - ss << tid; - - std::string log_prefix = "[Independent Dataset Process] Process pid: " + std::to_string(process_id_) + - ", parent pid: " + std::to_string(parent_process_id_) + ", thread id: " + ss.str(); - - // execute the detached dataset in the sub-process - MS_LOG(INFO) << log_prefix << " is started successfully."; - - auto ret = tree_->Launch(); - if (ret != Status::OK()) { - // here should prompt error because it's in subprocess - MS_LOG(ERROR) << log_prefix << ". Launch failed."; - - // got the first error from pipeline op - auto task_error = tree_->AllTasks()->GetTaskErrorIfAny(); - if (task_error != Status::OK()) { - ret = task_error; - } - - // release the message queue - message_queue.SetReleaseFlag(true); - - // send the INDEPENDENT error message to main process - if (message_queue.SerializeStatus(ret) != Status::OK()) { - MS_LOG(EXCEPTION) << log_prefix << " serialize Status failed."; - } - RETURN_IF_NOT_OK(message_queue.MsgSnd(kWorkerErrorMsg)); - - // waiting for the main process get the message - sleep(kMonitorInterval * kSleepDelays); - - SubprocessExit(-1); - } - - launched_ = true; - - // The the subprocess should be alive - const int main_thread_log_interval = 10; - const int main_thread_sleep_interval = 1; - time_t start = time(nullptr); - while (!tree_->isFinished()) { - time_t end = time(nullptr); - if (difftime(end, start) > main_thread_log_interval) { - MS_LOG(INFO) << log_prefix << " is alive. Its status is normal. Sleeping ..."; - start = time(nullptr); - } - - sleep(main_thread_sleep_interval); - - // got error from dataset pipeline - ret = tree_->AllTasks()->GetTaskErrorIfAny(); - if (ret != Status::OK()) { - MS_LOG(INFO) << log_prefix << ". Got error info in independent dataset pipeline."; - - // release the message queue - message_queue.SetReleaseFlag(true); - - // send the INDEPENDENT error message to main process - if (message_queue.SerializeStatus(ret) != Status::OK()) { - MS_LOG(EXCEPTION) << log_prefix << " serialize Status failed."; - } - RETURN_IF_NOT_OK(message_queue.MsgSnd(kWorkerErrorMsg)); - - // waiting for the main process get the message - sleep(kMonitorInterval * kSleepDelays); - - SubprocessExit(-1); - } - - // the message queue had been released by main process - MessageQueue::State state = dynamic_cast(tree_->root().get())->MessageQueueState(); - if (state == MessageQueue::State::kReleased) { - MS_LOG(INFO) << log_prefix << ". Message queue had been released by main process."; - - SubprocessExit(0); - } - - // get message ReceiveBridgeOp finished from main process, indicate that iterator / to_device is finished - if (message_queue.MsgRcv(kMasterReceiveBridgeOpFinishedMsg, IPC_NOWAIT) > 0) { - MS_LOG(INFO) << log_prefix - << ". Got ReceiveBridgeOp finished message from main process. Current process will exit."; - - SubprocessExit(0); - } - - // the parent had been closed - if (getppid() != parent_process_id_) { - MS_LOG(INFO) << log_prefix << ". Main process: " << std::to_string(parent_process_id_) - << " had been closed. Current process: " << std::to_string(process_id_) << " will exit too."; - - SubprocessExit(0); - } - } - - SubprocessExit(-1); - - // The process may not run to this point. exit() will cause core, so we use _exit() - _exit(-1); -} -#endif - -Status TreeAdapter::Launch() { - VLOG_FLOW("Dataset Pipeline launched."); - RETURN_IF_NOT_OK(CheckTreeIfNull()); - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - if (independent_dataset_) { - // move the send_tree_ to tree_ and launch it - tree_ = std::move(send_tree_); - - // begin hold gil - MS_LOG(INFO) << "[Main Dataset Process] Begin acquire gil. Current Py_IsInitialized: " << Py_IsInitialized() - << ", PyGILState_Check: " << PyGILState_Check(); - GilAcquireWithCheck gil_acquire_with_check; - PyOS_BeforeFork(); - - // launch the sub-process to detach dataset with send - pid_t fpid = fork(); - if (fpid < 0) { // fork sub-process failed - RETURN_STATUS_UNEXPECTED("Create an independent dataset process failed."); - } else if (fpid == 0) { // in sub-process - PyOS_AfterFork_Child(); - - // get the message queue - if (tree_->root()->Name() != kSendBridgeOp) { - MS_LOG(EXCEPTION) << "The send_tree_ root is not SendBridgeOp."; - } - auto message_queue = dynamic_cast(tree_->root().get())->GetMessageQueue(); - - // the subprocess had been launched - RETURN_IF_NOT_OK(message_queue.MsgSnd(kSubprocessReadyMsg)); - RETURN_IF_NOT_OK(message_queue.MsgRcv(kMainprocessReadyMsg)); - - // set the seed for independent dataset process - uint32_t seed = GlobalContext::config_manager()->seed(); - if (seed != std::mt19937::default_seed) { - py::module::import("mindspore.dataset.core.config").attr("set_seed")(seed); - } - - // no need to start new subprocess in independent dataset process - (void)common::SetEnv("MS_INDEPENDENT_DATASET", "False"); - - // release the gil in child process - MS_LOG(INFO) << "[Independent Dataset Process] Begin release gil. Current Py_IsInitialized: " - << Py_IsInitialized() << ", PyGILState_Check: " << PyGILState_Check(); - py::gil_scoped_release release; - MS_LOG(INFO) << "[Independent Dataset Process] End release gil. Current Py_IsInitialized: " << Py_IsInitialized() - << ", PyGILState_Check: " << PyGILState_Check(); - if (PyGILState_Check()) { - MS_LOG(ERROR) << "[Independent Dataset Process] PyGILState_Check: " << PyGILState_Check() - << ", it should be 0."; - _exit(-1); - } - - prctl(PR_SET_NAME, "independent_dataset_process"); // set the thread name - - // launch the independent dataset process - RETURN_IF_NOT_OK(LaunchSubprocess()); - } - - PyOS_AfterFork_Parent(); - - // In the main thread - sub_process_id_ = fpid; - MS_LOG(INFO) << "[Main Dataset Process] Process pid: " << std::to_string(process_id_) - << ", sub-process pid: " << std::to_string(sub_process_id_); - - // move the receive_tree_ to tree_ and launch it - tree_ = std::move(receive_tree_); - - // get the receive_bridge_op - // 1. get the message queue and make sure the subprocess had been forked and response to the subprocess - // 2. set the subprocess id to it - for (auto itr = tree_->begin(); itr != tree_->end(); ++itr) { - if (itr->Name() == kReceiveBridgeOp) { - // get the message queue - auto message_queue = dynamic_cast(itr.get().get())->GetMessageQueue(); - - // make sure the subprocess had been forked and response to the subprocess - RETURN_IF_NOT_OK(message_queue.MsgRcv(kSubprocessReadyMsg)); - RETURN_IF_NOT_OK(message_queue.MsgSnd(kMainprocessReadyMsg)); - - // set the subprocess id to it - auto receive_bridge_op = dynamic_cast(itr.get().get()); - receive_bridge_op->SetSubprocessID(sub_process_id_); - break; - } - } - MS_LOG(INFO) << "[Main Datast Process] Begin release gil. Current Py_IsInitialized: " << Py_IsInitialized() - << ", PyGILState_Check: " << PyGILState_Check(); - } -#endif - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) && !defined(ENABLE_ANDROID) - if (!independent_dataset_) { - // set num threads of opencv only for main process - int32_t thread_num = get_nprocs(); - if (thread_num == 0) { - std::string err_msg = "Invalid thread number, got 0."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - constexpr int32_t max_cv_threads_cnt = 8; - cv::setNumThreads(thread_num > max_cv_threads_cnt ? max_cv_threads_cnt : thread_num); - } -#endif - - RETURN_IF_NOT_OK(tree_->Launch()); - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) && !defined(ENABLE_ANDROID) - if (independent_dataset_) { - // ignore the SIGCHLD, the independent dataset process will exit successful without to be a defunct status - signal(SIGCHLD, SIG_IGN); - } -#endif - - launched_ = true; - return Status::OK(); -} - -nlohmann::json TreeAdapter::GetOffloadJson() { return offload_json_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/tree_adapter.h b/mindspore-lite/minddata/dataset/engine/tree_adapter.h deleted file mode 100644 index 2bb9da3bc..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_adapter.h +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" -#include "mindspore-lite/minddata/dataset/engine/perf/auto_tune.h" -#include "mindspore-lite/minddata/dataset/engine/perf/dataset_iterator_tracing.h" - -namespace mindspore { -namespace dataset { -class DatasetNode; -class TreeModifier; -class ToDevice; -class IteratorConsumer; - -class TreeAdapter { - friend ProfilingManager; - friend TreeConsumer; - friend ToDevice; - friend IteratorConsumer; - friend AutoTune; - friend TreeModifier; - - public: - // this flag is used to indicate the purpose of the creation of this tree adapter (type of the tree_consumer). - // Currently there are 3 types of consumer, Iterator, Getter and TDT/Vocab/Save ... - // To avoid premature optimization, the last type (TDT/Vocab/Save) is regarded as Iterator for now. - enum UsageFlag { kDeIterator = 0, kDeGetter = 1, kDeReset = 2 }; - - explicit TreeAdapter(UsageFlag usage = kDeIterator); - - ~TreeAdapter() = default; - - // This function performs syntax checking, semantics checking, optimizes, and then builds - // the Execution tree. - Status Compile(const std::shared_ptr &input_ir, int32_t num_epochs = -1, int64_t global_step = 0, - int64_t dataset_size = -1, bool independent_dataset = false); - - // Return the root node of the IR after cloned from the parsed IR tree - std::shared_ptr RootIRNode() const { return root_ir_; } - - const ExecutionTree *GetExecutionTree() const { return tree_.get(); } - - // This is the main method TreeConsumer uses to interact with TreeAdapter - // 1. GetNext will Launch() the ExeTree on its first call by iterator (tree is already prepared) - // 2. GetNext will return empty row when eoe/eof is obtained - Status GetNext(TensorRow *); - - // unique_ptr overloads operator bool(), will return false if it doesn't manage an object - // This is needed by Iterator to get data by 'GetNext'. - std::weak_ptr GetRoot() const { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - return tree_ ? tree_->root() : receive_tree_->root(); -#else - return tree_ ? tree_->root() : nullptr; -#endif - } - - // This function will return the column_name_map once BuildAndPrepare() is called - std::unordered_map GetColumnNameMap() const { return column_name_map_; } - - // This function returns the TaskGroup associated with ExeTree. This is needed by DeviceQueueConsumer - // to be able to launch a thread. BuildAndPrepare needs to be called before this function - TaskGroup *const AllTasks() const { return tree_ ? tree_->AllTasks() : nullptr; } - - Status Launch(); - - // Set optional optimization pass - void SetOptimize(bool value) { optimize_ = value; } - - // Optional optimizations status - bool OptimizationEnabled() const { return optimize_; } - - // Return Offload Json - nlohmann::json GetOffloadJson(); - /// \brief Setter for Profiling Manager - Status SetProfilingManagerPtr(const std::shared_ptr &profiling_manager, - std::shared_ptr tracing_node = nullptr) { - profiling_manager_ = profiling_manager; - if (tracing_node != nullptr) { - tracing_ = std::dynamic_pointer_cast(tracing_node); - } - return Status::OK(); - } - - /// \brief Getter for profiling manager, no ownership - ProfilingManager *GetProfilingManager() { return profiling_manager_.get(); } - - protected: - // Run the mandatory pass checking the syntax and semantics of the IR tree - Status PrePass(const std::shared_ptr &ir); - - // Run the optional optimization pass on the IR tree - static Status Optimize(const std::shared_ptr &ir); - - // Run the mandatory pass augmenting the IR tree - Status PostPass(const std::shared_ptr &ir); - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - // Insert SendBridgeOp and ReceiveBridgeOp to the tree - Status InsertSendReceiveOp(); - - // Split the tree to send tree and receive tree - Status SplitBySendReceiveOp(); -#endif - - Status CheckTreeIfNull(); - - // Build an Execution tree - Status Build(const std::shared_ptr &root_ir, int64_t init_epoch = 0); - - // This RECURSIVE function walks the (optimized) IR tree in DFS to build its corresponding Execution tree. - Status BuildExecutionTreeRecur(const std::shared_ptr &ir, std::shared_ptr *op); - - // Adjust the pipeline (eg, move rng_ forward) if in reset mode - Status AdjustReset(const int64_t epoch_num); - - std::unordered_map column_name_map_; - std::shared_ptr input_ir_; - std::shared_ptr root_ir_; -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - // Launch the subprocess - Status LaunchSubprocess(); - - // The subprocess will exit - void SubprocessExit(int exit_code); - - // the send tree, like: xxDataset -> map -> ... -> batch -> send - std::unique_ptr send_tree_; - // the receive tree, like: receive -> iterator / data_queue - std::unique_ptr receive_tree_; - - pid_t parent_process_id_; // parent process id - pid_t process_id_; // current process id - pid_t sub_process_id_; // sub process id - - bool independent_dataset_; // BuildVocabConsumer, DatasetSizeGetter, SaveToDisk consumer no need to start subprocess -#endif - - // 1. the tree holder, the send_tree_ will be moved to it and launched in independent dataset process - // 2. the tree holder, the receive_tree_ will be moved to it and launched in main dataset process - std::unique_ptr tree_; - bool optimize_; // Flag to enable optional optimization pass - std::shared_ptr profiling_manager_; // Profiling manager - std::shared_ptr tracing_; // trace profiling data - int32_t cur_batch_num_; // current batch number, used for profiling - int32_t cur_connector_size_; // current connector size of root op, used for profiling - int32_t cur_connector_capacity_; // current connector capacity of root op, used for profiling - UsageFlag usage_; // usage of this tree adapter (type of consumer) - bool launched_; - // State flags for the lifecycle of the tree - enum CompileState { - kCompileStateInit = 0, // The freshly initialized state - kCompileStateIRGraphBuilt, // User code has been parsed and its IR graph built - kCompileStateIRTreeCloned, // IR tree has been cloned from the IR graph - kCompileStateOptimized, // IR tree has been optimized - kCompileStateReady // Execution tree is generated from the optimized IR - }; - CompileState tree_state_; - nlohmann::json offload_json_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_H_ diff --git a/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.cc b/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.cc deleted file mode 100644 index b6328d2d4..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.cc +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/root_node.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pass.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/opt/pre/node_offload_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/post/repeat_pass.h" -#endif -#include "mindspore-lite/minddata/dataset/engine/opt/pre/debug_mode_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/deep_copy_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/getter_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/input_validation_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/insert_map_pass.h" -#include "mindspore-lite/minddata/dataset/engine/opt/pre/node_removal_pass.h" - -namespace mindspore { -namespace dataset { -TreeAdapterLite::TreeAdapterLite(UsageFlag usage) : root_(nullptr), usage_(usage) { - // Create ExecutionTree. - tree_ = std::make_unique(); -} - -Status TreeAdapterLite::BuildExecutionTreeRecur(std::shared_ptr ir, std::shared_ptr *const op) { - RETURN_UNEXPECTED_IF_NULL(ir); - RETURN_UNEXPECTED_IF_NULL(op); - // Build the DatasetOp ExecutionTree from the optimized IR tree - std::vector> ops; - RETURN_IF_NOT_OK(ir->Build(&ops)); - - CHECK_FAIL_RETURN_UNEXPECTED(!ops.empty(), "Unable to build node: " + ir->Name()); - - (*op) = ops.front(); // return the first op to be added as child by the caller of this function - - for (size_t i = 0; i < ops.size(); i++) { - RETURN_IF_NOT_OK(tree_->AssociateNode(ops[i])); - if (i > 0) { - RETURN_IF_NOT_OK(ops[i - 1]->AddChild(ops[i])); - } - } - - // Build the children of IR, once they return, add the return value to *op - for (const std::shared_ptr &child_ir : ir->Children()) { - std::shared_ptr child_op; - RETURN_IF_NOT_OK(BuildExecutionTreeRecur(child_ir, &child_op)); - RETURN_IF_NOT_OK(ops.back()->AddChild(child_op)); // append children to the last of ops - } - - return Status::OK(); -} - -Status TreeAdapterLite::BuildTree(std::shared_ptr root_ir) { - RETURN_UNEXPECTED_IF_NULL(root_ir); - // Build the Execution tree from the child of the IR root node, which represent the root of the input IR tree as a Top - // Node is added to IR tree. - RETURN_IF_NOT_OK(BuildExecutionTreeRecur(root_ir->Children()[0], &root_)); - RETURN_IF_NOT_OK(tree_->AssignRoot(root_)); - // Prepare the tree - RETURN_IF_NOT_OK(tree_->Prepare(true)); - return Status::OK(); -} - -Status TreeAdapterLite::GetNextRow(TensorRow *const row) { - RETURN_UNEXPECTED_IF_NULL(row); - RETURN_UNEXPECTED_IF_NULL(root_); - VLOG_FLOW("Dataset Pipeline TreeAdapterLite GetNextRow started."); - row->reset(); // Ensure TensorRow is empty and flags are initialized - RETURN_IF_NOT_OK(root_->GetNextRowPullMode(row)); - if (row->eoe()) { // return empty tensor if 1st buf is a ctrl buf (no rows) - MS_LOG(INFO) << "End of data iteration."; - return Status::OK(); - } - if (row->eof()) { - std::string err = "EOF buffer encountered. User tries to fetch data beyond the specified number of epochs."; - RETURN_STATUS_UNEXPECTED(err); - } - VLOG_FLOW("Dataset Pipeline TreeAdapterLite GetNextRow finished."); - return Status::OK(); -} - -Status TreeAdapterLite::PrePass(std::shared_ptr ir) { - RETURN_UNEXPECTED_IF_NULL(ir); - // Vector of actions in pre-pass phase - std::vector> actions; - MS_LOG(INFO) << "Prepare PrePass loops."; - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - (void)actions.emplace_back(std::make_unique()); - if (usage_ == kDeGetter) { - (void)actions.emplace_back(std::make_unique()); - } - - if (GlobalContext::config_manager()->get_debug_mode()) { - (void)actions.emplace_back(std::make_unique()); - } -#ifndef ENABLE_ANDROID - std::unique_ptr offload = std::make_unique(); - // Checks nodes for offload removal - bool offload_mod = false; - // Checks ir_tree nodes for offload removal - RETURN_IF_NOT_OK(offload->Run(ir, &offload_mod)); - // Creates JSON object of offload nodes. - offload_json_ = offload->GetOffloadJson(); -#endif - - // Apply pre-pass actions - for (size_t i = 0; i < actions.size(); i++) { - auto m = false; - RETURN_IF_NOT_OK(actions[i]->Run(ir, &m)); - } - MS_LOG(INFO) << "PrePass completed."; - return Status::OK(); -} - -Status TreeAdapterLite::PostPass(std::shared_ptr ir) const { - RETURN_UNEXPECTED_IF_NULL(ir); - // Vector of actions in post-pass phase - std::vector> actions; -#ifndef ENABLE_ANDROID - MS_LOG(INFO) << "Running repeat pass."; - (void)actions.emplace_back(std::make_unique()); - bool modified = false; - RETURN_IF_NOT_OK(actions[0]->Run(ir, &modified)); - MS_LOG(INFO) << "Repeat pass completed."; -#endif - return Status::OK(); -} - -Status TreeAdapterLite::Compile(const std::shared_ptr &input_ir, int32_t num_epochs) { - RETURN_UNEXPECTED_IF_NULL(input_ir); - VLOG_FLOW("Dataset Pipeline TreeAdapterLite Compile started."); - input_ir_ = input_ir; - MS_LOG(INFO) << "Input plan:" << '\n' << *input_ir << '\n'; - - // Clone the input IR tree and insert under the root node - // Create a root node to host the new copy of the input IR tree - // This is done so that the PrePass will process and modify the tree - // without changing the tree associated with the user code. - // The tree from the user code is permitted to form a graph where any node - // is consumed by more than one parent. However, this cloning process here - // will break the graph into a tree by copying each consumption of a node into a new copy. - DeepCopyPass cloning_tree; - bool m = false; - RETURN_IF_NOT_OK(cloning_tree.Run(input_ir, &m)); - std::shared_ptr root_ir = cloning_tree.Root(); - root_ir->SetNumEpochs(num_epochs); - - MS_LOG(INFO) << "Plan before PrePass:" << '\n' << *root_ir << '\n'; - // Pre-pass of the IR tree - RETURN_IF_NOT_OK(PrePass(root_ir)); - MS_LOG(INFO) << "Plan after PrePass:" << '\n' << *root_ir << '\n'; - - RETURN_IF_NOT_OK(PostPass(root_ir)); - MS_LOG(INFO) << "Plan after PostPass:" << '\n' << *root_ir << '\n'; - root_ir_ = root_ir; - - RETURN_IF_NOT_OK(BuildTree(root_ir)); - VLOG_FLOW("Dataset Pipeline TreeAdapterLite Compile finished."); - return Status::OK(); -} - -nlohmann::json TreeAdapterLite::GetOffloadJson() { return offload_json_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h b/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h deleted file mode 100644 index af5b61a61..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_adapter_lite.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_LITE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_LITE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/dataset_node.h" - -namespace mindspore { -namespace dataset { - -class TensorRow; -class DatasetNode; - -class TreeAdapterLite { - public: - // this flag is used to indicate the purpose of the creation of this tree adapter (type of the tree_consumer). - // Currently there are 3 types of consumer, Iterator, Getter and TDT/Vocab/Save ... - // To avoid premature optimization, the last type (TDT/Vocab/Save) is regarded as Iterator for now. - enum UsageFlag { kDeIterator = 0, kDeGetter = 1, kDeReset = 2 }; - - explicit TreeAdapterLite(UsageFlag usage = kDeGetter); - - ~TreeAdapterLite() = default; - - Status BuildTree(std::shared_ptr root_ir); - - // Get rows equal to num_rows - Status GetNextRow(TensorRow *const row); - - std::unordered_map GetColumnNameMap() const { return tree_->root()->column_name_id_map(); } - - // unique_ptr overloads operator bool(), will return false if it doesn't manage an object - std::weak_ptr GetRoot() const { return tree_ ? tree_->root() : nullptr; } - - // This function performs syntax checking, semantics checking, and then call BuildTree - Status Compile(const std::shared_ptr &input_ir, int32_t num_epochs = -1); - - protected: - // Run the mandatory pass checking the syntax and semantics of the IR tree - Status PrePass(std::shared_ptr ir); - - // Run the mandatory pass augmenting the IR tree - Status PostPass(std::shared_ptr ir) const; - - // Return Offload Json - nlohmann::json GetOffloadJson(); - - std::shared_ptr input_ir_; - std::shared_ptr root_ir_; - - private: - // This RECURSIVE function walks the (optimized) IR tree in DFS to build its corresponding Execution tree. - Status BuildExecutionTreeRecur(std::shared_ptr ir, std::shared_ptr *op); - - std::shared_ptr root_; // current connector capacity of root op, used for profiling - std::unique_ptr tree_; - UsageFlag usage_; // usage of this tree adapter (type of consumer) - nlohmann::json offload_json_; -}; - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_ADAPTER_LITE_H_ diff --git a/mindspore-lite/minddata/dataset/engine/tree_modifier.cc b/mindspore-lite/minddata/dataset/engine/tree_modifier.cc deleted file mode 100644 index 111df095c..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_modifier.cc +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/engine/tree_modifier.h" - -namespace mindspore { -namespace dataset { -Status AutotuneCallback::DSNStepBegin(const CallbackParam &cb_param) { - // check if the queue is empty, no need to wait until a change request is ready - if (!change_request_queue_->empty()) { - ChangeRequestPtr change_request; - RETURN_IF_NOT_OK(change_request_queue_->PopFront(&change_request)); - RETURN_IF_NOT_OK(change_request->ApplyChange(op_)); - } - return Status::OK(); -} - -Status AutotuneCallback::DSBegin(const CallbackParam &cb_param) { return Status::OK(); } - -Status AutotuneCallback::DSEpochBegin(const CallbackParam &cb_param) { return Status::OK(); } - -Status AutotuneCallback::DSEnd(const CallbackParam &cb_param) { return Status::OK(); } - -Status AutotuneCallback::DSEpochEnd(const CallbackParam &cb_param) { return Status::OK(); } - -Status AutotuneCallback::DSNStepEnd(const CallbackParam &cb_param) { return Status::OK(); } - -bool AutotuneCallback::IsBeginNeeded() { return false; } - -bool AutotuneCallback::IsEpochBeginNeeded() { return false; } - -bool AutotuneCallback::IsNStepBeginNeeded() { return true; } - -bool AutotuneCallback::IsEndNeeded() { return false; } - -bool AutotuneCallback::IsEpochEndNeeded() { return false; } - -bool AutotuneCallback::IsNStepEndNeeded() { return false; } - -Status AutotuneCallback::PushChangeRequest(ChangeRequestPtr change_request) { - RETURN_IF_NOT_OK(change_request_queue_->Add(std::move(change_request))); - return Status::OK(); -} - -Status ChangeNumWorkersRequest::ApplyChange(DatasetOp *op) { - int32_t diff = num_workers_ - op->NumWorkers(); - if (diff > 0) { - RETURN_IF_NOT_OK(op->AddNewWorkers(diff)); - } else if (diff < 0) { - RETURN_IF_NOT_OK(op->RemoveWorkers(-1 * diff)); - } - return Status::OK(); -} - -TreeModifier::TreeModifier(const TreeAdapter *adapter) : TreeModifier(adapter->tree_.get()) {} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/engine/tree_modifier.h b/mindspore-lite/minddata/dataset/engine/tree_modifier.h deleted file mode 100644 index 332505dcd..000000000 --- a/mindspore-lite/minddata/dataset/engine/tree_modifier.h +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_MODIFIER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_MODIFIER_H_ - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/engine/execution_tree.h" -#include "mindspore-lite/minddata/dataset/engine/tree_adapter.h" - -constexpr int64_t queue_size = 10; - -namespace mindspore { -namespace dataset { -class DatasetNode; - -/// A pure virtual class to be used as a base for all pipeline modification requests. -class ChangeRequest { - public: - /// Default constructor - ChangeRequest() = default; - virtual ~ChangeRequest() = default; - - /// Pure virtual method. Subclasses should override this function and implement the actual change to the give - /// operator. - /// \param op pointer to the operator that the change will be applied on - /// \return Status return Status code - virtual Status ApplyChange(DatasetOp *op) = 0; -}; - -using ChangeRequestPtr = std::shared_ptr; - -/// ChangeRequest to add n workers to an operator. -class ChangeNumWorkersRequest : public ChangeRequest { - public: - /// Constructor - /// \param num_workers number of workeres to be added to the operator. Default to 1. - explicit ChangeNumWorkersRequest(int32_t num_workers = 1) : num_workers_(num_workers) {} - virtual ~ChangeNumWorkersRequest() = default; - - /// Actual change to add n workers - /// \param op pointer to the operator that the change will be applied on - /// \return Status return Status code - Status ApplyChange(DatasetOp *op) override; - - private: - int32_t num_workers_; -}; - -/// ChangeRequest to change the size of the oupout connector of an operators. -class ResizeConnectorRequest : public ChangeRequest { - public: - /// Constructor - /// \param new_size new queue size. - explicit ResizeConnectorRequest(int32_t new_size) : new_size_(new_size) {} - virtual ~ResizeConnectorRequest() = default; - - /// Actual change to resize the output connector of the given operator - /// \param op pointer to the operator that the change will be applied on - /// \return Status return Status code - Status ApplyChange(DatasetOp *op) override { - RETURN_IF_NOT_OK(op->OutputConnector()->Resize(new_size_)); - return Status::OK(); - } - - private: - int32_t new_size_; -}; - -/// A callback class used by Aututune to queue changes for operators -class AutotuneCallback : public DSCallback { - public: - AutotuneCallback(int32_t step_size, DatasetOp *op) - : DSCallback(step_size), op_(op), change_request_queue_(std::make_unique>(queue_size)) {} - virtual ~AutotuneCallback() = default; - - Status DSNStepBegin(const CallbackParam &cb_param) override; - Status DSBegin(const CallbackParam &cb_param) override; - Status DSEpochBegin(const CallbackParam &cb_param) override; - Status DSEnd(const CallbackParam &cb_param) override; - Status DSEpochEnd(const CallbackParam &cb_param) override; - Status DSNStepEnd(const CallbackParam &cb_param) override; - - bool IsBeginNeeded() override; - bool IsEpochBeginNeeded() override; - bool IsNStepBeginNeeded() override; - bool IsEndNeeded() override; - bool IsEpochEndNeeded() override; - bool IsNStepEndNeeded() override; - - /// Push a change request to the queue of the callback. - /// \param change_request Shared pointer to the change request to be pushed to the queue. - /// \return Status return Status code - Status PushChangeRequest(ChangeRequestPtr change_request); - - private: - DatasetOp *op_; - std::unique_ptr> change_request_queue_; -}; - -/// Main class to handle modification of the ExecutionTree used by AutoTune -class TreeModifier { - // friend with TreeAdapter to access the ExecutionTree - friend TreeAdapter; - - public: - /// Constructor to create a TreeModifier given a TreeAdapter - /// \param adapter TreeAdapter - explicit TreeModifier(const TreeAdapter *adapter); - - /// Constructor to create a TreeModifier given an ExecutionTree - /// \param tree ExecutionTree - explicit TreeModifier(ExecutionTree *tree) : tree_(tree) { - // loop over all ops to create AutotuneCallback and register it. - for (auto itr = tree_->begin(); itr != tree_->end(); ++itr) { - auto cb = std::make_shared(1, itr.get().get()); - itr->AddCallbacks({cb}); - (void)callbacks.insert(std::make_pair(itr->id(), cb)); - } - } - - /// Add changeRequest to the callback associated with the op. - /// \param op_id Operator ID - /// \param change_request Pointer to the change request - /// \return Status return Status code - Status AddChangeRequest(int32_t op_id, const ChangeRequestPtr &change_request) { - num_requests_++; - RETURN_IF_NOT_OK(callbacks[op_id]->PushChangeRequest(change_request)); - return Status::OK(); - } - - /// \brief Get the number of change requests received - /// \return Number of change requests received - uint64_t GetRequestsCount() const { return num_requests_; } - - private: - ExecutionTree *tree_; - std::map> callbacks; - uint64_t num_requests_ = 0; // counter for number of requests received -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_ENGINE_TREE_MODIFIER_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/audio.h b/mindspore-lite/minddata/dataset/include/dataset/audio.h deleted file mode 100644 index ddf2315a3..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/audio.h +++ /dev/null @@ -1,1437 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_AUDIO_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_AUDIO_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" -#include "include/api/visible.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" - -namespace mindspore { -namespace dataset { -class TensorOperation; - -// Transform operations for performing computer audio. -namespace audio { -/// \brief Compute the angle of complex tensor input. -class DATASET_API Angle final : public TensorTransform { - public: - /// \brief Constructor. - Angle(); - - /// \brief Destructor. - ~Angle() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Design two-pole allpass filter. Similar to SoX implementation. -class DATASET_API AllpassBiquad final : public TensorTransform { - public: - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] central_freq Central frequency (in Hz). - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - explicit AllpassBiquad(int32_t sample_rate, float central_freq, float Q = 0.707); - - /// \brief Destructor. - ~AllpassBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief AmplitudeToDB TensorTransform. -/// \notes Turn a tensor from the power/amplitude scale to the decibel scale. -class DATASET_API AmplitudeToDB final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] stype Scale of input tensor, must be one of [ScaleType::kPower, ScaleType::kMagnitude]. - /// Default: ScaleType::kPower. - /// \param[in] ref_value Calculate db_multiplier. Default: 1.0. - /// \param[in] amin Minimum threshold for input tensor and ref_value. It must be greater than zero. Default: 1e-10. - /// \param[in] top_db Decibels cut-off value. It must be greater than or equal to zero. Default: 80.0. - explicit AmplitudeToDB(ScaleType stype = ScaleType::kPower, float ref_value = 1.0, float amin = 1e-10, - float top_db = 80.0); - - /// \brief Destructor. - ~AmplitudeToDB() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design two-pole band filter. -class DATASET_API BandBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] central_freq Central frequency (in Hz). - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - /// \param[in] noise Choose alternate mode for un-pitched audio or mode oriented to pitched audio. Default: False. - explicit BandBiquad(int32_t sample_rate, float central_freq, float Q = 0.707, bool noise = false); - - /// \brief Destructor. - ~BandBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design two-pole band-pass filter. -class DATASET_API BandpassBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] central_freq Central frequency (in Hz). - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - /// \param[in] const_skirt_gain, If True, uses a constant skirt gain (peak gain = Q). If False, uses a - /// constant 0dB peak gain. Default: False. - explicit BandpassBiquad(int32_t sample_rate, float central_freq, float Q = 0.707, bool const_skirt_gain = false); - - /// \brief Destructor. - ~BandpassBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design two-pole band-reject filter. Similar to SoX implementation. -class DATASET_API BandrejectBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] central_freq Central frequency (in Hz). - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - explicit BandrejectBiquad(int32_t sample_rate, float central_freq, float Q = 0.707); - - /// \brief Destructor. - ~BandrejectBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design a bass tone-control effect. -class DATASET_API BassBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] gain Desired gain at the boost (or attenuation) in dB. - /// \param[in] central_freq Central frequency (in Hz). Default: 100.0. - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - explicit BassBiquad(int32_t sample_rate, float gain, float central_freq = 100.0, float Q = 0.707); - - /// \brief Destructor. - ~BassBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Perform a biquad filter of input tensor. -class DATASET_API Biquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] b0 Numerator coefficient of current input, x[n]. - /// \param[in] b1 Numerator coefficient of input one time step ago x[n-1]. - /// \param[in] b2 Numerator coefficient of input two time steps ago x[n-2]. - /// \param[in] a0 Denominator coefficient of current output y[n], the value can't be zero, typically 1. - /// \param[in] a1 Denominator coefficient of current output y[n-1]. - /// \param[in] a2 Denominator coefficient of current output y[n-2]. - explicit Biquad(float b0, float b1, float b2, float a0, float a1, float a2); - - /// \brief Destructor. - ~Biquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief ComplexNorm TensorTransform. -/// \notes Compute the norm of complex tensor input. -class DATASET_API ComplexNorm final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] power Power of the norm, which must be non-negative. Default: 1.0. - explicit ComplexNorm(float power = 1.0); - - /// \brief Destructor. - ~ComplexNorm() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief ComputeDeltas Transform. -/// \note Compute delta coefficients of a spectrogram. -class DATASET_API ComputeDeltas final : public TensorTransform { - public: - /// \brief Construct a new Compute Deltas object. - /// \f[ - /// d_{t}=\frac{{\textstyle\sum_{n=1}^{N}}n(c_{t+n}-c_{t-n})}{2{\textstyle\sum_{n=1}^{N}}n^{2}} - /// \f] - /// \param[in] win_length The window length used for computing delta, must be no less than 3. Default: 5. - /// \param[in] pad_mode Padding mode. Can be one of BorderType::kConstant, BorderType::kEdge, - /// BorderType::kReflect or BorderType::kSymmetric. Default: BorderType::kEdge. - explicit ComputeDeltas(int32_t win_length = 5, BorderType pad_mode = BorderType::kEdge); - - /// \brief Destructor. - ~ComputeDeltas() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply contrast effect. -class DATASET_API Contrast final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] enhancement_amount Controls the amount of the enhancement. Default: 75.0. - explicit Contrast(float enhancement_amount = 75.0); - - /// \brief Destructor. - ~Contrast() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Turn a waveform from the decibel scale to the power/amplitude scale. -class DATASET_API DBToAmplitude final : public TensorTransform { - public: - /// \brief Constructor - /// \param[in] ref Reference which the output will be scaled by. - /// \param[in] power If power equals 1, will compute DB to power. If 0.5, will compute DB to amplitude. - explicit DBToAmplitude(float ref, float power); - - /// \brief Destructor. - ~DBToAmplitude() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply a DC shift to the audio. -class DATASET_API DCShift : public TensorTransform { - public: - /// \brief Constructor - /// \param[in] shift Indicates the amount to shift the audio, the value must be in the range [-2.0, 2.0]. - /// \param[in] limiter_gain Used only on peaks to prevent clipping. - DCShift(float shift, float limiter_gain); - - /// \brief Constructor - /// \param[in] shift Indicates the amount to shift the audio. - /// \note This constructor will use `shift` as `limiter_gain`. - explicit DCShift(float shift); - - /// \brief Destructor. - ~DCShift() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \param[in] n_mfcc Number of mfc coefficients to retain, the value must be greater than 0. -/// \param[in] n_mels Number of mel filterbanks, the value must be greater than 0. -/// \param[in] norm Norm to use, can be NormMode::kNone or NormMode::kOrtho. -/// \return Status error code, returns OK if no error encountered. -Status CreateDct(mindspore::MSTensor *output, int32_t n_mfcc, int32_t n_mels, NormMode norm = NormMode::kNone); - -/// \brief Design two-pole deemph filter. Similar to SoX implementation. -class DATASET_API DeemphBiquad final : public TensorTransform { - public: - /// \param[in] sample_rate Sampling rate of the waveform, the value can only be 44100 (Hz) or 48000(hz). - explicit DeemphBiquad(int32_t sample_rate); - - /// \brief Destructor. - ~DeemphBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Detect pitch frequency. -class DATASET_API DetectPitchFrequency final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] frame_time Duration of a frame, the value must be greater than zero. Default: 0.02. - /// \param[in] win_length The window length for median smoothing (in number of frames), the value must - /// be greater than zero. Default: 30. - /// \param[in] freq_low Lowest frequency that can be detected (Hz), the value must be greater than zero. Default: 85. - /// \param[in] freq_high Highest frequency that can be detected (Hz), the value must be greater than - /// zero. Default: 3400. - explicit DetectPitchFrequency(int32_t sample_rate, float frame_time = 0.01, int32_t win_length = 30, - int32_t freq_low = 85, int32_t freq_high = 3400); - - /// \brief Destructor. - ~DetectPitchFrequency() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Dither increases the perceived dynamic range of audio stored at a -/// particular bit-depth by eliminating nonlinear truncation distortion. -class DATASET_API Dither final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] density_function The density function of a continuous random variable. - /// Can be one of DensityFunction::kTPDF (Triangular Probability Density Function), - /// DensityFunction::kRPDF (Rectangular Probability Density Function) or - /// DensityFunction::kGPDF (Gaussian Probability Density Function). Default: DensityFunction::kTPDF. - /// \param[in] noise_shaping A filtering process that shapes the spectral energy of - /// quantisation error. Default: false. - explicit Dither(DensityFunction density_function = DensityFunction::kTPDF, bool noise_shaping = false); - - /// \brief Destructor. - ~Dither() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief EqualizerBiquad TensorTransform. Apply highpass biquad filter on audio. -class DATASET_API EqualizerBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] center_freq Filter's central frequency (in Hz). - /// \param[in] gain Desired gain at the boost (or attenuation) in dB. - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - EqualizerBiquad(int32_t sample_rate, float center_freq, float gain, float Q = 0.707); - - /// \brief Destructor. - ~EqualizerBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Add fade in or/and fade out on the input audio. -class DATASET_API Fade final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] fade_in_len Length of fade-in (time frames), which must be non-negative - /// and no more than the length of waveform. Default: 0. - /// \param[in] fade_out_len Length of fade-out (time frames), which must be non-negative - /// and no more than the length of waveform. Default: 0. - /// \param[in] fade_shape An enum for the fade shape. Default: FadeShape::kLinear. - explicit Fade(int32_t fade_in_len = 0, int32_t fade_out_len = 0, FadeShape fade_shape = FadeShape::kLinear); - - /// \brief Destructor. - ~Fade() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design IIR forward and backward filter. -class DATASET_API Filtfilt final : public TensorTransform { - public: - /// \param[in] a_coeffs Numerator coefficients of difference equation of dimension of (n_order + 1). - /// Lower delays coefficients are first, e.g. [a0, a1, a2, ...]. - /// Must be same size as b_coeffs (pad with 0's as necessary). - /// \param[in] b_coeffs Numerator coefficients of difference equation of dimension of (n_order + 1). - /// Lower delays coefficients are first, e.g. [b0, b1, b2, ...]. - /// Must be same size as a_coeffs (pad with 0's as necessary). - /// \param[in] clamp If True, clamp the output signal to be in the range [-1, 1]. Default: True. - Filtfilt(const std::vector &a_coeffs, const std::vector &b_coeffs, bool clamp = true); - - /// \brief Destructor. - ~Filtfilt() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply a flanger effect to the audio. -class DATASET_API Flanger final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz). - /// \param[in] delay Desired delay in milliseconds (ms), range: [0, 30]. Default: 0.0. - /// \param[in] depth Desired delay depth in milliseconds (ms), range: [0, 10]. Default: 2.0. - /// \param[in] regen Desired regen (feedback gain) in dB., range: [-95, 95]. Default: 0.0. - /// \param[in] width Desired width (delay gain) in dB, range: [0, 100]. Default: 71.0. - /// \param[in] speed Modulation speed in Hz, range: [0.1, 10]. Default: 0.5. - /// \param[in] phase Percentage phase-shift for multi-channel, range: [0, 100]. Default: 25.0. - /// \param[in] modulation Modulation of input tensor, must be one of [Modulation::kSinusoidal, - /// Modulation::kTriangular]. Default:Modulation::kSinusoidal. - /// \param[in] interpolation Interpolation of input tensor, must be one of [Interpolation::kLinear, - /// Interpolation::kQuadratic]. Default:Interpolation::kLinear. - explicit Flanger(int32_t sample_rate, float delay = 0.0, float depth = 2.0, float regen = 0.0, float width = 71.0, - float speed = 0.5, float phase = 25.0, Modulation modulation = Modulation::kSinusoidal, - Interpolation interpolation = Interpolation::kLinear); - - /// \brief Destructor. - ~Flanger() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief FrequencyMasking TensorTransform. -/// \notes Apply masking to a spectrogram in the frequency domain. -class DATASET_API FrequencyMasking final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] iid_masks Whether to apply different masks to each example. - /// \param[in] frequency_mask_param Maximum possible length of the mask, range: [0, freq_length]. Default: 0. - /// Indices uniformly sampled from [0, frequency_mask_param]. - /// Mask width when iid_masks=true. - /// \param[in] mask_start Mask start when iid_masks=true, range: [0, freq_length-frequency_mask_param]. Default: 0. - /// \param[in] mask_value Mask value. - explicit FrequencyMasking(bool iid_masks = false, int32_t frequency_mask_param = 0, int32_t mask_start = 0, - float mask_value = 0.0); - - /// \brief Destructor. - ~FrequencyMasking() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply amplification or attenuation to the whole waveform. -class DATASET_API Gain final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] gain_db Gain adjustment in decibels (dB). Default: 1.0. - explicit Gain(float gain_db = 1.0); - - /// \brief Destructor. - ~Gain() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Waveform calculation from linear scalar amplitude spectrogram using GriffinLim transform. -class DATASET_API GriffinLim final : public TensorTransform { - public: - /// \brief Constructor. - /// \notes Calculated by formula: - /// x(n)=\frac{\sum_{m=-\infty}^{\infty} w(m S-n) y_{w}(m S, n)}{\sum_{m=-\infty}^{\infty} w^{2}(m S-n)} - /// where w represents the window function, y represents the reconstructed signal of each frame and x represents - /// the whole signal. - /// \param[in] n_fft Size of FFT. Default: 400. - /// \param[in] n_iter Number of iteration for phase recovery. Default: 32. - /// \param[in] win_length Window size for GriffinLim. Default: 0, will be set to n_fft. - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will be set to win_length / 2. - /// \param[in] window_type Window type for GriffinLim. Default: WindowType::kHann. - /// \param[in] power Exponent for the magnitude spectrogram. Default: 2.0. - /// \param[in] momentum The momentum for fast Griffin-Lim. Default: 0.99. - /// \param[in] length Length of the expected output waveform. Default: 0.0, will be set to the value of last - /// dimension of the stft matrix. - /// \param[in] rand_init Flag for random phase initialization or all-zero phase initialization. Default: true. - explicit GriffinLim(int32_t n_fft = 400, int32_t n_iter = 32, int32_t win_length = 0, int32_t hop_length = 0, - WindowType window_type = WindowType::kHann, float power = 2.0, float momentum = 0.99, - int32_t length = 0, bool rand_init = true); - - /// \brief Destructor. - ~GriffinLim() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief HighpassBiquad TensorTransform. Apply highpass biquad filter on audio. -class DATASET_API HighpassBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] cutoff_freq Filter cutoff frequency (in Hz). - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - HighpassBiquad(int32_t sample_rate, float cutoff_freq, float Q = 0.707); - - /// \brief Destructor. - ~HighpassBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief InverseMelScale TensorTransform -/// \notes Solve for a normal STFT from a mel frequency STFT, using a conversion matrix. -class DATASET_API InverseMelScale final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] n_stft Number of bins in STFT, must be positive. - /// \param[in] n_mels Number of mel filter, must be positive. Default: 128. - /// \param[in] sample_rate Sample rate of the signal, the value can't be zero. Default: 16000. - /// \param[in] f_min Minimum frequency, must be non-negative. Default: 0.0. - /// \param[in] f_max Maximum frequency, must be non-negative. Default: 0.0, will be set to sample_rate / 2. - /// \param[in] max_iter Maximum number of optimization iterations, must be positive. Default: 100000. - /// \param[in] tolerance_loss Value of loss to stop optimization at, must be non-negative. Default: 1e-5. - /// \param[in] tolerance_change Difference in losses to stop optimization at, must be non-negative. Default: 1e-8. - /// \param[in] sgdargs Parameters of SGD optimizer, including lr, momentum. - /// Default: {{"sgd_lr", 0.1}, {"sgd_momentum", 0.0}}. - /// \param[in] norm Type of norm, value should be NormType::kSlaney or NormType::kNone. If norm is NormType::kSlaney, - /// divide the triangle mel weight by the width of the mel band. Default: NormType::kNone. - /// \param[in] mel_type Type of mel, value should be MelType::kHtk or MelType::kSlaney. Default: MelType::kHtk. - explicit InverseMelScale(int32_t n_stft, int32_t n_mels = 128, int32_t sample_rate = 16000, float f_min = 0.0, - float f_max = 0.0, int32_t max_iter = 100000, float tolerance_loss = 1e-5, - float tolerance_change = 1e-8, - const std::map &sgdargs = {{"sgd_lr", 0.1}, {"sgd_momentum", 0.0}}, - NormType norm = NormType::kNone, MelType mel_type = MelType::kHtk); - - /// \brief Destructor. - ~InverseMelScale() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create an inverse spectrogram to recover an audio signal from a spectrogram. -class DATASET_API InverseSpectrogram final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] length The output length of the waveform. Default: 0, means to output the whole waveform. - /// \param[in] n_fft Size of FFT, creates n_fft // 2 + 1 bins. Default: 400. - /// \param[in] win_length Window size. Default: 0, will be set to `n_fft` . - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will be set to `win_length // 2` . - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] window A function to create a window tensor that is applied/multiplied to each frame/window. - /// Default: WindowType::kHann. - /// \param[in] normalized Whether the spectrogram was normalized by magnitude after stft. Default:false. - /// \param[in] center Whether the signal in spectrogram was padded on both sides. Default: true. - /// \param[in] pad_mode Controls the padding method used when center is True. Default: BorderType::kReflect. - /// \param[in] onesided Controls whether spectrogram was used to return half of results to avoid - /// redundancy. Default: true. - explicit InverseSpectrogram(int32_t length = 0, int32_t n_fft = 400, int32_t win_length = 0, int32_t hop_length = 0, - int32_t pad = 0, WindowType window = WindowType::kHann, bool normalized = false, - bool center = true, BorderType pad_mode = BorderType::kReflect, bool onesided = true); - - /// \brief Destructor. - ~InverseSpectrogram() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create LFCC for a raw audio signal. -class DATASET_API LFCC final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sample rate of audio signal. Default: 16000. - /// \param[in] n_filter Number of linear filters to apply. Default: 128. - /// \param[in] n_lfcc Number of lfc coefficients to retain. Default: 40. - /// \param[in] f_min Minimum frequency. Default: 0.0. - /// \param[in] f_max Maximum frequency. Default: 0.0, will be set to sample_rate // 2. - /// \param[in] dct_type Type of DCT (discrete cosine transform) to use. Default: 2. - /// \param[in] norm Norm to use. Default: NormMode::kOrtho. - /// \param[in] log_lf Whether to use log-lf spectrograms instead of db-scaled. Default: false. - /// \param[in] n_fft Size of FFT, creates n_fft // 2 + 1 bins. Default: 400. - /// \param[in] win_length Window size. Default: 0, will be set to n_fft. - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will be set to win_length // 2. - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] window A function to create a window tensor that is applied/multiplied to - /// each frame/window. Default: WindowType::kHann. - /// \param[in] power Exponent for the magnitude spectrogram, (must be > 0) e.g., 1 for energy, 2 - /// for power, etc. Default: 2.0. - /// \param[in] normalized Whether to normalize by magnitude after stft. Default: false - /// \param[in] center Whether to pad waveform on both sides so that the tt-th frame is centered at - /// time t t*hop_length. Default: true. - /// \param[in] pad_mode Controls the padding method used when center is True. Default: - /// BorderType::kReflect. - /// \param[in] onesided Controls whether to return half of results to avoid - /// redundancy. Default: true. - explicit LFCC(int32_t sample_rate = 16000, int32_t n_filter = 128, int32_t n_lfcc = 40, float f_min = 0.0, - float f_max = 0.0, int32_t dct_type = 2, NormMode norm = NormMode::kOrtho, bool log_lf = false, - int32_t n_fft = 400, int32_t win_length = 0, int32_t hop_length = 0, int32_t pad = 0, - WindowType window = WindowType::kHann, float power = 2.0, bool normalized = false, bool center = true, - BorderType pad_mode = BorderType::kReflect, bool onesided = true); - - /// \brief Destructor. - ~LFCC() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design filter. Similar to SoX implementation. -class DATASET_API LFilter final : public TensorTransform { - public: - /// \param[in] a_coeffs Numerator coefficients of difference equation of dimension of (n_order + 1). - /// Lower delays coefficients are first, e.g. [a0, a1, a2, ...]. - /// Must be same size as b_coeffs (pad with 0's as necessary). - /// \param[in] b_coeffs Numerator coefficients of difference equation of dimension of (n_order + 1). - /// Lower delays coefficients are first, e.g. [b0, b1, b2, ...]. - /// Must be same size as a_coeffs (pad with 0's as necessary). - /// \param[in] clamp If True, clamp the output signal to be in the range [-1, 1]. Default: True. - explicit LFilter(const std::vector &a_coeffs, const std::vector &b_coeffs, bool clamp = true); - - /// \brief Destructor. - ~LFilter() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Creates a linear triangular filterbank. -/// \param output Tensor of a linear triangular filterbank. -/// \param n_freqs: Number of frequency. -/// \param f_min: Minimum of frequency in Hz. -/// \param f_max: Maximum of frequency in Hz. -/// \param n_filter: Number of (linear) triangular filter. -/// \param sample_rate: Sample rate. -/// \return Status code. -Status DATASET_API LinearFbanks(MSTensor *output, int32_t n_freqs, float f_min, float f_max, int32_t n_filter, - int32_t sample_rate); - -/// \brief Design biquad lowpass filter and perform filtering. Similar to SoX implementation. -class DATASET_API LowpassBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] cutoff_freq Filter cutoff frequency. - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - LowpassBiquad(int32_t sample_rate, float cutoff_freq, float Q = 0.707); - - /// \brief Destructor. - ~LowpassBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Separate a complex-valued spectrogram with shape (..., 2) into its magnitude and phase. -class DATASET_API Magphase final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] power Power of the norm, which must be non-negative. Default: 1.0. - explicit Magphase(float power); - - /// \brief Destructor. - ~Magphase() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief MaskAlongAxis TensorTransform. -/// \note Tensor operation to mask the input tensor along axis. -class MaskAlongAxis final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] mask_start Starting position of the mask, which must be non negative. - /// \param[in] mask_width The width of the mask, which must be positive. - /// \param[in] mask_value Value to assign to the masked columns. - /// \param[in] axis Axis to apply masking on (1 for frequency and 2 for time). - MaskAlongAxis(int32_t mask_start, int32_t mask_width, float mask_value, int32_t axis); - - /// \brief Destructor. - ~MaskAlongAxis() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief MaskAlongAxisIID TensorTransform. -/// \note Apply a mask along axis. -class MaskAlongAxisIID final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] mask_param Number of columns to be masked, will be uniformly sampled from [0, mask_param], - /// must be non negative. - /// \param[in] mask_value Value to assign to the masked columns. - /// \param[in] axis Axis to apply masking on (1 for frequency and 2 for time). - MaskAlongAxisIID(int32_t mask_param, float mask_value, int32_t axis); - - /// \brief Destructor. - ~MaskAlongAxisIID() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief MelScale TensorTransform. -/// \notes Convert normal STFT to STFT at the Mel scale. -class DATASET_API MelScale final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] n_mels Number of mel filter, which must be positive. Default: 128. - /// \param[in] sample_rate Sample rate of the signal, the value can't be zero. Default: 16000. - /// \param[in] f_min Minimum frequency, which must be non negative. Default: 0.0. - /// \param[in] f_max Maximum frequency, which must be positive. Default: 0.0, will be set to sample_rate / 2. - /// \param[in] n_stft Number of bins in STFT, which must be positive. Default: 201. - /// \param[in] norm Type of norm, value should be NormType::kSlaney or NormType::kNone. If norm is NormType::kSlaney, - /// divide the triangle mel weight by the width of the mel band. Default: NormType::kNone. - /// \param[in] mel_type Type of mel, value should be MelType::kHtk or MelType::kSlaney. Default: MelType::kHtk. - explicit MelScale(int32_t n_mels = 128, int32_t sample_rate = 16000, float f_min = 0.0, float f_max = 0.0, - int32_t n_stft = 201, NormType norm = NormType::kNone, MelType mel_type = MelType::kHtk); - - /// \brief Destructor. - ~MelScale() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create a frequency transformation matrix with shape (n_freqs, n_mels). -/// \param[in] output Tensor of the frequency transformation matrix. -/// \param[in] n_freqs Number of frequencies to highlight/apply. -/// \param[in] f_min Minimum frequency (Hz). -/// \param[in] f_max Maximum frequency (Hz). -/// \param[in] n_mels Number of mel filterbanks. -/// \param[in] sample_rate Sample rate of the audio waveform. -/// \param[in] norm Norm to use, can be NormType::kNone or NormType::kSlaney. Default: NormType::kNone. -/// \param[in] mel_type Scale to use, can be MelType::kHtk or MelType::kSlaney. Default: MelType::kHtz. -/// \return Status code. -Status DATASET_API MelscaleFbanks(MSTensor *output, int32_t n_freqs, float f_min, float f_max, int32_t n_mels, - int32_t sample_rate, NormType norm = NormType::kNone, - MelType mel_type = MelType::kHtk); - -/// \brief Create MelSpectrogram for a raw audio signal. -class DATASET_API MelSpectrogram final : public TensorTransform { - public: - /// \param[in] sample_rate Sample rate of audio signal. Default: 16000. - /// \param[in] n_fft Size of FFT, creates `n_fft // 2 + 1` bins. Default: 400. - /// \param[in] win_length Window size. Default: 0, will be set to `n_fft` . - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will be set to `win_length // 2` . - /// \param[in] f_min Minimum frequency. Default: 0.0. - /// \param[in] f_max Maximum frequency. Default: 0.0. - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] n_mels Number of mel filterbanks. Default: 128. - /// \param[in] window A function to create a window tensor that is applied/multiplied to each frame/window. - /// Default: WindowType::kHann. - /// \param[in] power Exponent for the magnitude spectrogram, (must be > 0) e.g., 1 for energy, 2 for power, etc. - /// Default: 2.0. - /// \param[in] normalized Whether to normalize by magnitude after stft Default: false. - /// \param[in] center Whether to pad waveform on both sides. Default: true. - /// \param[in] pad_mode Controls the padding method used when center is True. Default: BorderType::kReflect. - /// \param[in] onesided Controls whether to return half of results to avoid redundancy. Default: true. - /// \param[in] norm If 'slaney', divide the triangular mel weights by the width of the mel band (area normalization). - /// Default: NormType::kNone. - /// \param[in] mel_scale Scale to use: htk or slaney. Default: MelType::kHtk. - explicit MelSpectrogram(int32_t sample_rate = 16000, int32_t n_fft = 400, int32_t win_length = 0, - int32_t hop_length = 0, float f_min = 0.0, float f_max = 0.0, int32_t pad = 0, - int32_t n_mels = 128, WindowType window = WindowType::kHann, float power = 2.0, - bool normalized = false, bool center = true, BorderType pad_mode = BorderType::kReflect, - bool onesided = true, NormType norm = NormType::kNone, MelType mel_scale = MelType::kHtk); - - /// \brief Destructor. - ~MelSpectrogram() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create MFCC for a raw audio signal. -class DATASET_API MFCC final : public TensorTransform { - public: - /// \param[in] sample_rate Sample rate of audio signal. Default: 16000. - /// \param[in] n_mfcc Number of mfc coefficients to retain. Default: 40. - /// \param[in] dct_type Type of DCT (discrete cosine transform) to use. Default: 2. - /// \param[in] norm If 'slaney', divide the triangular mel weights by the width of the mel band (area normalization). - /// Default: NormMode::kOrtho. - /// \param[in] log_mels Whether to use log-mel spectrograms instead of db-scaled. Default: false. - /// \param[in] n_fft Size of FFT, creates n_fft // 2 + 1 bins. Default: 400. - /// \param[in] win_length Window size. Default: 0. - /// \param[in] hop_length Length of hop between STFT windows. Default: 0. - /// \param[in] f_min Minimum frequency. Default: 0.0. - /// \param[in] f_max Maximum frequency. Default: 0.0. - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] n_mels Number of mel filterbanks. Default: 128. - /// \param[in] window A function to create a window tensor that is applied/multiplied to each frame/window. - /// Default: WindowType::kHann. - /// \param[in] power Exponent for the magnitude spectrogram, (must be > 0) e.g., 1 for energy, 2 for power, etc. - /// Default: 2.0. - /// \param[in] normalized Whether to normalize by magnitude after stft. Default: false. - /// \param[in] center Whether to pad waveform on both sides. Default: true. - /// \param[in] pad_mode Controls the padding method used when center is True. Default: BorderType::kReflect. - /// \param[in] onesided Controls whether to return half of results to avoid redundancy. Default: true. - /// \param[in] norm_mel Norm to use. Default: NormType::kNone. - /// \param[in] mel_scale Scale to use: htk or slaney. Default: MelType::kHtk. - explicit MFCC(int32_t sample_rate = 16000, int32_t n_mfcc = 40, int32_t dct_type = 2, - NormMode norm = NormMode::kOrtho, bool log_mels = false, int32_t n_fft = 400, int32_t win_length = 0, - int32_t hop_length = 0, float f_min = 0.0, float f_max = 0.0, int32_t pad = 0, int32_t n_mels = 128, - WindowType window = WindowType::kHann, float power = 2.0, bool normalized = false, bool center = true, - BorderType pad_mode = BorderType::kReflect, bool onesided = true, NormType norm_mel = NormType::kNone, - MelType mel_scale = MelType::kHtk); - - /// \brief Destructor. - ~MFCC() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief MuLawDecoding TensorTransform. -/// \note Decode mu-law encoded signal. -class DATASET_API MuLawDecoding final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] quantization_channels Number of channels, which must be positive. Default: 256. - explicit MuLawDecoding(int32_t quantization_channels = 256); - - /// \brief Destructor. - ~MuLawDecoding() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief MuLawEncoding TensorTransform. -/// \note Encode signal based on mu-law companding. -class DATASET_API MuLawEncoding final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] quantization_channels Number of channels, which must be positive. Default: 256. - explicit MuLawEncoding(int32_t quantization_channels = 256); - - /// \brief Destructor. - ~MuLawEncoding() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Overdrive TensorTransform. -class DATASET_API Overdrive final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] gain Coefficient of overload in dB, in range of [0, 100]. Default: 20.0. - /// \param[in] color Coefficient of translation, in range of [0, 100]. Default: 20.0. - explicit Overdrive(float gain = 20.0, float color = 20.0); - - /// \brief Destructor. - ~Overdrive() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Phaser TensorTransform. -class DATASET_API Phaser final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz). - /// \param[in] gain_in Desired input gain at the boost (or attenuation) in dB. - /// Allowed range of values is [0, 1]. Default: 0.4. - /// \param[in] gain_out Desired output gain at the boost (or attenuation) in dB. - /// Allowed range of values is [0, 1e9]. Default: 0.74. - /// \param[in] delay_ms Desired delay in milli seconds. Allowed range of values is [0, 5]. Default: 3.0. - /// \param[in] decay Desired decay relative to gain-in. Allowed range of values is [0, 0.99]. Default: 0.4. - /// \param[in] mod_speed Modulation speed in Hz. Allowed range of values is [0.1, 2]. Default: 0.5. - /// \param[in] sinusoidal If true, use sinusoidal modulation (preferable for multiple instruments). - /// If false, use triangular modulation (gives single instruments a sharper phasing effect). Default: true. - explicit Phaser(int32_t sample_rate, float gain_in = 0.4, float gain_out = 0.74, float delay_ms = 3.0, - float decay = 0.4, float mod_speed = 0.5, bool sinusoidal = true); - - /// \brief Destructor. - ~Phaser() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief PhaseVocoder TensorTransform -/// \notes Given a STFT tensor, speed up in time without modifying pitch by factor of rate. -class DATASET_API PhaseVocoder final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] rate Speed-up factor. - /// \param[in] phase_advance Expected phase advance in each bin in shape of (freq, 1). - PhaseVocoder(float rate, const MSTensor &phase_advance); - - /// \brief Destructor. - ~PhaseVocoder() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -// \brief Shift the pitch of a waveform by 'n_steps' steps. -class DATASET_API PitchShift final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of "waveform". Default: 0. - /// \param[in] n_steps The (fractional) steps to shift "waveform". Default: 0. - /// \param[in] bins_per_octave The number of steps per octave. Default: 12. - /// \param[in] n_fft Size of FFT, creates "n_fft // 2 + 1" bins. Default: 512. - /// \param[in] win_length Window size. Default: 0, will be set to `n_fft` . - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will be set to `win_length // 4` . - /// \param[in] window Window tensor that is applied/multiplied to each frame/window. Default: WindowType::kHann. - explicit PitchShift(int32_t sample_rate = 0, int32_t n_steps = 0, int32_t bins_per_octave = 12, int32_t n_fft = 512, - int32_t win_length = 0, int32_t hop_length = 0, WindowType window = WindowType::kHann); - - /// \brief Destructor. - ~PitchShift() override = default; - - protected: - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Resample TensorTransform. -/// \notes Resample a signal from one frequency to another. A sampling method can be given. -class DATASET_API Resample : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] orig_freq The original frequency of the signal, which must be positive. Default: 16000.0. - /// \param[in] new_freq The desired frequency, which must be positive. Default: 16000.0. - /// \param[in] resample_method The resampling method, which can be ResampleMethod::kSincInterpolation - /// and ResampleMethod::kKaiserWindow. Default: ResampleMethod::kSincInterpolation. - /// \param[in] lowpass_filter_width Controls the sharpness of the filter, more means sharper but less efficient, - /// which must be positive. Default: 6. - /// \param[in] rolloff The roll-off frequency of the filter, as a fraction of the Nyquist. Lower values - /// reduce anti-aliasing, but also reduce some of the highest frequencies, range: (0, 1]. Default: 0.99. - /// \param[in] beta The shape parameter used for kaiser window. Default: 14.769656459379492. - explicit Resample(float orig_freq = 16000.0, float new_freq = 16000.0, - ResampleMethod resample_method = ResampleMethod::kSincInterpolation, - int32_t lowpass_filter_width = 6, float rolloff = 0.99, float beta = 14.769656459379492); - - /// \brief Destructor. - ~Resample() override = default; - - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply RIAA vinyl playback equalization. -class DATASET_API RiaaBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), - /// can only be one of 44100, 48000, 88200, 96000. - explicit RiaaBiquad(int32_t sample_rate); - - /// \brief Destructor. - ~RiaaBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply sliding-window cepstral mean (and optionally variance) normalization per utterance. -class DATASET_API SlidingWindowCmn final : public TensorTransform { - public: - /// \brief Constructor of SlidingWindowCmnOp. - /// \param[in] cmn_window The window in frames for running average CMN computation. Default: 600. - /// \param[in] min_cmn_window The minimum CMN window. Only applicable if center is false, ignored if center - /// is true. Default: 100. - /// \param[in] center If true, use a window centered on the current frame. If false, window is to the left. - /// Default: false. - /// \param[in] norm_vars If true, normalize variance to one. Default: false. - explicit SlidingWindowCmn(int32_t cmn_window = 600, int32_t min_cmn_window = 100, bool center = false, - bool norm_vars = false); - - /// \brief Destructor. - ~SlidingWindowCmn() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create a spectral centroid from an audio signal. -class DATASET_API SpectralCentroid : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz). - /// \param[in] n_fft Size of FFT, creates n_fft / 2 + 1 bins. Default: 400. - /// \param[in] win_length Window size. Default: 0, will use n_fft. - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will use win_length / 2. - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] window Window function that is applied/multiplied to each frame/window, - /// which can be WindowType::kBartlett, WindowType::kBlackman, WindowType::kHamming, - /// WindowType::kHann or WindowType::kKaiser. Default: WindowType::kHann. - explicit SpectralCentroid(int32_t sample_rate, int32_t n_fft = 400, int32_t win_length = 0, int32_t hop_length = 0, - int32_t pad = 0, WindowType window = WindowType::kHann); - - ~SpectralCentroid() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - int32_t sample_rate_; - int32_t n_fft_; - int32_t win_length_; - int32_t hop_length_; - int32_t pad_; - WindowType window_; - struct Data; - std::shared_ptr data_; -}; - -/// \brief Create a spectrogram from an audio signal. -class DATASET_API Spectrogram : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] n_fft Size of FFT, creates n_fft / 2 + 1 bins. Default: 400. - /// \param[in] win_length Window size. Default: 0, will use n_fft. - /// \param[in] hop_length Length of hop between STFT windows. Default: 0, will use win_length / 2. - /// \param[in] pad Two sided padding of signal. Default: 0. - /// \param[in] window Window function that is applied/multiplied to each frame/window, - /// which can be WindowType::kBartlett, WindowType::kBlackman, WindowType::kHamming, - /// WindowType::kHann or WindowType::kKaiser. Default: WindowType::kHann. - /// \param[in] power Exponent for the magnitude spectrogram, which must be greater than or equal to 0. Default: 2.0. - /// \param[in] normalized Whether to normalize by magnitude after stft. Default: false. - /// \param[in] center Whether to pad waveform on both sides. Default: true. - /// \param[in] pad_mode Controls the padding method used when center is true, - /// which can be BorderType::kReflect, BorderType::kConstant, BorderType::kEdge, - /// BorderType::kSymmetric. Default: BorderType::kReflect. - /// \param[in] onesided Controls whether to return half of results to avoid redundancy. Default: true. - explicit Spectrogram(int32_t n_fft = 400, int32_t win_length = 0, int32_t hop_length = 0, int32_t pad = 0, - WindowType window = WindowType::kHann, float power = 2.0, bool normalized = false, - bool center = true, BorderType pad_mode = BorderType::kReflect, bool onesided = true); - - /// \brief Destructor. - ~Spectrogram() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - int32_t n_fft_; - int32_t win_length_; - int32_t hop_length_; - int32_t pad_; - WindowType window_; - float power_; - bool normalized_; - bool center_; - BorderType pad_mode_; - bool onesided_; - struct Data; - std::shared_ptr data_; -}; - -/// \brief TimeMasking TensorTransform. -/// \notes Apply masking to a spectrogram in the time domain. -class DATASET_API TimeMasking final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] iid_masks Whether to apply different masks to each example. - /// \param[in] time_mask_param Maximum possible length of the mask, range: [0, time_length]. Default: 0. - /// Indices uniformly sampled from [0, time_mask_param]. - /// Mask width when iid_masks=true. - /// \param[in] mask_start Mask start when iid_masks=true, range: [0, time_length-time_mask_param]. Default: 0. - /// \param[in] mask_value Mask value. - explicit TimeMasking(bool iid_masks = false, int32_t time_mask_param = 0, int32_t mask_start = 0, - float mask_value = 0.0); - - /// \brief Destructor. - ~TimeMasking() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief TimeStretch TensorTransform -/// \notes Stretch STFT in time at a given rate, without changing the pitch. -class DATASET_API TimeStretch final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] hop_length Length of hop between STFT windows. Default: None, will use ((n_freq - 1) * 2) // 2. - /// \param[in] n_freq Number of filter banks form STFT. Default: 201. - /// \param[in] fixed_rate Rate to speed up or slow down the input in time. - /// Default: std::numeric_limits::quiet_NaN(), will keep the original rate. - explicit TimeStretch(float hop_length = std::numeric_limits::quiet_NaN(), int n_freq = 201, - float fixed_rate = std::numeric_limits::quiet_NaN()); - - /// \brief Destructor. - ~TimeStretch() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Design a treble tone-control effect. -class DATASET_API TrebleBiquad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. - /// \param[in] gain Desired gain at the boost (or attenuation) in dB. - /// \param[in] central_freq Central frequency (in Hz). Default: 3000.0. - /// \param[in] Q Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1]. Default: 0.707. - TrebleBiquad(int32_t sample_rate, float gain, float central_freq = 3000.0, float Q = 0.707); - - /// \brief Destructor. - ~TrebleBiquad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Vad TensorTransform. -/// \notes Attempt to trim silent background sounds from the end of the voice recording. -class DATASET_API Vad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sample_rate Sample rate of audio signal. - /// \param[in] trigger_level The measurement level used to trigger activity detection. Default: 7.0. - /// \param[in] trigger_time The time constant (in seconds) used to help ignore short sounds. Default: 0.25. - /// \param[in] search_time The amount of audio (in seconds) to search for quieter/shorter sounds to include prior to - /// the detected trigger point. Default: 1.0. - /// \param[in] allowed_gap The allowed gap (in seconds) between quiteter/shorter sounds to include prior to the - /// detected trigger point. Default: 0.25. - /// \param[in] pre_trigger_time The amount of audio (in seconds) to preserve before the trigger point and any found - /// quieter/shorter bursts. Default: 0.0. - /// \param[in] boot_time The time for the initial noise estimate. Default: 0.35. - /// \param[in] noise_up_time Time constant used by the adaptive noise estimator, when the noise level is increasing. - /// Default: 0.1. - /// \param[in] noise_down_time Time constant used by the adaptive noise estimator, when the noise level is decreasing. - /// Default: 0.01. - /// \param[in] noise_reduction_amount The amount of noise reduction used in the detection algorithm. Default: 1.35. - /// \param[in] measure_freq The frequency of the algorithm’s processing. Default: 20.0. - /// \param[in] measure_duration The duration of measurement. Default: 0, use twice the measurement period. - /// \param[in] measure_smooth_time The time constant used to smooth spectral measurements. Default: 0.4. - /// \param[in] hp_filter_freq The "Brick-wall" frequency of high-pass filter applied at the input to the detector - /// algorithm. Default: 50.0. - /// \param[in] lp_filter_freq The "Brick-wall" frequency of low-pass filter applied at the input to the detector - /// algorithm. Default: 6000.0. - /// \param[in] hp_lifter_freq The "Brick-wall" frequency of high-pass lifter applied at the input to the detector - /// algorithm. Default: 150.0. - /// \param[in] lp_lifter_freq The "Brick-wall" frequency of low-pass lifter applied at the input to the detector - /// algorithm. Default: 2000.0. - explicit Vad(int32_t sample_rate, float trigger_level = 7.0, float trigger_time = 0.25, float search_time = 1.0, - float allowed_gap = 0.25, float pre_trigger_time = 0.0, float boot_time = 0.35, - float noise_up_time = 0.1, float noise_down_time = 0.01, float noise_reduction_amount = 1.35, - float measure_freq = 20.0, float measure_duration = 0.0, float measure_smooth_time = 0.4, - float hp_filter_freq = 50.0, float lp_filter_freq = 6000.0, float hp_lifter_freq = 150.0, - float lp_lifter_freq = 2000.0); - - /// \brief Destructor. - ~Vad() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Vol TensorTransform. -/// \notes Add a volume to an waveform. -class DATASET_API Vol final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] gain Gain value, varies according to the value of gain_type. If gain_type is GainType::kAmplitude, - /// gain must be greater than or equal to zero. If gain_type is GainType::kPower, gain must be greater than zero. - /// If gain_type is GainType::kDb, there is no limit for gain. - /// \param[in] gain_type Type of gain, should be one of [GainType::kAmplitude, GainType::kDb, GainType::kPower]. - explicit Vol(float gain, GainType gain_type = GainType::kAmplitude); - - /// \brief Destructor. - ~Vol() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; -} // namespace audio -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_AUDIO_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/config.h b/mindspore-lite/minddata/dataset/include/dataset/config.h deleted file mode 100644 index 47d12903e..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/config.h +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONFIG_H -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONFIG_H - -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/visible.h" - -namespace mindspore { -namespace dataset { -// Config operations for setting and getting the configuration. -namespace config { -/// \brief A function to set the seed to be used in any random generator. This is used to produce deterministic results. -/// \param[in] seed The default seed to be used. -/// \return The seed is set successfully or not. -/// \par Example -/// \code -/// // Set a new global configuration value for the seed value. -/// // Operations with randomness will use the seed value to generate random values. -/// bool rc = config::set_seed(5); -/// \endcode -bool DATASET_API set_seed(int32_t seed); - -/// \brief A function to get the seed. -/// \return The seed set in the configuration. -/// \par Example -/// \code -/// // Get the global configuration of seed. -/// // If set_seed() is never called before, the default value(std::mt19937::default_seed) will be returned. -/// uint32_t seed = config::get_seed(); -/// \endcode -uint32_t DATASET_API get_seed(); - -/// \brief A function to set the number of rows to be prefetched. -/// \param[in] prefetch_size Total number of rows to be prefetched. -/// \return The prefetch size is set successfully or not. -/// \par Example -/// \code -/// // Set a new global configuration value for the prefetch size. -/// bool rc = config::set_prefetch_size(1000); -/// \endcode -bool DATASET_API set_prefetch_size(int32_t prefetch_size); - -/// \brief A function to get the prefetch size in number of rows. -/// \return Total number of rows to be prefetched. -/// \par Example -/// \code -/// // Get the global configuration of prefetch size. -/// // If set_prefetch_size() is never called before, the default value(16) will be returned. -/// int32_t prefetch_size = config::get_prefetch_size(); -/// \endcode -int32_t DATASET_API get_prefetch_size(); - -/// \brief A function to set the default number of parallel workers. -/// \param[in] num_parallel_workers Number of parallel workers to be used as the default for each operation. -/// \return The workers is set successfully or not. -/// \par Example -/// \code -/// // Set a new global configuration value for the number of parallel workers. -/// // Now parallel dataset operations will run with 16 workers. -/// bool rc = config::set_num_parallel_workers(16); -/// \endcode -bool DATASET_API set_num_parallel_workers(int32_t num_parallel_workers); - -/// \brief A function to get the default number of parallel workers. -/// \return Number of parallel workers to be used as the default for each operation. -/// \par Example -/// \code -/// // Get the global configuration of parallel workers. -/// // If set_num_parallel_workers() is never called before, the default value(8) will be returned. -/// int32_t parallel_workers = config::get_num_parallel_workers(); -/// \endcode -int32_t DATASET_API get_num_parallel_workers(); - -/// \brief A function to set the default interval (in milliseconds) for monitor sampling. -/// \param[in] interval Interval (in milliseconds) to be used for performance monitor sampling. -/// \return The sampling interval is set successfully or not. -/// \par Example -/// \code -/// // Set a new global configuration value for the monitor sampling interval. -/// bool rc = config::set_monitor_sampling_interval(100); -/// \endcode -bool DATASET_API set_monitor_sampling_interval(int32_t interval); - -/// \brief A function to get the default interval of performance monitor sampling. -/// \return Interval (in milliseconds) for performance monitor sampling. -/// \par Example -/// \code -/// // Get the global configuration of monitor sampling interval. -/// // If set_monitor_sampling_interval() is never called before, the default value(1000) will be returned. -/// int32_t sampling_interval = config::get_monitor_sampling_interval(); -/// \endcode -int32_t DATASET_API get_monitor_sampling_interval(); - -/// \brief A function to set the default timeout (in seconds) for DSWaitedCallback. In case of a deadlock, the wait -/// function will exit after the timeout period. -/// \param[in] timeout Timeout (in seconds) to be used to end the wait in DSWaitedCallback in case of a deadlock. -/// \return The callback timeout is set successfully or not. -/// \par Example -/// \code -/// // Set a new global configuration value for the timeout value. -/// bool rc = config::set_callback_timeout(100); -/// \endcode -bool DATASET_API set_callback_timeout(int32_t timeout); - -/// \brief A function to get the default timeout for DSWaitedCallback. In case of a deadback, the wait function -/// will exit after the timeout period. -/// \return The duration in seconds. -/// \par Example -/// \code -/// // Get the global configuration of callback timeout. -/// // If set_callback_timeout() is never called before, the default value(60) will be returned. -/// int32_t callback_timeout = config::get_callback_timeout(); -/// \endcode -int32_t DATASET_API get_callback_timeout(); - -/// \brief A function to load the configuration from a file. -/// \param[in] file Path of the configuration file to be loaded. -/// \return The config file is loaded successfully or not. -/// \note The reason for using this API is that std::string will be constrained by the -/// compiler option '_GLIBCXX_USE_CXX11_ABI' while char is free of this restriction. -/// Check API `mindspore::dataset::config::load(const std::string &file)` and find more usage. -bool DATASET_API load(const std::vector &file); - -/// \brief A function to load the configuration from a file. -/// \param[in] file Path of the configuration file to be loaded. -/// \return The config file is loaded successfully or not. -/// \par Example -/// \code -/// // Set new default configuration according to values in the configuration file. -/// // example config file: -/// // { -/// // "logFilePath": "/tmp", -/// // "numParallelWorkers": 4, -/// // "seed": 5489, -/// // "monitorSamplingInterval": 30 -/// // } -/// std::string config_file = "/path/to/config/file"; -/// bool rc = config::load(config_file); -/// \endcode -inline bool DATASET_API load(const std::string &file) { return load(StringToChar(file)); } -} // namespace config -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONFIG_H diff --git a/mindspore-lite/minddata/dataset/include/dataset/constants.h b/mindspore-lite/minddata/dataset/include/dataset/constants.h deleted file mode 100644 index e2b760bb4..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/constants.h +++ /dev/null @@ -1,366 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONSTANTS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONSTANTS_H_ - -#include -#include -#include - -#include "include/api/visible.h" - -namespace mindspore { -namespace dataset { -// Various type defines for convenience -using uchar = unsigned char; -using dsize_t = int64_t; - -/// \brief The modulation in Flanger -enum class DATASET_API Modulation { - kSinusoidal = 0, ///< Use sinusoidal modulation. - kTriangular = 1 ///< Use triangular modulation. -}; - -/// \brief The interpolation in Flanger -enum class DATASET_API Interpolation { - kLinear = 0, ///< Use linear for delay-line interpolation. - kQuadratic = 1 ///< Use quadratic for delay-line interpolation. -}; - -/// \brief The dataset auto augment policy in AutoAugment -enum class DATASET_API AutoAugmentPolicy { - kImageNet = 0, ///< AutoAugment policy learned on the ImageNet dataset. - kCifar10 = 1, ///< AutoAugment policy learned on the Cifar10 dataset. - kSVHN = 2 ///< AutoAugment policy learned on the SVHN dataset. -}; - -/// \brief The color conversion code -enum class DATASET_API ConvertMode { - COLOR_BGR2BGRA = 0, ///< Add alpha channel to BGR image. - COLOR_RGB2RGBA = COLOR_BGR2BGRA, ///< Add alpha channel to RGB image. - COLOR_BGRA2BGR = 1, ///< Remove alpha channel to BGR image. - COLOR_RGBA2RGB = COLOR_BGRA2BGR, ///< Remove alpha channel to RGB image. - COLOR_BGR2RGBA = 2, ///< Convert BGR image to RGBA image. - COLOR_RGB2BGRA = COLOR_BGR2RGBA, ///< Convert RGB image to BGRA image. - COLOR_RGBA2BGR = 3, ///< Convert RGBA image to BGR image. - COLOR_BGRA2RGB = COLOR_RGBA2BGR, ///< Convert BGRA image to RGB image. - COLOR_BGR2RGB = 4, ///< Convert BGR image to RGB image. - COLOR_RGB2BGR = COLOR_BGR2RGB, ///< Convert RGB image to BGR image. - COLOR_BGRA2RGBA = 5, ///< Convert BGRA image to RGBA image. - COLOR_RGBA2BGRA = COLOR_BGRA2RGBA, ///< Convert RGBA image to BGRA image. - COLOR_BGR2GRAY = 6, ///< Convert BGR image to GRAY image. - COLOR_RGB2GRAY = 7, ///< Convert RGB image to GRAY image. - COLOR_GRAY2BGR = 8, ///< Convert GRAY image to BGR image. - COLOR_GRAY2RGB = COLOR_GRAY2BGR, ///< Convert GRAY image to RGB image. - COLOR_GRAY2BGRA = 9, ///< Convert GRAY image to BGRA image. - COLOR_GRAY2RGBA = COLOR_GRAY2BGRA, ///< Convert GRAY image to RGBA image. - COLOR_BGRA2GRAY = 10, ///< Convert BGRA image to GRAY image. - COLOR_RGBA2GRAY = 11 ///< Convert RGBA image to GRAY image. -}; - -/// \brief The mode for reading a image file. -enum class DATASET_API ImageReadMode { - kUNCHANGED = 0, ///< Remain the output in the original format. - kGRAYSCALE = 1, ///< Convert the output into one channel grayscale data. - kCOLOR = 2, ///< Convert the output into three channels RGB color data. -}; - -// \brief Possible density function in Dither. -enum DATASET_API DensityFunction { - kTPDF = 0, ///< Use triangular probability density function. - kRPDF = 1, ///< Use rectangular probability density function. - kGPDF = 2 ///< Use gaussian probability density function. -}; - -/// \brief Values of norm in CreateDct. -enum class DATASET_API NormMode { - kNone = 0, ///< None type norm. - kOrtho = 1 ///< Ortho type norm. -}; - -/// \brief Possible options for norm in MelscaleFbanks. -enum class DATASET_API NormType { - kNone = 0, ///< None type norm. - kSlaney = 1, ///< Slaney type norm. -}; - -/// \brief The mode for manual offload. -enum class DATASET_API ManualOffloadMode { - kUnspecified, ///< Not set, will use auto_offload setting instead. - kDisabled, ///< Do not perform offload. - kEnabled ///< Attempt to offload. -}; - -/// \brief Target devices to perform map operation. -enum class DATASET_API MapTargetDevice { - kCpu = 0, ///< CPU Device. - kGpu, ///< Gpu Device. - kAscend310, ///< - kAscend910B, ///< - kInvalid = 100 -}; - -/// \brief Possible options for mel_type in MelscaleFbanks. -enum class DATASET_API MelType { - kHtk = 0, ///< Htk scale type. - kSlaney = 1, ///< Slaney scale type. -}; - -/// \brief The initial type of tensor implementation. -enum class DATASET_API TensorImpl { - kNone, ///< None type tensor. - kFlexible, ///< Flexible type tensor, can be converted to any type. - kCv, ///< CV type tensor. - kNP ///< Numpy type tensor. -}; - -/// \brief The mode for shuffling data. -enum class DATASET_API ShuffleMode { - kFalse = 0, ///< No shuffling is performed. - kFiles = 1, ///< Shuffle files only. - kGlobal = 2, ///< Shuffle both the files and samples. - kInfile = 3, ///< Shuffle data within each file. - kPartial = 4, ///< Shuffle data with every 1 million samples - kAdaptive = 5 ///< When the number of dataset samples is less than or equal to 100 million, global shuffle is used. - ///< When the number of dataset samples is greater than 100 million, partial shuffle is used. -}; - -/// \brief Possible scale for input audio. -enum class DATASET_API ScaleType { - kMagnitude = 0, ///< Audio scale is magnitude. - kPower = 1, ///< Audio scale is power. -}; - -/// \brief The scale for gain type. -enum class DATASET_API GainType { - kAmplitude = 0, ///< Audio gain type is amplitude. - kPower = 1, ///< Audio gain type is power. - kDb = 2, ///< Audio gain type is db. -}; - -/// \brief The method of padding. -enum class DATASET_API BorderType { - kConstant = 0, ///< Fill the border with constant values. - kEdge = 1, ///< Fill the border with the last value on the edge. - kReflect = 2, ///< Reflect the values on the edge omitting the last value of edge. - kSymmetric = 3 ///< Reflect the values on the edge repeating the last value of edge. -}; - -/// \brief Possible fix rotation angle for Rotate Op. -enum class DATASET_API FixRotationAngle { - k0Degree = 1, ///< Rotate 0 degree. - k0DegreeAndMirror = 2, ///< Rotate 0 degree and apply horizontal flip. - k180Degree = 3, ///< Rotate 180 degree. - k180DegreeAndMirror = 4, ///< Rotate 180 degree and apply horizontal flip. - k90DegreeAndMirror = 5, ///< Rotate 90 degree and apply horizontal flip. - k90Degree = 6, ///< Rotate 90 degree. - k270DegreeAndMirror = 7, ///< Rotate 270 degree and apply horizontal flip. - k270Degree = 8, ///< Rotate 270 degree. -}; - -/// \brief Possible types for windows function. -enum class DATASET_API WindowType { - kBartlett = 0, ///< Bartlett window function. - kBlackman = 1, ///< Blackman window function. - kHamming = 2, ///< Hamming window function. - kHann = 3, ///< Hann window function. - kKaiser = 4 ///< Kaiser window function. -}; - -/// \brief Possible options for Image format types in a batch. -enum class DATASET_API ImageBatchFormat { - kNHWC = 0, ///< Indicate the input batch is of NHWC format. - kNCHW = 1 ///< Indicate the input batch is of NCHW format. -}; - -/// \brief Possible options for Image format types. -enum class DATASET_API ImageFormat { - HWC = 0, ///< Indicate the input batch is of NHWC format - CHW = 1, ///< Indicate the input batch is of NHWC format - HW = 2 ///< Indicate the input batch is of NHWC format -}; - -/// \brief Possible options for interpolation method. -enum class DATASET_API InterpolationMode { - kLinear = 0, ///< Interpolation method is linear interpolation. - kNearestNeighbour = 1, ///< Interpolation method is nearest-neighbor interpolation. - kCubic = 2, ///< Interpolation method is bicubic interpolation. - kArea = 3, ///< Interpolation method is pixel area interpolation. - kCubicPil = 4 ///< Interpolation method is bicubic interpolation like implemented in pillow. -}; - -/// \brief Possible formats for Vdec output image. -enum class DATASET_API VdecOutputFormat { - kYuvSemiplanar420 = 1, ///< Output image with PIXEL_FORMAT_YUV_SEMIPLANAR_420. - kYvuSemiplanar420 = 2, ///< Output image with PIXEL_FORMAT_YVU_SEMIPLANAR_420. -}; - -/// \brief Possible formats for Vdec input video. -enum class DATASET_API VdecStreamFormat { - kH265MainLevel = 0, ///< Input video with H265_MAIN_LEVEL - kH264BaselineLevel, ///< Input video with H264_BASELINE_LEVEL - kH264MainLevel, ///< Input video with H264_MAIN_LEVEL - kH264HighLevel ///< Input video with H264_HIGH_LEVEL -}; - -/// \brief Possible tokenize modes for JiebaTokenizer. -enum class DATASET_API JiebaMode { - kMix = 0, ///< Tokenize with MPSegment algorithm. - kMp = 1, ///< Tokenize with Hiddel Markov Model Segment algorithm. - kHmm = 2 ///< Tokenize with a mix of MPSegment and HMMSegment algorithm. -}; - -/// \brief Possible options for SPieceTokenizerOutType. -enum class DATASET_API SPieceTokenizerOutType { - kString = 0, ///< Output of sentencepiece tokenizer is string type. - kInt = 1 ///< Output of sentencepiece tokenizer is int type. -}; - -/// \brief Possible options for SPieceTokenizerLoadType. -enum class DATASET_API SPieceTokenizerLoadType { - kFile = 0, ///< Load sentencepiece tokenizer from local sentencepiece vocab file. - kModel = 1 ///< Load sentencepiece tokenizer from sentencepiece vocab instance. -}; - -/// \brief Type options for SentencePiece Model. -enum class DATASET_API SentencePieceModel { - kUnigram = 0, ///< Based on Unigram model. - kBpe = 1, ///< Based on Byte Pair Encoding (BPE) model. - kChar = 2, ///< Based on Char model. - kWord = 3 ///< Based on Word model. -}; - -/// \brief Possible options to specify a specific normalize mode. -enum class DATASET_API NormalizeForm { - kNone = 0, ///< Keep the input string tensor unchanged. - kNfc, ///< Normalize with Normalization Form C. - kNfkc, ///< Normalize with Normalization Form KC. - kNfd, ///< Normalize with Normalization Form D. - kNfkd, ///< Normalize with Normalization Form KD. -}; - -/// \brief Possible options for Mask. -enum class DATASET_API RelationalOp { - kEqual = 0, ///< equal to `==` - kNotEqual, ///< equal to `!=` - kLess, ///< equal to `<` - kLessEqual, ///< equal to `<=` - kGreater, ///< equal to `>` - kGreaterEqual, ///< equal to `>=` -}; - -/// \brief Possible modes for slice patches. -enum class DATASET_API SliceMode { - kPad = 0, ///< Pad some pixels before slice to patches. - kDrop = 1, ///< Drop remainder pixels before slice to patches. -}; - -/// \brief Possible options for SamplingStrategy. -enum class DATASET_API SamplingStrategy { - kRandom = 0, ///< Random sampling with replacement. - kEdgeWeight = 1 ///< Sampling with edge weight as probability. -}; - -/// \brief Possible options for fade shape. -enum class DATASET_API FadeShape { - kLinear = 0, ///< Fade shape is linear mode. - kExponential = 1, ///< Fade shape is exponential mode. - kLogarithmic = 2, ///< Fade shape is logarithmic mode. - kQuarterSine = 3, ///< Fade shape is quarter_sine mode. - kHalfSine = 4, ///< Fade shape is half_sine mode. -}; - -/// \brief Sample method for audio resample. -enum class DATASET_API ResampleMethod { - kSincInterpolation = 0, ///< Resample audio by sinc interpolation method - kKaiserWindow = 1, ///< Resample audio by Kaiser window -}; - -/// \brief Possible configuration methods for processing error samples. -enum class DATASET_API ErrorSamplesMode { - kReturn = 0, ///< Erroneous sample results in error raised and returned - kReplace = 1, ///< Erroneous sample is replaced with an internally determined sample - kSkip = 2 ///< Erroneous sample is skipped -}; - -/// \brief Convenience function to check bitmask for a 32bit int -/// \param[in] bits a 32bit int to be tested -/// \param[in] bitMask a 32bit int representing bit mask -/// \return bool Result for the check -inline bool DATASET_API BitTest(uint32_t bits, uint32_t bitMask) { return (bits & bitMask) == bitMask; } - -/// \brief Convenience function to set bitmask for a 32bit int -/// \param[in] bits a 32bit int to deal with -/// \param[in] bitMask a 32bit int representing bit mask -inline void DATASET_API BitSet(uint32_t *bits, uint32_t bitMask) { - if (bits == nullptr) { - return; - } - *bits |= bitMask; -} - -/// \brief Convenience function to clear bitmask from a 32bit int -/// \param[in] bits a 32bit int to deal with -/// \param[in] bitMask a 32bit int representing bit mask -inline void DATASET_API BitClear(uint32_t *bits, uint32_t bitMask) { - if (bits == nullptr) { - return; - } - *bits &= (~bitMask); -} - -constexpr uint32_t kFrameWidthMax = 4096; -constexpr uint32_t kFrameHeightMax = 4096; -constexpr uint32_t kFrameWidthMin = 128; -constexpr uint32_t kFrameHeightMin = 128; - -constexpr int64_t kDeMaxDim = std::numeric_limits::max(); -constexpr int32_t kDeMaxRank = std::numeric_limits::max(); -constexpr int64_t kDeMaxFreq = std::numeric_limits::max(); // 9223372036854775807 or 2^(64-1) -constexpr int64_t kDeMaxTopk = std::numeric_limits::max(); - -constexpr uint32_t kCfgRowsPerBuffer = 1; -constexpr uint32_t kCfgParallelWorkers = 8; -constexpr uint32_t kCfgWorkerConnectorSize = 16; -constexpr uint32_t kCfgOpConnectorSize = 16; -constexpr uint32_t kCfgSendingBatch = 0; -constexpr int32_t kCfgDefaultRankId = -1; -constexpr uint32_t kCfgDefaultSeed = std::mt19937::default_seed; -constexpr uint32_t kCfgMonitorSamplingInterval = 1000; // timeout value for monitor sampling interval in - // milliseconds -constexpr uint32_t kCfgCallbackTimeout = 60; // timeout value for callback in seconds -constexpr uint32_t kCfgMultiprocessingTimeoutInterval = 300; // timeout value for multiprocessing interval in seconds -constexpr int32_t kCfgDefaultCachePort = 50052; -constexpr char kCfgDefaultCacheHost[] = ""; -constexpr int32_t kDftCachePrefetchSize = 20; -constexpr int32_t kDftNumConnections = 12; -constexpr bool kDftAutoNumWorkers = false; -constexpr char kDftMetaColumnPrefix[] = "_meta-"; -constexpr int32_t kDecimal = 10; // used in strtol() to convert a string value according to decimal numeral system -constexpr int32_t kMinLegalPort = 1025; -constexpr int32_t kMaxLegalPort = 65535; - -// Invalid OpenCV type should not be from 0 to 7 (opencv4/opencv2/core/hal/interface.h) -constexpr uint8_t kCVInvalidType = 255; - -using connection_id_type = uint64_t; -using session_id_type = uint32_t; -using row_id_type = int64_t; - -constexpr uint32_t kCfgAutoTuneInterval = 0; // default number of steps -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_CONSTANTS_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/data_helper.h b/mindspore-lite/minddata/dataset/include/dataset/data_helper.h deleted file mode 100644 index 138a06e07..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/data_helper.h +++ /dev/null @@ -1,487 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATA_HELPER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATA_HELPER_H_ - -#include - -#if (defined(_WIN32) || defined(_WIN64)) && defined(_MSC_VER) -#define F_OK 0 -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" - -namespace mindspore { -namespace dataset { -/// \brief Simple class to do data manipulation, contains helper function to update json files in dataset -class DATASET_API DataHelper { - public: - /// \brief constructor - DataHelper() = default; - - /// \brief Destructor - ~DataHelper() = default; - - /// \brief Create an Album dataset while taking in a path to a image folder - /// Creates the output directory if doesn't exist - /// \param[in] in_dir Image folder directory that takes in images - /// \param[in] out_dir Directory containing output json files - /// \return Status The status code returned - Status CreateAlbum(const std::string &in_dir, const std::string &out_dir) { - return CreateAlbumIF(StringToChar(in_dir), StringToChar(out_dir)); - } - - /// \brief Update a json file field with a vector of string values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional input for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), VectorStringToChar(value), StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of bool values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of int8 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of uint8 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of int16 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of uint16 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of int32 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of uint32 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of int64 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of uint64 values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of float values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a vector of double values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - return UpdateArrayIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a string value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const std::string &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), StringToChar(value), StringToChar(out_file)); - } - - /// \brief Update a json file field with a bool value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const bool &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an int8 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const int8_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an uint8 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const uint8_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an int16 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const int16_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an uint16 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const uint16_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an int32 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const int32_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an uint32 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const uint32_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an int64 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const int64_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with an uint64 value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const uint64_t &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a float value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const float &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Update a json file field with a double value - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateValue(const std::string &in_file, const std::string &key, const double &value, - const std::string &out_file = "") { - return UpdateValueIF(StringToChar(in_file), StringToChar(key), value, StringToChar(out_file)); - } - - /// \brief Template function to write tensor to file - /// \param[in] in_file File to write to - /// \param[in] data Array of type T values - /// \return Status The status code returned - template - Status WriteBinFile(const std::string &in_file, const std::vector &data) { - std::ofstream ofs(in_file, std::ios::binary | std::ios::out); - if (!ofs.is_open()) { - return Status(kMDUnexpectedError, "Failed to open file: " + in_file); - } - size_t length = data.size(); - if (length == 0) { - ofs.close(); - return Status(kMDUnexpectedError, "Input data is empty."); - } - (void)ofs.write(reinterpret_cast(&data[0]), static_cast(length * sizeof(T))); - if (!ofs.good()) { - ofs.close(); - return Status(kMDUnexpectedError, "Failed to write file: " + in_file); - } - ofs.close(); - - // Change the file mode - if (access(in_file.c_str(), F_OK) == -1) { - return Status(kMDUnexpectedError, "Couldn't access the file " + in_file); - } - try { - if (chmod(in_file.c_str(), S_IRUSR | S_IWUSR) != 0) { - return Status(kMDUnexpectedError, "Change file " + in_file + " mode fail."); - } - } catch (std::exception &e) { - return Status(kMDUnexpectedError, "File " + in_file + " change mode failed! May be not exist."); - } - - return Status::OK(); - } - - /// \brief Write pointer to bin, use pointer to avoid memcpy - /// \note The value of `length` must be equal to the length of `data` - /// \param[in] in_file File name to write to - /// \param[in] data Pointer to data - /// \param[in] length Length of values to write from pointer - /// \return Status The status code returned - template - Status WriteBinFile(const std::string &in_file, T *data, size_t length) { - if (data == nullptr) { - return Status(kMDUnexpectedError, "Input data can not be nullptr."); - } - std::ofstream ofs(in_file, std::ios::binary | std::ios::out); - if (!ofs.is_open()) { - return Status(kMDUnexpectedError, "Failed to open file: " + in_file); - } - (void)ofs.write(reinterpret_cast(data), static_cast(length * sizeof(T))); - if (!ofs.good()) { - ofs.close(); - return Status(kMDUnexpectedError, "Failed to write file: " + in_file); - } - ofs.close(); - - // Change the file mode - if (access(in_file.c_str(), F_OK) == -1) { - return Status(kMDUnexpectedError, "Couldn't access the file " + in_file); - } - try { - if (chmod(in_file.c_str(), S_IRUSR | S_IWUSR) != 0) { - return Status(kMDUnexpectedError, "Change file " + in_file + " mode fail."); - } - } catch (std::exception &e) { - return Status(kMDUnexpectedError, "File " + in_file + " change mode failed! May be not exist."); - } - - return Status::OK(); - } - - /// \brief Helper function to copy content of a tensor to buffer - /// \note This function iterates over the tensor in bytes, since - /// \param[in] tensor_addr The memory held by a tensor - /// \param[in] tensor_size The amount of data in bytes in tensor_addr, e.g. tensor->SizeInBytes() - /// \param[out] addr The address to copy tensor data to - /// \param[in] buffer_size The buffer size of addr - /// \return The size of the tensor (bytes copied - size_t DumpData(const unsigned char *tensor_addr, const size_t &tensor_size, void *addr, const size_t &buffer_size); - - /// \brief Helper function to delete key in json file - /// \note This function will return okay even if key not found - /// \param[in] in_file Json file to remove key from - /// \param[in] key The key to remove - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - Status RemoveKey(const std::string &in_file, const std::string &key, const std::string &out_file = "") { - return RemoveKeyIF(StringToChar(in_file), StringToChar(key), StringToChar(out_file)); - } - - /// \brief A print method typically used for debugging - /// \param out - The output stream to write output to - void Print(std::ostream &out) const; - - /// \brief << Stream output operator overload - /// \note This allows you to write the debug print info using stream operators - /// \param out Reference to the output stream being overloaded - /// \param dh Reference to the DataSchema to display - /// \return The output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const DataHelper &dh) { - dh.Print(out); - return out; - } - - private: - // Helper function for dual ABI support - Status CreateAlbumIF(const std::vector &in_dir, const std::vector &out_dir); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector> &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, const std::vector &value, - const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, const std::vector &value, - const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, - const std::vector &value, const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, const std::vector &value, - const std::vector &out_file); - Status UpdateArrayIF(const std::vector &in_file, const std::vector &key, const std::vector &value, - const std::vector &out_file); - - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const std::vector &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const bool &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const int8_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint8_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const int16_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint16_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const int32_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint32_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const int64_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const uint64_t &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const float &value, - const std::vector &out_file); - Status UpdateValueIF(const std::vector &in_file, const std::vector &key, const double &value, - const std::vector &out_file); - Status RemoveKeyIF(const std::vector &in_file, const std::vector &key, const std::vector &out_file); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATA_HELPER_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/datasets.h b/mindspore-lite/minddata/dataset/include/dataset/datasets.h deleted file mode 100644 index ad3981733..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/datasets.h +++ /dev/null @@ -1,6538 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATASETS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATASETS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nlohmann/json_fwd.hpp" - -#include "include/api/dual_abi_helper.h" -#include "include/api/visible.h" -#include "include/dataset/iterator.h" -#include "include/dataset/samplers.h" -#include "include/dataset/text.h" - -namespace mindspore { -namespace dataset { -class CsvBase; -class DatasetCache; -class DatasetNode; -class DSCallback; -class Iterator; -class PullBasedIterator; -class SamplerObj; -class SchemaObj; -enum class SentencePieceModel; -class SentencePieceVocab; -class Tensor; -class TensorOperation; -class TensorShape; -class TreeAdapter; -class TreeAdapterLite; -class TreeGetters; -class Vocab; - -// Dataset classes (in alphabetical order) -class BatchDataset; -class BucketBatchByLengthDataset; -class ConcatDataset; -class CSVDataset; -class FilterDataset; -class MapDataset; -class ProjectDataset; -class RenameDataset; -class RepeatDataset; -class ShuffleDataset; -class SkipDataset; -class TakeDataset; -class TransferDataset; -class ZipDataset; - -/// \class Dataset datasets.h -/// \brief A base class to represent a dataset in the data pipeline. -class DATASET_API Dataset : public std::enable_shared_from_this { - public: - // need friend class so they can access the children_ field - friend class Iterator; - friend class DataQueueNode; - - /// \brief Constructor - Dataset(); - - /// \brief Destructor - virtual ~Dataset() = default; - - /// \brief Get the dataset size - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \return Dataset size. If failed, return -1. - int64_t GetDatasetSize(bool estimate = false); - - /// \brief Get the output type - /// \return A vector contains output DataType of dataset. If failed, return an empty vector. - std::vector GetOutputTypes(); - - /// \brief Get the output shape - /// \return A vector contains output TensorShape of dataset. If failed, return an empty vector. - std::vector> GetOutputShapes(); - - /// \brief Get the batch size - /// \return Batch size configuration of dataset. - int64_t GetBatchSize(); - - /// \brief Get the repeat count - /// \return Repeat count configuration of dataset. - int64_t GetRepeatCount(); - - /// \brief Get the number of classes - /// \return Number of classes of dataset. If failed, return -1. - int64_t GetNumClasses(); - - /// \brief Get the column names - /// \return A vector contains all column names of dataset. If failed, return an empty vector. - std::vector GetColumnNames() { return VectorCharToString(GetColumnNamesCharIF()); } - - /// \brief Get the class indexing - /// \return A map of ClassIndexing of dataset. If failed, return an empty map. - std::vector>> GetClassIndexing() { - return ClassIndexCharToString(GetClassIndexingCharIF()); - } - - /// \brief Function to set runtime number of workers. - /// \param[in] num_workers The number of threads in this operation. - /// \return Shared pointer to the original object. - /// \par Example - /// \code - /// /* Set number of workers(threads) to process the dataset in parallel */ - /// std::shared_ptr ds = ImageFolder(folder_path, true); - /// ds = ds->SetNumWorkers(16); - /// \endcode - std::shared_ptr SetNumWorkers(int32_t num_workers); - - /// \brief A Function to create an PullBasedIterator over the Dataset. - /// \return Shared pointer to the Iterator. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreatePullBasedIterator(); - /// std::unordered_map row; - /// iter->GetNextRow(&row); - /// \endcode - std::shared_ptr CreatePullBasedIterator(); - - /// \brief Function to create an Iterator over the Dataset pipeline. - /// \param[in] num_epochs Number of epochs to run through the pipeline (default=-1, which means infinite epochs). - /// An empty row is returned at the end of each epoch. - /// \return Shared pointer to the Iterator. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreateIterator(); - /// std::unordered_map row; - /// iter->GetNextRow(&row); - /// \endcode - std::shared_ptr CreateIterator(int32_t num_epochs = -1) { return CreateIteratorCharIF(num_epochs); } - - /// \brief Function to transfer data through a device. - /// \note If device is Ascend, features of data will be transferred one by one. The limitation - /// of data transmission per time is 256M. - /// \param[in] queue_name Channel name (default="", create new unique name). - /// \param[in] device_type Type of device (default="", get from MSContext). - /// \param[in] device_id id of device (default=1, get from MSContext). - /// \param[in] num_epochs Number of epochs (default=-1, infinite epochs). - /// \param[in] send_epoch_end Whether to send end of sequence to device or not (default=true). - /// \param[in] total_batches Number of batches to be sent to the device (default=0, all data). - /// \param[in] create_data_info_queue Whether to create queue which stores types and shapes - /// of data or not (default=false). - /// \return Returns true if no error encountered else false. - bool DeviceQueue(const std::string &queue_name = "", const std::string &device_type = "", int32_t device_id = 0, - int32_t num_epochs = -1, bool send_epoch_end = true, int32_t total_batches = 0, - bool create_data_info_queue = false) { - return DeviceQueueCharIF(StringToChar(queue_name), StringToChar(device_type), device_id, num_epochs, send_epoch_end, - total_batches, create_data_info_queue); - } - - /// \brief Function to create a Saver to save the dynamic data processed by the dataset pipeline. - /// \note Usage restrictions: - /// 1. Supported dataset formats: 'mindrecord' only. - /// 2. To save the samples in order, set dataset's shuffle to false and num_files to 1. - /// 3. Before calling the function, do not use batch operation, repeat operation or data augmentation operations - /// with random attribute in map operation. - /// 4. Mindrecord does not support bool, uint64, multi-dimensional uint8(drop dimension) nor - /// multi-dimensional string. - /// \param[in] dataset_path Path to dataset file. - /// \param[in] num_files Number of dataset files (default=1). - /// \param[in] dataset_type Dataset format (default="mindrecord"). - /// \return Returns true if no error encountered else false. - /// \par Example - /// \code - /// /* Create a dataset and save its data into MindRecord */ - /// std::string folder_path = "/path/to/cifar_dataset"; - /// std::shared_ptr ds = Cifar10(folder_path, "all", std::make_shared(0, 10)); - /// std::string save_file = "Cifar10Data.mindrecord"; - /// bool rc = ds->Save(save_file); - /// \endcode - bool Save(const std::string &dataset_path, int32_t num_files = 1, const std::string &dataset_type = "mindrecord") { - return SaveCharIF(StringToChar(dataset_path), num_files, StringToChar(dataset_type)); - } - - /// \brief Function to create a BatchDataset. - /// \note Combines batch_size number of consecutive rows into batches. - /// \param[in] batch_size The number of rows each batch is created with. - /// \param[in] drop_remainder Determines whether or not to drop the last possibly incomplete - /// batch. If true, and if there are less than batch_size rows - /// available to make the last batch, then those rows will - /// be dropped and not propagated to the next node. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset where every 100 rows is combined into a batch */ - /// std::shared_ptr ds = ImageFolder(folder_path, true); - /// ds = ds->Batch(100, true); - /// \endcode - std::shared_ptr Batch(int32_t batch_size, bool drop_remainder = false); - - /// \brief Function to create a BucketBatchByLengthDataset. - /// \note Bucket elements according to their lengths. Each bucket will be padded and batched when - /// they are full. - /// \param[in] column_names Columns passed to element_length_function. - /// \param[in] bucket_boundaries A list consisting of the upper boundaries of the buckets. - /// Must be strictly increasing. If there are n boundaries, n+1 buckets are created: One bucket for - /// [0, bucket_boundaries[0]), one bucket for [bucket_boundaries[i], bucket_boundaries[i+1]) for each - /// 0 ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->BucketBatchByLength({"image"}, {1, 2, 3}, {4, 5, 6, 7}); - /// \endcode - std::shared_ptr BucketBatchByLength( - const std::vector &column_names, const std::vector &bucket_boundaries, - const std::vector &bucket_batch_sizes, - const std::function &element_length_function = nullptr, - const std::map, MSTensor>> &pad_info = {}, - bool pad_to_bucket_boundary = false, bool drop_remainder = false) { - return std::make_shared( - shared_from_this(), VectorStringToChar(column_names), bucket_boundaries, bucket_batch_sizes, - element_length_function, MapStringToChar(pad_info), pad_to_bucket_boundary, drop_remainder); - } - - /// \brief Function to create a SentencePieceVocab from source dataset. - /// \note Build a SentencePieceVocab from a dataset. - /// \param[in] col_names Column names to get words from. It can be a vector of column names. - /// \param[in] vocab_size Vocabulary size. - /// \param[in] character_coverage Percentage of characters covered by the model, must be between - /// 0.98 and 1.0 Good defaults are: 0.9995 for languages with rich character sets like - /// Japanese or Chinese character sets, and 1.0 for other languages with small character sets. - /// \param[in] model_type Model type. Choose from unigram (default), bpe, char, or word. - /// The input sentence must be pretokenized when using word type. - /// \param[in] params A vector contains more option parameters of sentencepiece library. - /// \return Shared pointer to the SentencePieceVocab. - /// \par Example - /// \code - /// /* Build a SentencePieceVocab from TextFile dataset */ - /// std::string vocab_file = "/path/to/txtfile"; - /// std::shared_ptr ds_vocab = TextFile({vocab_file}, 0, ShuffleMode::kFalse); - /// std::shared_ptr vocab = - /// ds_vocab->BuildSentencePieceVocab({}, 5000, 0.9995, SentencePieceModel::kUnigram, {}); - /// \endcode - std::shared_ptr BuildSentencePieceVocab( - const std::vector &col_names, int32_t vocab_size, float character_coverage, - SentencePieceModel model_type, const std::unordered_map ¶ms) { - return BuildSentencePieceVocabCharIF(VectorStringToChar(col_names), vocab_size, character_coverage, model_type, - UnorderedMapStringToChar(params)); - } - - /// \brief Function to create a Vocab from source dataset. - /// \note Build a vocab from a dataset. This would collect all the unique words in a dataset and return a vocab - /// which contains top_k most frequent words (if top_k is specified). - /// \param[in] columns Column names to get words from. It can be a vector of column names. - /// \param[in] freq_range A tuple of integers (min_frequency, max_frequency). Words within the frequency - /// range would be kept. 0 <= min_frequency <= max_frequency <= total_words. min_frequency/max_frequency - /// can be set to default, which corresponds to 0/total_words separately. - /// \param[in] top_k Number of words to be built into vocab. top_k most frequent words are - /// taken. The top_k is taken after freq_range. If not enough top_k, all words will be taken. - /// \param[in] special_tokens A list of strings, each one is a special token. - /// \param[in] special_first Whether special_tokens will be prepended/appended to vocab, If special_tokens - /// is specified and special_first is set to default, special_tokens will be prepended. - /// \return Shared pointer to the Vocab. - /// \par Example - /// \code - /// /* Build a Vocab from TextFile dataset */ - /// std::string vocab_file = "/path/to/txtfile"; - /// std::shared_ptr ds = TextFile({vocab_file}, 0, ShuffleMode::kFalse); - /// std::shared_ptr vocab = ds->BuildVocab(); - /// \endcode - std::shared_ptr BuildVocab(const std::vector &columns = {}, - const std::pair &freq_range = {0, kDeMaxFreq}, - int64_t top_k = kDeMaxTopk, const std::vector &special_tokens = {}, - bool special_first = true) { - return BuildVocabCharIF(VectorStringToChar(columns), freq_range, top_k, VectorStringToChar(special_tokens), - special_first); - } - - /// \brief Function to create a ConcatDataset. - /// \note Concat the datasets in the input. - /// \param[in] datasets List of shared pointers to the dataset that should be concatenated together. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset by concatenating dataset_1 and dataset_2 with "+" operator */ - /// std::shared_ptr dataset = dataset_1 + dataset_2; - /// /* Create a dataset by concatenating dataset_1 and dataset_2 with concat operation */ - /// std::shared_ptr dataset = dataset_1->Concat({dataset_2}); - /// \endcode - std::shared_ptr Concat(const std::vector> &datasets) { - std::vector> all_datasets{shared_from_this()}; - (void)all_datasets.insert(all_datasets.end(), datasets.begin(), datasets.end()); - return std::make_shared(all_datasets); - } - - /// \brief Function to filter dataset by predicate. - /// \note If input_columns is not provided or empty, all columns will be used. - /// \param[in] predicate Function callable which returns a boolean value. If false then filter the element. - /// \param[in] input_columns List of names of the input columns to filter. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Define a predicate function */ - /// MSTensorVec Predicate1(MSTensorVec in) { - /// // Return true if input is equal to 3 - /// uint64_t input_value; - /// TensorRow input = VecToRow(in); - /// (void)input.at(0)->GetItemAt(&input_value, {0}); - /// bool result = (input_value == 3); - /// // Convert from boolean to TensorRow - /// TensorRow output; - /// std::shared_ptr out; - /// (void)Tensor::CreateEmpty(mindspore::dataset::TensorShape({}), - /// mindspore::dataset::DataType(mindspore::dataset::DataType::Type::DE_BOOL), &out); - /// (void)out->SetItemAt({}, result); - /// output.push_back(out); - /// return RowToVec(output); - /// } - /// - /// /* Apply predicate function for datase */ - /// std::shared_ptr ds = ds->Filter(Predicate1, {"label"}); - /// \endcode - std::shared_ptr Filter(const std::function &predicate, - const std::vector &input_columns = {}) { - return std::make_shared(shared_from_this(), predicate, VectorStringToChar(input_columns)); - } - - /// \brief Function to create a MapDataset. - /// \note Applies each operation in operations to this dataset. - /// \param[in] operations Vector of raw pointers to TensorTransform objects to be applied on the dataset. Operations - /// are applied in the order they appear in this list. - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operation. The default input_columns - /// is the first column. - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation. - /// This parameter is mandatory if len(input_columns) != len(output_columns). - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced. - /// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// // Create objects for the tensor ops - /// std::shared_ptr decode_op = std::make_shared(true); - /// std::shared_ptr resize_op = std::make_shared({224, 224}); - /// - /// /* 1) Simple map example */ - /// // Apply decode_op on column "image". This column will be replaced by the outputted - /// // column of decode_op. - /// dataset = dataset->Map({decode_op}, {"image"}); - /// - /// // Decode and rename column "image" to "decoded_image". - /// dataset = dataset->Map({decode_op}, {"image"}, {"decoded_image"}); - /// - /// /* 2) Map example with more than one operation */ - /// // Create a dataset where the images are decoded, then randomly color jittered. - /// // decode_op takes column "image" as input and outputs one column. The column - /// // outputted by decode_op is passed as input to resize_op. - /// // resize_op will output one column. Column "image" will be replaced by - /// // the column outputted by resize_op (the very last operation). All other - /// // columns are unchanged. - /// dataset = dataset->Map({decode_op, resize_op}, {"image"}) - /// \endcode - std::shared_ptr Map(const std::vector &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform( - operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](TensorTransform *op) -> std::shared_ptr { return op != nullptr ? op->Parse() : nullptr; }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a MapDataset. - /// \note Applies each operation in operations to this dataset. - /// \param[in] operations Vector of shared pointers to TensorTransform objects to be applied on the dataset. - /// Operations are applied in the order they appear in this list. - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operation. The default input_columns - /// is the first column. - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation. - /// This parameter is mandatory if len(input_columns) != len(output_columns). - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current Dataset. - std::shared_ptr Map(const std::vector> &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform(operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a MapDataset. - /// \note Applies each operation in operations to this dataset. - /// \param[in] operations Vector of TensorTransform objects to be applied on the dataset. Operations are applied in - /// the order they appear in this list. - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operation. The default input_columns - /// is the first column. - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation. - /// This parameter is mandatory if len(input_columns) != len(output_columns). - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current Dataset. - std::shared_ptr Map(const std::vector> &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform(operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a Project Dataset. - /// \note Applies project to the dataset. - /// \param[in] columns The name of columns to project. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Reorder the original column names in dataset */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Project({"label", "image"}); - /// \endcode - std::shared_ptr Project(const std::vector &columns) { - return std::make_shared(shared_from_this(), VectorStringToChar(columns)); - } - - /// \brief Function to create a Rename Dataset. - /// \note Renames the columns in the input dataset. - /// \param[in] input_columns List of the input columns to rename. - /// \param[in] output_columns List of the output columns. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Rename the original column names in dataset */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Rename({"image", "label"}, {"image_output", "label_output"}); - /// \endcode - std::shared_ptr Rename(const std::vector &input_columns, - const std::vector &output_columns) { - return std::make_shared(shared_from_this(), VectorStringToChar(input_columns), - VectorStringToChar(output_columns)); - } - /// \brief Function to create a RepeatDataset. - /// \note Repeats this dataset count times. Repeat indefinitely if count is -1. - /// \param[in] count Number of times the dataset should be repeated. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset where the dataset is repeated for 50 epochs */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Repeat(50); - /// \endcode - std::shared_ptr Repeat(int32_t count = -1) { - return std::make_shared(shared_from_this(), count); - } - - /// \brief Function to create a Shuffle Dataset. - /// \note Randomly shuffles the rows of this dataset. - /// \param[in] buffer_size The size of the buffer (must be larger than 1) for shuffling - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a shuffled dataset using a shuffle buffer of size 4 */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Shuffle(4); - /// \endcode - std::shared_ptr Shuffle(int32_t buffer_size) { - return std::make_shared(shared_from_this(), buffer_size); - } - - /// \brief Function to create a SkipDataset. - /// \note Skips count elements in this dataset. - /// \param[in] count Number of elements the dataset to be skipped. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset which skips first 3 elements from data */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Skip(3); - /// \endcode - std::shared_ptr Skip(int32_t count) { return std::make_shared(shared_from_this(), count); } - - /// \brief Function to create a TakeDataset. - /// \note Takes count elements in this dataset. - /// \param[in] count Number of elements the dataset to be taken. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset where the dataset includes 50 elements. */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Take(50); - /// \endcode - std::shared_ptr Take(int32_t count = -1) { - return std::make_shared(shared_from_this(), count); - } - - /// \brief Function to create a Zip Dataset. - /// \note Applies zip to the dataset. - /// \param[in] datasets A list of shared pointers to the datasets that we want to zip. - /// \return Shared pointer to the current Dataset. - /// \par Example - /// \code - /// /* Create a dataset which is the combination of dataset and dataset_1 */ - /// std::shared_ptr ds1 = ImageFolder(folder_path, true, std::make_shared(false, 10)); - /// std::shared_ptr ds2 = Cifar10(folder_path, "all", std::make_shared(false, 10)); - /// std::shared_ptr ds = ds->Zip({ds1, ds2}); - /// \endcode - std::shared_ptr Zip(const std::vector> &datasets) { - std::vector> all_datasets = datasets; - all_datasets.push_back(shared_from_this()); - return std::make_shared(all_datasets); - } - - std::shared_ptr IRNode() { return ir_node_; } - - protected: - std::shared_ptr tree_getters_; - std::shared_ptr ir_node_; - - private: - // Char interface(CharIF) of GetColumnNames - std::vector> GetColumnNamesCharIF(); - - // Char interface(CharIF) of GetClassIndexing - std::vector, std::vector>> GetClassIndexingCharIF(); - - // Char interface(CharIF) of CreateIterator - std::shared_ptr CreateIteratorCharIF(int32_t num_epochs); - - // Char interface(CharIF) of DeviceQueue - bool DeviceQueueCharIF(const std::vector &queue_name, const std::vector &device_type, int32_t device_id, - int32_t num_epochs, bool send_epoch_end, int32_t total_batches, bool create_data_info_queue); - - // Char interface(CharIF) of Save - bool SaveCharIF(const std::vector &dataset_path, int32_t num_files, const std::vector &dataset_type); - - // Char interface(CharIF) of BuildSentencePieceVocab - std::shared_ptr BuildSentencePieceVocabCharIF( - const std::vector> &col_names, int32_t vocab_size, float character_coverage, - SentencePieceModel model_type, const std::map, std::vector> ¶ms); - - // Char interface(CharIF) of BuildVocab - std::shared_ptr BuildVocabCharIF(const std::vector> &columns, - const std::pair &freq_range, int64_t top_k, - const std::vector> &special_tokens, bool special_first); -}; - -/// \class SchemaObj -/// \brief A schema object to set column type, data type and data shape. -class DATASET_API SchemaObj { - public: - /// \brief Constructor - explicit SchemaObj(const std::string &schema_file = "") : SchemaObj(StringToChar(schema_file)) {} - - /// \brief Destructor - ~SchemaObj() = default; - - /// \brief SchemaObj Init function. - /// \return bool true if schema initialization is successful. - Status Init(); - - /// \brief Add new column to the schema with unknown shape of rank 1. - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(mindspore::DataType). - /// \return Status code. - Status add_column(const std::string &name, mindspore::DataType ms_type) { - return add_column_char(StringToChar(name), ms_type); - } - - /// \brief Add new column to the schema with unknown shape of rank 1. - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(std::string). - /// \param[in] shape Shape of the column. - /// \return Status code. - Status add_column(const std::string &name, const std::string &ms_type) { - return add_column_char(StringToChar(name), StringToChar(ms_type)); - } - - /// \brief Add new column to the schema. - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(mindspore::DataType). - /// \param[in] shape Shape of the column. - /// \return Status code. - Status add_column(const std::string &name, mindspore::DataType ms_type, const std::vector &shape) { - return add_column_char(StringToChar(name), ms_type, shape); - } - - /// \brief Add new column to the schema. - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(std::string). - /// \param[in] shape Shape of the column. - /// \return Status code. - Status add_column(const std::string &name, const std::string &ms_type, const std::vector &shape) { - return add_column_char(StringToChar(name), StringToChar(ms_type), shape); - } - - /// \brief Get a JSON string of the schema. - /// \return JSON string of the schema. - std::string to_json() { return CharToString(to_json_char()); } - - /// \brief Serialize schema config to JSON. - /// \return Status code. - Status schema_to_json(nlohmann::json *out_json); - - /// \brief Get a JSON string of the schema. - /// \return JSON string of the schema. - std::string to_string() { return to_json(); } - - /// \brief Set a new value to dataset_type. - /// \param[in] dataset_type Data type of the schema. - void set_dataset_type(const std::string &dataset_type); - - /// \brief Set a new value to num_rows. - /// \param[in] dataset_type Number of rows of the schema. - void set_num_rows(int32_t num_rows); - - /// \brief Get the current num_rows - /// \return Number of rows. - int32_t get_num_rows() const; - - /// \brief Get schema file from JSON file. - /// \param[in] json_obj parsed JSON object. - /// \return Status code. - Status from_json(nlohmann::json json_obj); - - /// \brief Get schema file from JSON file. - /// \param[in] json_string Name of JSON file to be parsed. - /// \return Status code. - Status FromJSONString(const std::string &json_string) { return FromJSONStringCharIF(StringToChar(json_string)); } - - /// \brief Parse and add column information. - /// \param[in] json_string Name of JSON string for column dataset attribute information, decoded from schema file. - /// \return Status code. - Status ParseColumnString(const std::string &json_string) { - return ParseColumnStringCharIF(StringToChar(json_string)); - } - - private: - /// \brief Parse the columns and add them to columns. - /// \param[in] columns Dataset attribution information, decoded from schema file. - /// Support both nlohmann::json::value_t::array and nlohmann::json::value_t::onject. - /// \return Status code. - Status parse_column(nlohmann::json columns); - - // Char constructor of SchemaObj - explicit SchemaObj(const std::vector &schema_file); - - // Char interface of add_column - Status add_column_char(const std::vector &name, mindspore::DataType ms_type); - - Status add_column_char(const std::vector &name, const std::vector &ms_type); - - Status add_column_char(const std::vector &name, mindspore::DataType ms_type, const std::vector &shape); - - Status add_column_char(const std::vector &name, const std::vector &ms_type, - const std::vector &shape); - - // Char interface of to_json - std::vector to_json_char(); - - // Char interface of FromJSONString - Status FromJSONStringCharIF(const std::vector &json_string); - - // Char interface of ParseColumnString - Status ParseColumnStringCharIF(const std::vector &json_string); - - struct Data; - std::shared_ptr data_; -}; - -/// \class BatchDataset -/// \brief The result of applying Batch operation to the input dataset. -class DATASET_API BatchDataset : public Dataset { - public: - /// \brief Constructor of BatchDataset. - /// \note Combines batch_size number of consecutive rows into batches. - /// \param[in] input The dataset which need to apply batch operation. - /// \param[in] batch_size The number of rows each batch is created with. - /// \param[in] drop_remainder Determines whether or not to drop the last possibly incomplete - /// batch. If true, and if there are less than batch_size rows - /// available to make the last batch, then those rows will - /// be dropped and not propagated to the next node. - BatchDataset(const std::shared_ptr &input, int32_t batch_size, bool drop_remainder = false); - - /// \brief Destructor of BatchDataset. - ~BatchDataset() override = default; -}; - -/// \class BucketBatchByLengthDataset -/// \brief The result of applying BucketBatchByLength operation to the input dataset. -class DATASET_API BucketBatchByLengthDataset : public Dataset { - public: - /// \brief Constructor of BucketBatchByLengthDataset. - /// \note Bucket elements according to their lengths. Each bucket will be padded and batched when - /// they are full. - /// \param[in] input The dataset which need to apply bucket batch by length operation. - /// \param[in] column_names Columns passed to element_length_function. - /// \param[in] bucket_boundaries A list consisting of the upper boundaries of the buckets. - /// Must be strictly increasing. If there are n boundaries, n+1 buckets are created: One bucket for - /// [0, bucket_boundaries[0]), one bucket for [bucket_boundaries[i], bucket_boundaries[i+1]) for each - /// 0 &input, const std::vector> &column_names, - const std::vector &bucket_boundaries, const std::vector &bucket_batch_sizes, - const std::function &element_length_function = nullptr, - const std::map, std::pair, MSTensor>> &pad_info = {}, - bool pad_to_bucket_boundary = false, bool drop_remainder = false); - - /// \brief Destructor of BucketBatchByLengthDataset. - ~BucketBatchByLengthDataset() override = default; -}; - -/// \class ConcatDataset -/// \brief The result of applying Concat operation to the input Dataset. -class DATASET_API ConcatDataset : public Dataset { - public: - /// \brief Constructor of ConcatDataset. - /// \note Concat the datasets in the input. - /// \param[in] input List of shared pointers to the dataset that should be concatenated together. - explicit ConcatDataset(const std::vector> &datasets); - - /// \brief Destructor of ConcatDataset. - ~ConcatDataset() override = default; -}; - -/// \class FilterDataset -/// \brief The result of applying filter predicate to the input Dataset. -class DATASET_API FilterDataset : public Dataset { - public: - /// \brief Constructor of FilterDataset. - /// \note If input_columns is not provided or empty, all columns will be used. - /// \param[in] input The dataset which need to apply filter operation. - /// \param[in] predicate Function callable which returns a boolean value. If false then filter the element. - /// \param[in] input_columns List of names of the input columns to filter. - FilterDataset(const std::shared_ptr &input, const std::function &predicate, - const std::vector> &input_columns); - - /// \brief Destructor of FilterDataset. - ~FilterDataset() override = default; -}; - -/// \class MapDataset -/// \brief The result of applying the Map operation to the input Dataset. -class DATASET_API MapDataset : public Dataset { - public: - /// \brief Constructor of MapDataset. - /// \note Applies each operation in operations to this dataset. - /// \param[in] input The dataset which need to apply map operation. - /// \param[in] operations Vector of raw pointers to TensorTransform objects to be applied on the dataset. Operations - /// are applied in the order they appear in this list. - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operation. The default input_columns - /// is the first column. - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation. - /// This parameter is mandatory if len(input_columns) != len(output_columns). - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - MapDataset(const std::shared_ptr &input, const std::vector> &operations, - const std::vector> &input_columns, const std::vector> &output_columns, - const std::shared_ptr &cache, const std::vector> &callbacks); - - /// \brief Destructor of MapDataset. - ~MapDataset() override = default; -}; - -/// \class ProjectDataset -/// \brief The result of applying the Project operation to the input Dataset. -class DATASET_API ProjectDataset : public Dataset { - public: - /// \brief Constructor of ProjectDataset. - /// \note Applies project to the dataset. - /// \param[in] input The dataset which need to apply project operation. - /// \param[in] columns The name of columns to project. - ProjectDataset(const std::shared_ptr &input, const std::vector> &columns); - - /// \brief Destructor of ProjectDataset. - ~ProjectDataset() override = default; -}; - -/// \class RenameDataset -/// \brief The result of applying the Rename operation to the input Dataset. -class DATASET_API RenameDataset : public Dataset { - public: - /// \brief Constructor of RenameDataset. - /// \note Renames the columns in the input dataset. - /// \param[in] input The dataset which need to apply rename operation. - /// \param[in] input_columns List of the input columns to rename. - /// \param[in] output_columns List of the output columns. - RenameDataset(const std::shared_ptr &input, const std::vector> &input_columns, - const std::vector> &output_columns); - - /// \brief Destructor of RenameDataset. - ~RenameDataset() override = default; -}; - -/// \class RepeatDataset -/// \brief The result of applying the Repeat operation to the input Dataset. -class DATASET_API RepeatDataset : public Dataset { - public: - /// \brief Constructor of RepeatDataset. - /// \note Repeats this dataset count times. Repeat indefinitely if count is -1. - /// \param[in] input The dataset which need to apply repeat operation. - /// \param[in] count Number of times the dataset should be repeated. - RepeatDataset(const std::shared_ptr &input, int32_t count); - - /// \brief Destructor of RepeatDataset. - ~RepeatDataset() override = default; -}; - -/// \class ShuffleDataset -/// \brief The result of applying the Shuffle operation to the input Dataset. -class DATASET_API ShuffleDataset : public Dataset { - public: - /// \brief Constructor of ShuffleDataset. - /// \note Randomly shuffles the rows of this dataset. - /// \param[in] input The dataset which need to apply shuffle operation. - /// \param[in] buffer_size The size of the buffer (must be larger than 1) for shuffling - ShuffleDataset(const std::shared_ptr &input, int32_t buffer_size); - - /// \brief Destructor of ShuffleDataset. - ~ShuffleDataset() override = default; -}; - -/// \class SkipDataset -/// \brief The result of applying the Skip operation to the input Dataset. -class DATASET_API SkipDataset : public Dataset { - public: - /// \brief Constructor of SkipDataset. - /// \note Skips count elements in this dataset. - /// \param[in] input The dataset which need to apply skip operation. - /// \param[in] count Number of elements the dataset to be skipped. - SkipDataset(const std::shared_ptr &input, int32_t count); - - /// \brief Destructor of SkipDataset. - ~SkipDataset() override = default; -}; - -/// \class TakeDataset -/// \brief The result of applying the Take operation to the input Dataset. -class DATASET_API TakeDataset : public Dataset { - public: - /// \brief Constructor of TakeDataset. - /// \note Takes count elements in this dataset. - /// \param[in] input The dataset which need to apply take operation. - /// \param[in] count Number of elements the dataset to be taken. - TakeDataset(const std::shared_ptr &input, int32_t count); - - /// \brief Destructor of TakeDataset. - ~TakeDataset() override = default; -}; - -/// \class ZipDataset -/// \brief The result of applying the Zip operation to the input Dataset. -class DATASET_API ZipDataset : public Dataset { - public: - /// \brief Constructor of ZipDataset. - /// \note Applies zip to the dataset. - /// \param[in] inputs A list of shared pointers to the datasets that we want to zip. - explicit ZipDataset(const std::vector> &datasets); - - /// \brief Destructor of ZipDataset. - ~ZipDataset() override = default; -}; - -/// \brief Function to create a SchemaObj. -/// \param[in] schema_file Path of schema file. -/// \note The reason for using this API is that std::string will be constrained by the -/// compiler option '_GLIBCXX_USE_CXX11_ABI' while char is free of this restriction. -/// Check API `mindspore::dataset::Schema` and find more usage. -/// \return Shared pointer to the current schema. -std::shared_ptr DATASET_API SchemaCharIF(const std::vector &schema_file); - -/// \brief Function to create a SchemaObj. -/// \param[in] schema_file Path of schema file (default = "", which means do not set the path). -/// \return Shared pointer to the current schema. -/// \par Example -/// \code -/// /* Define a schema to make RandomDataset generate specific data. */ -/// std::shared_ptr schema = Schema(); -/// schema->add_column("image", mindspore::DataType::kNumberTypeUInt8, {2}); -/// schema->add_column("label", mindspore::DataType::kNumberTypeUInt8, {1}); -/// std::shared_ptr ds = RandomData(50, schema); -/// \endcode -inline std::shared_ptr DATASET_API Schema(const std::string &schema_file = "") { - return SchemaCharIF(StringToChar(schema_file)); -} - -/// \class AGNewsDataset -/// \brief A source dataset that reads and parses AG News datasets. -class DATASET_API AGNewsDataset : public Dataset { - public: - /// \brief Constructor of AGNewsDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data list csv file to be read, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - AGNewsDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of AGNewsDataset. - ~AGNewsDataset() override = default; -}; - -/// \brief Function to create a AGNewsDataset. -/// \note The generated dataset has three columns ['index', 'title', 'description']. -/// The index range is [1, 4]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train" or "test" (default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0 means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch -/// (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1) -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use.(default=nullptr which means no cache is used). -/// \return Shared pointer to the AGNewsDataset. -inline std::shared_ptr DATASET_API AGNews(const std::string &dataset_dir, - const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class AlbumDataset -/// \brief A source dataset for reading and parsing Album dataset. -class DATASET_API AlbumDataset : public Dataset { - public: - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load, if empty, will read all columns - /// (default = {}). - /// \param[in] decode The option to decode the images in dataset (default = false). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load. - /// \param[in] decode The option to decode the images in dataset. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load. - /// \param[in] decode The option to decode the images in dataset. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of AlbumDataset. - ~AlbumDataset() override = default; -}; - -/// \brief Function to create an AlbumDataset. -/// \note The generated dataset is specified through setting a schema. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] data_schema Path to dataset schema file. -/// \param[in] column_names Column names used to specify columns to load, if empty, will read all columns -/// (default = {}). -/// \param[in] decode The option to decode the images in dataset (default = false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the AlbumDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/album_dataset_directory"; -/// std::string schema_file = "/path/to/album_schema_file"; -/// std::vector column_names = {"image", "label", "id"}; -/// std::shared_ptr ds = Album(folder_path, schema_file, column_names); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, each data dictionary owns keys "image", "label" and "id" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Album(const std::string &dataset_dir, const std::string &data_schema, const std::vector &column_names = {}, - bool decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -/// \brief Function to create an AlbumDataset. -/// \note The generated dataset is specified through setting a schema. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] data_schema Path to dataset schema file. -/// \param[in] column_names Column names used to specify columns to load. -/// \param[in] decode The option to decode the images in dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the AlbumDataset -inline std::shared_ptr DATASET_API Album(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -/// \brief Function to create an AlbumDataset. -/// \note The generated dataset is specified through setting a schema. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] data_schema Path to dataset schema file. -/// \param[in] column_names Column names used to specify columns to load. -/// \param[in] decode The option to decode the images in dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the AlbumDataset. -inline std::shared_ptr DATASET_API Album(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -/// \class AmazonReviewDataset -/// \brief A source dataset for reading and parsing Amazon Review Polarity and Amazon Review Full datasets. -class DATASET_API AmazonReviewDataset : public Dataset { - public: - /// \brief Constructor of AmazonReviewDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of AmazonReview, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - AmazonReviewDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of AmazonReviewDataset. - ~AmazonReviewDataset() override = default; -}; - -/// \brief Function to create a AmazonReviewDataset. -/// \note This dataset includes polarity and full, which can be read according to your own needs. -/// The generated dataset has three columns ["label","title","content"]. Their types are all string. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of AmazonReview, can be "train", "test" or "all" (default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, which means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the AmazonReviewDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/amazon_review_dataset_directory"; -/// std::shared_ptr ds = AmazonReview(folder_path, "test", 0, ShuffleMode::kGlobal); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In AmazonReview dataset, each data dictionary owns keys "label", "title", "content" */ -/// auto title = row["title"]; -/// \endcode -inline std::shared_ptr DATASET_API -AmazonReview(const std::string &dataset_dir, const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class Caltech256Dataset -/// \brief A source dataset for reading and parsing Caltech256 dataset. -class DATASET_API Caltech256Dataset : public Dataset { - public: - /// \brief Constructor of Caltech256Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Caltech256Dataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of Caltech256Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Caltech256Dataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of Caltech256Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Caltech256Dataset(const std::vector &dataset_dir, bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of Caltech256Dataset. - ~Caltech256Dataset() override = default; -}; - -/// \brief Function to create a Caltech256Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the Caltech256Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/caltech256_dataset_directory"; -/// std::shared_ptr ds = Caltech256(dataset_path, true, std::make_shared(false, 10)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Caltech256 dataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Caltech256(const std::string &dataset_dir, const std::shared_ptr &sampler = std::make_shared(), - bool decode = false, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a Caltech256Dataset -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the Caltech256Dataset. -inline std::shared_ptr DATASET_API Caltech256(const std::string &dataset_dir, const Sampler *sampler, - bool decode = false, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a Caltech256Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the Caltech256Dataset. -inline std::shared_ptr DATASET_API Caltech256(const std::string &dataset_dir, - const std::reference_wrapper &sampler, - bool decode = false, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \class CelebADataset -/// \brief A source dataset for reading and parsing CelebA dataset. -class DATASET_API CelebADataset : public Dataset { - public: - /// \brief Constructor of CelebADataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage One of "all", "train", "valid" or "test" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] extensions Set of file extensions to be included in the dataset (default={}). - /// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). - CelebADataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, bool decode, const std::set> &extensions, - const std::shared_ptr &cache); - - /// \brief Constructor of CelebADataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage One of "all", "train", "valid" or "test". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] extensions Set of file extensions to be included in the dataset (default={}). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - CelebADataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - bool decode, const std::set> &extensions, const std::shared_ptr &cache); - - /// \brief Constructor of CelebADataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage One of "all", "train", "valid" or "test". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] extensions Set of file extensions to be included in the dataset (default={}). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - CelebADataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, bool decode, - const std::set> &extensions, const std::shared_ptr &cache); - - /// \brief Destructor of CelebADataset. - ~CelebADataset() override = default; -}; - -/// \brief Function to create a CelebADataset. -/// \note The generated dataset has two columns ['image', 'attr']. -/// The type of the image tensor is uint8. The attr tensor is uint32 and one hot type. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train", "valid" or "test" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] extensions Set of file extensions to be included in the dataset (default={}). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the CelebADataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/celeba_dataset_directory"; -/// std::shared_ptr ds = CelebA(folder_path, "all", std::make_shared(0, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In CelebA dataset, each data dictionary owns keys "image" and "attr" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -CelebA(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), bool decode = false, - const std::set &extensions = {}, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, decode, - SetStringToChar(extensions), cache); -} - -/// \brief Function to create a CelebADataset. -/// \note The generated dataset has two columns ['image', 'attr']. -/// The type of the image tensor is uint8. The attr tensor is uint32 and one hot type. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train", "valid" or "test". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] extensions Set of file extensions to be included in the dataset (default={}). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the CelebADataset. -inline std::shared_ptr DATASET_API CelebA(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, bool decode = false, - const std::set &extensions = {}, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, decode, - SetStringToChar(extensions), cache); -} - -/// \brief Function to create a CelebADataset. -/// \note The generated dataset has two columns ['image', 'attr']. -/// The type of the image tensor is uint8. The attr tensor is uint32 and one hot type. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train", "valid" or "test". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] extensions Set of file extensions to be included in the dataset (default={}). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the CelebADataset. -inline std::shared_ptr DATASET_API CelebA(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - bool decode = false, - const std::set &extensions = {}, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, decode, - SetStringToChar(extensions), cache); -} - -/// \class Cifar10Dataset -/// \brief A source dataset for reading and parsing Cifar10 dataset. -class DATASET_API Cifar10Dataset : public Dataset { - public: - /// \brief Constructor of Cifar10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Cifar10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of Cifar10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of Cifar10Dataset. - ~Cifar10Dataset() override = default; -}; - -/// \brief Function to create a Cifar10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar10Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/cifar10_dataset_directory"; -/// std::shared_ptr ds = Cifar10(folder_path, "all", std::make_shared(false, 10)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In CIFAR10 dataset, each data dictionary owns keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Cifar10(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a Cifar10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar10Dataset. -inline std::shared_ptr DATASET_API Cifar10(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a Cifar10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR10, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar10Dataset. -inline std::shared_ptr DATASET_API Cifar10(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class Cifar100Dataset -/// \brief A source dataset for reading and parsing Cifar100 dataset. -class DATASET_API Cifar100Dataset : public Dataset { - public: - /// \brief Constructor of Cifar100Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Cifar100Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of Cifar100Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Cifar100Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of Cifar100Dataset. - ~Cifar100Dataset() override = default; -}; - -/// \brief Function to create a Cifar100 Dataset. -/// \note The generated dataset has three columns ["image", "coarse_label", "fine_label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar100Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/cifar100_dataset_directory"; -/// std::shared_ptr ds = Cifar100(folder_path, "all", std::make_shared()); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In CIFAR100 dataset, each dictionary has 3 keys: "image", "fine_label" and "coarse_label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Cifar100(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a Cifar100 Dataset. -/// \note The generated dataset has three columns ["image", "coarse_label", "fine_label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar100Dataset. -inline std::shared_ptr DATASET_API Cifar100(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a Cifar100 Dataset. -/// \note The generated dataset has three columns ["image", "coarse_label", "fine_label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CIFAR100, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the Cifar100Dataset. -inline std::shared_ptr DATASET_API Cifar100(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class CityscapesDataset -/// \brief A source dataset for reading and parsing Cityscapes dataset. -class DATASET_API CityscapesDataset : public Dataset { - public: - /// \brief Constructor of CityscapesDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if - /// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". - /// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include - /// "fine" or "coarse". - /// \param[in] task The type of task which is used to select output data. Acceptable tasks include - /// "instance", "semantic", "polygon" or "color". - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of CityscapesDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if - /// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". - /// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include - /// "fine" or "coarse". - /// \param[in] task The type of task which is used to select output data. Acceptable tasks include - /// "instance", "semantic", "polygon" or "color". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of CityscapesDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if - /// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". - /// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include - /// "fine" or "coarse". - /// \param[in] task The type of task which is used to select output data. Acceptable tasks include - /// "instance", "semantic", "polygon" or "color". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - CityscapesDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector &quality_mode, const std::vector &task, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of CityscapesDataset. - ~CityscapesDataset() override = default; -}; - -/// \brief Function to create a CityscapesDataset. -/// \note The generated dataset has two columns ["image", "task"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if -/// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". -/// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include -/// "fine" or "coarse". -/// \param[in] task The type of task which is used to select output data. Acceptable tasks include -/// "instance", "semantic", "polygon" or "color". -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current CityscapesDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/cityscapes_dataset_directory"; -/// std::shared_ptr ds = Cityscapes(dataset_path, "train", "fine", "color"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Cityscapes dataset, each data dictionary owns keys "image" and "task" */ -/// auto task = row["task"]; -/// \endcode -inline std::shared_ptr DATASET_API Cityscapes( - const std::string &dataset_dir, const std::string &usage, const std::string &quality_mode, const std::string &task, - bool decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(quality_mode), - StringToChar(task), decode, sampler, cache); -} - -/// \brief Function to create a CityscapesDataset. -/// \note The generated dataset has two columns ["image", "task"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if -/// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". -/// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include -/// "fine" or "coarse". -/// \param[in] task The type of task which is used to select output data. Acceptable tasks include -/// "instance", "semantic", "polygon" or "color". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current CityscapesDataset. -inline std::shared_ptr DATASET_API Cityscapes( - const std::string &dataset_dir, const std::string &usage, const std::string &quality_mode, const std::string &task, - bool decode, const Sampler *sampler, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(quality_mode), - StringToChar(task), decode, sampler, cache); -} - -/// \brief Function to create a CityscapesDataset. -/// \note The generated dataset has two columns ["image", "task"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all" if -/// quality_mode is "fine" otherwise "train", "train_extra", "val" or "all". -/// \param[in] quality_mode The quality mode of processed image. Acceptable quality_modes include -/// "fine" or "coarse". -/// \param[in] task The type of task which is used to select output data. Acceptable tasks include -/// "instance", "semantic", "polygon" or "color". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current CityscapesDataset. -inline std::shared_ptr DATASET_API Cityscapes( - const std::string &dataset_dir, const std::string &usage, const std::string &quality_mode, const std::string &task, - bool decode, const std::reference_wrapper &sampler, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(quality_mode), - StringToChar(task), decode, sampler, cache); -} - -/// \class CLUEDataset -/// \brief A source dataset for reading and parsing CLUE dataset. -class DATASET_API CLUEDataset : public Dataset { - public: - /// \brief Constructor of CLUEDataset. - /// \param[in] dataset_files List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] task The kind of task, one of "AFQMC", "TNEWS", "IFLYTEK", "CMNLI", "WSC" and "CSL" (default="AFQMC"). - /// \param[in] usage Part of dataset of CLUE, can be "train", "test" or "eval" data (default="train"). - /// \param[in] num_samples The number of samples to be included in the dataset - /// (Default = 0 means all samples). - /// \param[in] shuffle The mode for shuffling data every epoch. (Default=ShuffleMode.kGlobal) - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1) - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified (Default = 0). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - CLUEDataset(const std::vector> &dataset_files, const std::vector &task, - const std::vector &usage, int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of CLUEDataset. - ~CLUEDataset() override = default; -}; - -/// \brief Function to create a CLUEDataset. -/// \note The generated dataset has a variable number of columns depending on the task and usage. -/// \param[in] dataset_files List of files to be read to search for a pattern of files. The list -/// will be sorted in a lexicographical order. -/// \param[in] task The kind of task, one of "AFQMC", "TNEWS", "IFLYTEK", "CMNLI", "WSC" and "CSL" (default="AFQMC"). -/// \param[in] usage Part of dataset of CLUE, can be "train", "test" or "eval" data (default="train"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0 means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch. (Default=ShuffleMode.kGlobal) -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1) -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the CLUEDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string train_file = "/path/to/clue_dataset_file"; -/// std::shared_ptr ds = CLUE({train_file}, "AFQMC", "train", 0, ShuffleMode::kFalse); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// auto text = row["sentence1"]; -/// \endcode -inline std::shared_ptr DATASET_API CLUE(const std::vector &dataset_files, - const std::string &task = "AFQMC", - const std::string &usage = "train", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, - int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), StringToChar(task), StringToChar(usage), - num_samples, shuffle, num_shards, shard_id, cache); -} - -/// \class CMUArcticDataset -/// \brief A source dataset for reading and parsing CMUArctic dataset. -class DATASET_API CMUArcticDataset : public Dataset { - public: - /// \brief Constructor of CMUArcticDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", - /// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of CMUArcticDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", - /// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of CMUArcticDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", - /// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - CMUArcticDataset(const std::vector &dataset_dir, const std::vector &name, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of CMUArcticDataset. - ~CMUArcticDataset() override = default; -}; - -/// \brief Function to create a CMUArcticDataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcript", "utterance_id"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", -/// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt" (default = "aew"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the CMUArcticDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/cmu_arctic_dataset_directory"; -/// std::shared_ptr ds = -/// CMUArcticDataset(folder_path, name = "aew", std::make_shared(false, 10)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In CMUArctic dataset, each data dictionary has keys "waveform", "sample_rate", "transcript" -/// and "utterance_id" */ -/// auto waveform = row["waveform"]; -/// \endcode -inline std::shared_ptr DATASET_API -CMUArctic(const std::string &dataset_dir, const std::string &name = "aew", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), sampler, cache); -} - -/// \brief Function to create a CMUArcticDataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcript", "utterance_id"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", -/// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the CMUArcticDataset. -inline std::shared_ptr DATASET_API CMUArctic(const std::string &dataset_dir, const std::string &name, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), sampler, cache); -} - -/// \brief Function to create a CMUArcticDataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcript", "utterance_id"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Part of dataset of CMUArctic, can be "aew", "ahw", "aup", "awb", "axb", "bdl", "clb", "eey", -/// "fem", "gka", "jmk", "ksp", "ljm", "lnh", "rms", "rxr", "slp" or "slt". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the CMUArcticDataset. -inline std::shared_ptr DATASET_API CMUArctic(const std::string &dataset_dir, const std::string &name, - const std::reference_wrapper sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), sampler, cache); -} - -/// \class CocoDataset -/// \brief A source dataset for reading and parsing Coco dataset. -class DATASET_API CocoDataset : public Dataset { - public: - /// \brief Constructor of CocoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] annotation_file Path to the annotation json. - /// \param[in] task Set the task type of reading coco data, now support 'Detection'/'Stuff'/'Panoptic'/'Keypoint'. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). - CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache, const bool &extra_metadata); - - /// \brief Constructor of CocoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] annotation_file Path to the annotation json. - /// \param[in] task Set the task type of reading coco data, now support 'Detection'/'Stuff'/'Panoptic'/'Keypoint'. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). - CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, const Sampler *sampler, - const std::shared_ptr &cache, const bool &extra_metadata); - - /// \brief Constructor of CocoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] annotation_file Path to the annotation json. - /// \param[in] task Set the task type of reading coco data, now support 'Detection'/'Stuff'/'Panoptic'/'Keypoint'. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). - CocoDataset(const std::vector &dataset_dir, const std::vector &annotation_file, - const std::vector &task, const bool &decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache, const bool &extra_metadata); - - /// \brief Constructor of CocoDataset. - ~CocoDataset() override = default; -}; - -/// \brief Function to create a CocoDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32]]. -/// - task='Stuff', column: [['image', dtype=uint8], ['segmentation',dtype=float32], ['iscrowd', dtype=uint32]]. -/// - task='Keypoint', column: [['image', dtype=uint8], ['keypoints', dtype=float32], -/// ['num_keypoints', dtype=uint32]]. -/// - task='Panoptic', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32], ['area', dtype=uitn32]]. -/// - task='Captioning', column: [['image', dtype=uint8], ['captions', dtype=string]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] annotation_file Path to the annotation json. -/// \param[in] task Set the task type of reading coco data. Supported task types are "Detection", "Stuff", "Panoptic", -/// "Keypoint" and "Captioning". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). -/// \return Shared pointer to the CocoDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/coco_dataset_directory"; -/// std::string annotation_file = "/path/to/annotation_file"; -/// std::shared_ptr ds = Coco(folder_path, annotation_file); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In COCO dataset, each dictionary has keys "image" and "annotation" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Coco(const std::string &dataset_dir, const std::string &annotation_file, const std::string &task = "Detection", - const bool &decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr, const bool &extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), StringToChar(task), - decode, sampler, cache, extra_metadata); -} - -/// \brief Function to create a CocoDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32]]. -/// - task='Stuff', column: [['image', dtype=uint8], ['segmentation',dtype=float32], ['iscrowd', dtype=uint32]]. -/// - task='Keypoint', column: [['image', dtype=uint8], ['keypoints', dtype=float32], -/// ['num_keypoints', dtype=uint32]]. -/// - task='Panoptic', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32], ['area', dtype=uitn32]]. -/// - task='Captioning', column: [['image', dtype=uint8], ['captions', dtype=string]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] annotation_file Path to the annotation json. -/// \param[in] task Set the task type of reading coco data. Supported task types are "Detection", "Stuff", "Panoptic", -/// "Keypoint" and "Captioning". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). -/// \return Shared pointer to the CocoDataset. -inline std::shared_ptr DATASET_API Coco(const std::string &dataset_dir, const std::string &annotation_file, - const std::string &task, const bool &decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr, - const bool &extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), StringToChar(task), - decode, sampler, cache, extra_metadata); -} - -/// \brief Function to create a CocoDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32]]. -/// - task='Stuff', column: [['image', dtype=uint8], ['segmentation',dtype=float32], ['iscrowd', dtype=uint32]]. -/// - task='Keypoint', column: [['image', dtype=uint8], ['keypoints', dtype=float32], -/// ['num_keypoints', dtype=uint32]]. -/// - task='Panoptic', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['category_id', dtype=uint32], -/// ['iscrowd', dtype=uint32], ['area', dtype=uitn32]]. -/// - task='Captioning', column: [['image', dtype=uint8], ['captions', dtype=string]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] annotation_file Path to the annotation json. -/// \param[in] task Set the task type of reading coco data. Supported task types are "Detection", "Stuff", "Panoptic", -/// "Keypoint" and "Captioning". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row. (default=false). -/// \return Shared pointer to the CocoDataset. -inline std::shared_ptr DATASET_API Coco(const std::string &dataset_dir, const std::string &annotation_file, - const std::string &task, const bool &decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr, - const bool &extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), StringToChar(task), - decode, sampler, cache, extra_metadata); -} - -/// \class CoNLL2000Dataset -/// \brief A source dataset for reading and parsing CoNLL2000Dataset. -class DATASET_API CoNLL2000Dataset : public Dataset { - public: - /// \brief Constructor of CoNLL2000Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data list txt file to be read, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - CoNLL2000Dataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of CoNLL2000Dataset. - ~CoNLL2000Dataset() override = default; -}; - -/// \brief Function to create a CoNLL2000Dataset. -/// \note The generated dataset has three column ['word', 'pos_tag', 'chunk_tag']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of CoNLL2000, can be "train", "test" or "all" (default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the CoNLL2000Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_dir = "/path/to/conll2000_dataset_directory"; -/// std::shared_ptr ds = CoNLL2000(dataset_dir, "all", 0, ShuffleMode::kGlobal); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In CoNLL2000 dataset, each dictionary has keys "word", "pos_tag", "chunk_tag" */ -/// auto word = row["word"]; -/// \endcode -inline std::shared_ptr DATASET_API CoNLL2000(const std::string &dataset_dir, - const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class CSVDataset -/// \brief A source dataset that reads and parses comma-separated values (CSV) datasets. -class DATASET_API CSVDataset : public Dataset { - public: - /// \brief Constructor of CSVDataset. - /// \param[in] dataset_files List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] field_delim A char that indicates the delimiter to separate fields (default=','). - /// \param[in] column_defaults List of default values for the CSV field (default={}). Each item in the list is - /// either a valid type (float, int, or string). If this is not provided, treats all columns as string type. - /// \param[in] column_names List of column names of the dataset (default={}). If this is not provided, infers the - /// column_names from the first row of CSV file. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// (Default = 0 means all samples). - /// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1) - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified (Default = 0). - /// \param[in] cache Tensor cache to use.(default=nullptr which means no cache is used). - CSVDataset(const std::vector> &dataset_files, char field_delim, - const std::vector> &column_defaults, - const std::vector> &column_names, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of CSVDataset. - ~CSVDataset() override = default; -}; - -/// \brief Function to create a CSVDataset. -/// \note The generated dataset has a variable number of columns. -/// \param[in] dataset_files List of files to be read to search for a pattern of files. The list -/// will be sorted in a lexicographical order. -/// \param[in] field_delim A char that indicates the delimiter to separate fields (default=','). -/// \param[in] column_defaults List of default values for the CSV field (default={}). Each item in the list is -/// either a valid type (float, int, or string). If this is not provided, treats all columns as string type. -/// \param[in] column_names List of column names of the dataset (default={}). If this is not provided, infers the -/// column_names from the first row of CSV file. -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0 means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1) -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use.(default=nullptr which means no cache is used). -/// \return Shared pointer to the CSVDataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string train_file = "/path/to/csv_file"; -/// std::vector column_names = {"col1", "col2", "col3", "col4"}; -/// std::shared_ptr ds = CSV({train_file}, ',', {}, column_names, 0, ShuffleMode::kFalse); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, the dataset has column "col1", "col2", "col3" and "col4" */ -/// auto col1 = row["col1"]; -/// \endcode -inline std::shared_ptr DATASET_API CSV(const std::vector &dataset_files, - char field_delim = ',', - const std::vector> &column_defaults = {}, - const std::vector &column_names = {}, - int64_t num_samples = 0, ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), field_delim, column_defaults, - VectorStringToChar(column_names), num_samples, shuffle, num_shards, shard_id, - cache); -} - -/// \class DBpediaDataset -/// \brief A source dataset for reading and parsing DBpedia dataset. -class DATASET_API DBpediaDataset : public Dataset { - public: - /// \brief Constructor of DBpediaDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of DBpedia, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - DBpediaDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of DBpediaDataset. - ~DBpediaDataset() override = default; -}; - -/// \brief Function to create a DBpediaDataset. -/// \note The generated dataset has three columns ["class", "title", "content"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of DBpedia, can be "train", "test" or "all" (default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the DBpediaDataset -inline std::shared_ptr DATASET_API DBpedia(const std::string &dataset_dir, - const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class DIV2KDataset -/// \brief A source dataset for reading and parsing DIV2K dataset. -class DATASET_API DIV2KDataset : public Dataset { - public: - /// \brief Constructor of DIV2KDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". - /// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", - /// "difficult" or "wild". - /// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, const std::vector &downgrade, - int32_t scale, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of DIV2KDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". - /// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", - /// "difficult" or "wild". - /// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, const std::vector &downgrade, - int32_t scale, bool decode, const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of DIV2KDataset. - /// \param[in] dataset_dir The dataset dir to be read. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". - /// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", - /// "difficult" or "wild". - /// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - DIV2KDataset(const std::vector &dataset_dir, const std::vector &usage, const std::vector &downgrade, - int32_t scale, bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of DIV2KDataset. - ~DIV2KDataset() override = default; -}; - -/// \brief Function to create a DIV2KDataset. -/// \note The generated dataset has two columns ["hr_image", "lr_image"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". -/// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", "difficult" -/// or "wild". -/// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current DIV2KDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/div2k_dataset_directory"; -/// std::shared_ptr ds = DIV2K(dataset_path, "train", "bicubic", 2); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In DIV2K dataset, each dictionary has keys "hr_image" and "lr_image" */ -/// auto hr_image = row["hr_image"]; -/// \endcode -inline std::shared_ptr DATASET_API -DIV2K(const std::string &dataset_dir, const std::string &usage, const std::string &downgrade, int32_t scale, - bool decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(downgrade), scale, - decode, sampler, cache); -} - -/// \brief Function to create a DIV2KDataset. -/// \note The generated dataset has two columns ["hr_image", "lr_image"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". -/// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", "difficult" -/// or "wild". -/// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current DIV2KDataset. -inline std::shared_ptr DATASET_API DIV2K(const std::string &dataset_dir, const std::string &usage, - const std::string &downgrade, int32_t scale, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(downgrade), scale, - decode, sampler, cache); -} - -/// \brief Function to create a DIV2KDataset. -/// \note The generated dataset has two columns ["hr_image", "lr_image"]. -/// \param[in] dataset_dir The dataset dir to be read. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "valid" or "all". -/// \param[in] downgrade The mode of downgrade. Acceptable downgrades include "bicubic", "unknown", "mild", "difficult" -/// or "wild". -/// \param[in] scale The scale of downgrade. Acceptable scales include 2, 3, 4 or 8. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current DIV2KDataset. -inline std::shared_ptr DATASET_API DIV2K(const std::string &dataset_dir, const std::string &usage, - const std::string &downgrade, int32_t scale, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), StringToChar(downgrade), scale, - decode, sampler, cache); -} - -/// \class EMnistDataset -/// \brief A source dataset for reading and parsing EMnist dataset. -class DATASET_API EMnistDataset : public Dataset { - public: - /// \brief Constructor of EMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" - /// or "mnist". - /// \param[in] usage Part of dataset of EMNIST, can be "train", "test" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - EMnistDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of EMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" - /// or "mnist". - /// \param[in] usage Part of dataset of EMNIST, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - EMnistDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of EMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" - /// or "mnist". - /// \param[in] usage Part of dataset of EMNIST, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - EMnistDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of EMnistDataset. - ~EMnistDataset() override = default; -}; - -/// \brief Function to create a EMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" or "mnist". -/// \param[in] usage Usage of EMNIST, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not. -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current EMnistDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/emnist_dataset_directory"; -/// std::shared_ptr ds = EMnist(folder_path, "mnist", "train", std::make_shared(false, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In EMNIST dataset dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -EMnist(const std::string &dataset_dir, const std::string &name, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \brief Function to create a EMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" or "mnist". -/// \param[in] usage Usage of EMNIST, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current EMnistDataset. -inline std::shared_ptr DATASET_API EMnist(const std::string &dataset_dir, const std::string &usage, - const std::string &name, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \brief Function to create a EMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Name of splits for EMNIST, can be "byclass", "bymerge", "balanced", "letters", "digits" or "mnist". -/// \param[in] usage Usage of EMNIST, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current EMnistDataset. -inline std::shared_ptr DATASET_API EMnist(const std::string &dataset_dir, const std::string &name, - const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \class EnWik9Dataset -/// \brief A source dataset for reading and parsing EnWik9 dataset. -class DATASET_API EnWik9Dataset : public Dataset { - public: - /// \brief Function to create a EnWik9Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - EnWik9Dataset(const std::vector &dataset_dir, int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of EnWik9Dataset. - ~EnWik9Dataset() override = default; -}; - -/// \brief Function to create a EnWik9Dataset. -/// \note The generated dataset has one column ['text']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode.kFalse - No shuffling is performed. -/// ShuffleMode.kFiles - Shuffle files only. -/// ShuffleMode.kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the EnWik9Dataset. -inline std::shared_ptr DATASET_API EnWik9(const std::string &dataset_dir, int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), num_samples, shuffle, num_shards, shard_id, cache); -} - -/// \class FakeImageDataset -/// \brief A source dataset for generating fake images. -class DATASET_API FakeImageDataset : public Dataset { - public: - /// \brief Constructor of FakeImageDataset. - /// \param[in] num_images The number of images to generate, which must be positive. - /// \param[in] image_size Size of the images, which must be a vector of three positive values. - /// \param[in] num_classes The number of classes of the images, which must be positive. - /// \param[in] base_seed The base seed to generate the images. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, int32_t base_seed, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of FakeImageDataset. - /// \param[in] num_images The number of images to generate, which must be positive. - /// \param[in] image_size Size of the images, which must be a vector of three positive values. - /// \param[in] num_classes The number of classes of the images, which must be positive. - /// \param[in] base_seed The base seed to generate the images. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, int32_t base_seed, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of FakeImageDataset. - /// \param[in] num_images The number of images to generate, which must be positive. - /// \param[in] image_size Size of the images, which must be a vector of three positive values. - /// \param[in] num_classes The number of classes of the images, which must be positive. - /// \param[in] base_seed The base seed to generate the images. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - FakeImageDataset(int32_t num_images, const std::vector &image_size, int32_t num_classes, int32_t base_seed, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of FakeImageDataset. - ~FakeImageDataset() override = default; -}; - -/// \brief Function to create a FakeImageDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] num_images The number of images to generate, which must be positive (default = 1000). -/// \param[in] image_size Size of the images, which must be a vector of three positive values -/// (default = {224, 224, 3}). -/// \param[in] num_classes The number of classes of the images, which must be positive (default = 10). -/// \param[in] base_seed The base seed to generate the images (default = 0). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FakeDataset. -inline std::shared_ptr DATASET_API -FakeImage(int32_t num_images = 1000, const std::vector &image_size = {224, 224, 3}, int32_t num_classes = 10, - int32_t base_seed = 0, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(num_images, image_size, num_classes, base_seed, sampler, cache); -} - -/// \brief Function to create a FakeImageDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] num_images The number of images to generate, which must be positive. -/// \param[in] image_size Size of the images, which must be a vector of three positive values. -/// \param[in] num_classes The number of classes of the images, which must be positive. -/// \param[in] base_seed The base seed to generate the images. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FakeImageDataset. -inline std::shared_ptr DATASET_API FakeImage(int32_t num_images, - const std::vector &image_size, - int32_t num_classes, int32_t base_seed, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(num_images, image_size, num_classes, base_seed, sampler, cache); -} - -/// \brief Function to create a FakeImageDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] num_images The number of images to generate, which must be positive. -/// \param[in] image_size Size of the images, which must be a vector of three positive values. -/// \param[in] num_classes The number of classes of the images, which must be positive. -/// \param[in] base_seed The base seed to generate the images. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FakeImageDataset. -inline std::shared_ptr DATASET_API FakeImage(int32_t num_images, - const std::vector &image_size, - int32_t num_classes, int32_t base_seed, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(num_images, image_size, num_classes, base_seed, sampler, cache); -} - -/// \class FashionMnistDataset -/// \brief A source dataset that reads and parses FASHION-MNIST dataset. -class DATASET_API FashionMnistDataset : public Dataset { - public: - /// \brief Constructor of FashionMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of FashionMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of FashionMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - FashionMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of FashionMnistDataset. - ~FashionMnistDataset() override = default; -}; - -/// \brief Function to create a FashionMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the FashionMnistDataset. -inline std::shared_ptr DATASET_API -FashionMnist(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a FashionMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the FashionMnistDataset. -inline std::shared_ptr DATASET_API -FashionMnist(const std::string &dataset_dir, const std::string &usage, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a FashionMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of FASHION-MNIST, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the FashionMnistDataset. -inline std::shared_ptr DATASET_API -FashionMnist(const std::string &dataset_dir, const std::string &usage, const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class FlickrDataset -/// \brief A source dataset for reading and parsing Flickr dataset. -class DATASET_API FlickrDataset : public Dataset { - public: - /// \brief Constructor of FlickrDataset. - /// \param[in] dataset_dir The dataset dir to be read - /// \param[in] annotation_file The annotation file to be read - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of FlickrDataset. - /// \param[in] dataset_dir The dataset dir to be read - /// \param[in] annotation_file The annotation file to be read - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of FlickrDataset. - /// \param[in] dataset_dir The dataset dir to be read - /// \param[in] annotation_file The annotation file to be read - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - FlickrDataset(const std::vector &dataset_dir, const std::vector &annotation_file, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of FlickrDataset. - ~FlickrDataset() override = default; -}; - -/// \brief Function to create a FlickrDataset -/// \note The generated dataset has two columns ["image", "annotation"] -/// \param[in] dataset_dir The dataset dir to be read -/// \param[in] annotation_file The annotation file to be read -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FlickrDataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/flickr30k_dataset_directory"; -/// std::string file_path = "/path/to/token_file"; -/// std::shared_ptr ds = Flickr(dataset_path, file_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In FLICKR dataset, each dictionary has keys "image" and "annotation" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Flickr(const std::string &dataset_dir, const std::string &annotation_file, bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), decode, sampler, - cache); -} - -/// \brief Function to create a FlickrDataset -/// \note The generated dataset has two columns ["image", "annotation"] -/// \param[in] dataset_dir The dataset dir to be read -/// \param[in] annotation_file The annotation file to be read -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FlickrDataset -inline std::shared_ptr DATASET_API Flickr(const std::string &dataset_dir, - const std::string &annotation_file, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), decode, sampler, - cache); -} - -/// \brief Function to create a FlickrDataset -/// \note The generated dataset has two columns ["image", "annotation"] -/// \param[in] dataset_dir The dataset dir to be read -/// \param[in] annotation_file The annotation file to be read -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current FlickrDataset -inline std::shared_ptr DATASET_API Flickr(const std::string &dataset_dir, - const std::string &annotation_file, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(annotation_file), decode, sampler, - cache); -} - -/// \class Food101Dataset -/// \brief A source dataset for reading and parsing Food101 dataset. -class DATASET_API Food101Dataset : public Dataset { - public: - /// \brief Constructor of Food101Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Food101Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Food101Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Food101Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of Food101Dataset. - ~Food101Dataset() override = default; -}; - -/// \brief Function to create a Food101Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". Default: "all". -/// \param[in] decode Decode the images after reading. Default: false. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset. Default: RandomSampler(). -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the Food101Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/Food101_dataset_directory"; -/// std::shared_ptr ds = Food101(dataset_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Food101 dataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Food101(const std::string &dataset_dir, const std::string &usage = "all", bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a Food101Dataset -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all" . -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the Food101Dataset. -inline std::shared_ptr DATASET_API Food101(const std::string &dataset_dir, const std::string &usage, - bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a Food101Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the Food101Dataset. -inline std::shared_ptr DATASET_API Food101(const std::string &dataset_dir, const std::string &usage, - bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \class GTZANDataset -/// \brief A source dataset for reading and parsing GTZAN dataset. -class DATASET_API GTZANDataset : public Dataset { - public: - /// \brief Constructor of GTZANDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of GTZANDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of GTZANDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - GTZANDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of GTZANDataset. - ~GTZANDataset() override = default; -}; - -/// \brief Function to create a GTZANDataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the GTZANDataset. -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/gtzan_dataset_directory"; -/// std::shared_ptr ds = -/// GTZANDataset(folder_path, usage = "all", std::make_shared(false, 10)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In GTZAN dataset, each data dictionary has keys "waveform", "sample_rate" and "label" */ -/// auto waveform = row["waveform"]; -/// \endcode -inline std::shared_ptr DATASET_API -GTZAN(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a GTZANDataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the GTZANDataset. -inline std::shared_ptr DATASET_API GTZAN(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a GTZANDataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of GTZAN, can be "train", "valid", "test", or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the GTZANDataset. -inline std::shared_ptr DATASET_API GTZAN(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class ImageFolderDataset -/// \brief A source dataset that reads images from a tree of directories. -class DATASET_API ImageFolderDataset : public Dataset { - public: - /// \brief Constructor of ImageFolderDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode A flag to decode in ImageFolder. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] extensions File extensions to be read. - /// \param[in] class_indexing a class name to label map. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ImageFolderDataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache); - - /// \brief Constructor of ImageFolderDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode A flag to decode in ImageFolder. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] extensions File extensions to be read. - /// \param[in] class_indexing a class name to label map. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ImageFolderDataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache); - - /// \brief Constructor of ImageFolderDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode A flag to decode in ImageFolder. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] extensions File extensions to be read. - /// \param[in] class_indexing a class name to label map. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ImageFolderDataset(const std::vector &dataset_dir, bool decode, const std::reference_wrapper &sampler, - const std::set> &extensions, - const std::map, int32_t> &class_indexing, - const std::shared_ptr &cache); - - /// \brief Destructor of ImageFolderDataset. - ~ImageFolderDataset() override = default; -}; - -/// \brief Function to create an ImageFolderDataset. -/// \note A source dataset that reads images from a tree of directories. -/// All images within one folder have the same label. -/// The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode A flag to decode in ImageFolder. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] extensions File extensions to be read. -/// \param[in] class_indexing a class name to label map. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ImageFolderDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/image_directory"; -/// std::shared_ptr ds = ImageFolder(dataset_path, true, std::make_shared(false, 10)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In ImageFolder dataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -ImageFolder(const std::string &dataset_dir, bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::set &extensions = {}, const std::map &class_indexing = {}, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, SetStringToChar(extensions), - MapStringToChar(class_indexing), cache); -} - -/// \brief Function to create an ImageFolderDataset -/// \note A source dataset that reads images from a tree of directories. -/// All images within one folder have the same label. -/// The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode A flag to decode in ImageFolder. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] extensions File extensions to be read. -/// \param[in] class_indexing a class name to label map. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ImageFolderDataset. -inline std::shared_ptr DATASET_API ImageFolder( - const std::string &dataset_dir, bool decode, const Sampler *sampler, const std::set &extensions = {}, - const std::map &class_indexing = {}, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, SetStringToChar(extensions), - MapStringToChar(class_indexing), cache); -} - -/// \brief Function to create an ImageFolderDataset. -/// \note A source dataset that reads images from a tree of directories. -/// All images within one folder have the same label. -/// The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode A flag to decode in ImageFolder. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] extensions File extensions to be read. -/// \param[in] class_indexing a class name to label map. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ImageFolderDataset. -inline std::shared_ptr DATASET_API -ImageFolder(const std::string &dataset_dir, bool decode, const std::reference_wrapper &sampler, - const std::set &extensions = {}, const std::map &class_indexing = {}, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, SetStringToChar(extensions), - MapStringToChar(class_indexing), cache); -} - -/// \class IMDBDataset -/// \brief A source dataset for reading and parsing IMDB dataset. -class DATASET_API IMDBDataset : public Dataset { - public: - /// \brief Constructor of IMDBDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of IMDBDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of IMDBDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - IMDBDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of IMDBDataset. - ~IMDBDataset() override = default; -}; - -/// \brief A source dataset for reading and parsing IMDB dataset. -/// \note The generated dataset has two columns ["text", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all" -/// (Default="all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the IMDBDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/imdb_dataset_directory"; -/// std::shared_ptr ds = IMDB(dataset_path, "all"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In IMDB dataset, each data dictionary has keys "text" and "label" */ -/// auto text = row["text"]; -/// \endcode -inline std::shared_ptr DATASET_API -IMDB(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief A source dataset for reading and parsing IMDB dataset. -/// \note The generated dataset has two columns ["text", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the IMDBDataset. -inline std::shared_ptr DATASET_API IMDB(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief A source dataset for reading and parsing IMDB dataset. -/// \note The generated dataset has two columns ["text", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the IMDBDataset. -inline std::shared_ptr DATASET_API IMDB(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class IWSLT2016Dataset. -/// \brief A source dataset for reading and parsing IWSLT2016 dataset. -class DATASET_API IWSLT2016Dataset : public Dataset { - public: - /// \brief Constructor of IWSLT2016Dataset. - /// \note The generated dataset has two columns ["text", "translation"]. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of IWSLT2016, can be "train", "valid", "test" or "all". - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] valid_set A string to identify validation set. - /// \param[in] test_set A string to identify test set. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - IWSLT2016Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, const std::vector &valid_set, - const std::vector &test_set, int64_t num_samples, ShuffleMode shuffle, int32_t num_shards, - int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of IWSLT2016Dataset. - ~IWSLT2016Dataset() override = default; -}; - -/// \brief Function to create a IWSLT2016Dataset. -/// \note The generated dataset has two columns ["text", "translation"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of IWSLT2016, can be "train", "valid", "test" or "all" (default = "all"). -/// \param[in] language_pair List containing src and tgt language (Default = {"de", "en"}). -/// \param[in] valid_set A string to identify validation set (Default = "tst2013"). -/// \param[in] test_set A string to identify test set (Default = "tst2014"). -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the IWSLT2016Dataset. -inline std::shared_ptr DATASET_API -IWSLT2016(const std::string &dataset_dir, const std::string &usage = "all", - const std::vector &language_pair = {"de", "en"}, const std::string &valid_set = "tst2013", - const std::string &test_set = "tst2014", int64_t num_samples = 0, ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), - VectorStringToChar(language_pair), StringToChar(valid_set), - StringToChar(test_set), num_samples, shuffle, num_shards, shard_id, cache); -} - -/// \class IWSLT2017Dataset. -/// \brief A source dataset for reading and parsing IWSLT2017 dataset. -class DATASET_API IWSLT2017Dataset : public Dataset { - public: - /// \brief Constructor of IWSLT2017Dataset. - /// \note The generated dataset has two columns ["text", "translation"]. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of IWSLT2017, can be "train", "valid", "test" or "all". - /// \param[in] language_pair List containing src and tgt language. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - /// \return Shared pointer to the IWSLT2017Dataset. - IWSLT2017Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of IWSLT2017Dataset. - ~IWSLT2017Dataset() override = default; -}; - -/// \brief Function to create a IWSLT2017Dataset. -/// \note The generated dataset has two columns ["text", "translation"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of IWSLT2017, can be "train", "valid", "test" or "all" (default = "all"). -/// \param[in] language_pair List containing src and tgt language (Default = {"de", "en"}). -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the IWSLT2017Dataset. -inline std::shared_ptr DATASET_API -IWSLT2017(const std::string &dataset_dir, const std::string &usage = "all", - const std::vector &language_pair = {"de", "en"}, int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), - VectorStringToChar(language_pair), num_samples, shuffle, num_shards, - shard_id, cache); -} - -/// \class KITTIDataset -/// \brief A source dataset that reads KITTI images and labels. -class DATASET_API KITTIDataset : public Dataset { - public: - /// \brief Constructor of KITTIDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data file to read. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of KITTIDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data file to read. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of KITTIDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data file to read. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - KITTIDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of KITTIDataset. - ~KITTIDataset() override = default; -}; - -/// \brief Function to create a KITTIDataset. -/// \note When usage is 'train', the generated dataset has multi-columns, 'image', 'label', 'truncated', -/// 'occluded', 'alpha', 'bbox', 'dimensions', 'location', 'rotation_y'; When usage is 'test', -/// the generated dataset has one column 'image'. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data file to read (default = "train"). -/// \param[in] decode Decode the images after reading (default = false). -/// \param[in] sampler Sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/kitti_dataset_directory"; -/// std::shared_ptr ds = KITTI(folder_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In KITTI dataset, each dictionary has key "image" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -KITTI(const std::string &dataset_dir, const std::string &usage = "train", bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a KITTIDataset. -/// \note When usage is 'train', the generated dataset has multi-columns, 'image', 'label', 'truncated', -/// 'occluded', 'alpha', 'bbox', 'dimensions', 'location', 'rotation_y'; When usage is 'test', -/// the generated dataset has one column 'image'. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data file to read. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API KITTI(const std::string &dataset_dir, const std::string &usage, - bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a KITTIDataset. -/// \note When usage is 'train', the generated dataset has multi-columns, 'image', 'label', 'truncated', -/// 'occluded', 'alpha', 'bbox', 'dimensions', 'location', 'rotation_y'; When usage is 'test', -/// the generated dataset has one column 'image'. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data file to read. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API KITTI(const std::string &dataset_dir, const std::string &usage, - bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \class KMnistDataset. -/// \brief A source dataset for reading and parsing KMnist dataset. -class DATASET_API KMnistDataset : public Dataset { - public: - /// \brief Function to create a KMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage of KMNIST, can be "train", "test" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - /// \return Shared pointer to the current KMnistDataset. - KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Function to create a KMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage of Kmnist, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - /// \return Shared pointer to the current KMnistDataset. - KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Function to create a KMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage of Kmnist, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - /// \return Shared pointer to the current KMnistDataset. - KMnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of KMnistDataset. - ~KMnistDataset() override = default; -}; - -/// \brief Function to create a KMnistDataset. -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] usage of KMNIST, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current KMnistDataset. -inline std::shared_ptr DATASET_API -KMnist(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a KMnistDataset. -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage of Kmnist, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current KMnistDataset. -inline std::shared_ptr DATASET_API KMnist(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a KMnistDataset. -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage of Kmnist, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current KMnistDataset. -inline std::shared_ptr DATASET_API KMnist(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class LFWDataset -/// \brief A source dataset for reading and parsing LFW dataset. -class DATASET_API LFWDataset : public Dataset { - public: - /// \brief Constructor of LFWDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading LFW, support "people" and "pairs". - /// \param[in] usage The image split to use, support "10fold", "train", "test" and "all". - /// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LFWDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::vector &image_set, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LFWDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading LFW, support "people" and "pairs". - /// \param[in] usage The image split to use, support "10fold", "train", "test" and "all". - /// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LFWDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::vector &image_set, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LFWDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading LFW, support "people" and "pairs". - /// \param[in] usage The image split to use, support "10fold", "train", "test" and "all". - /// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LFWDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::vector &image_set, bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of LFWDataset. - ~LFWDataset() override = default; -}; - -/// \brief Function to create a LFWDataset. -/// \note When usage is 'people', the generated dataset has two columns ["image", "label"]; -/// When task is 'pairs', the generated dataset has three columns ["image1", "image2", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading LFW, support "people" and "pairs" (default = "people"). -/// \param[in] usage The image split to use, support "10fold", "train", "test" and "all" (default = "all", -/// will read samples including train and test). -/// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled" -/// (default = "funneled"). -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LFWDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/lfw_dataset_directory"; -/// std::shared_ptr ds = LFW(folder_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In LFW dataset, each data dictionary owns keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -LFW(const std::string &dataset_dir, const std::string &task = "people", const std::string &usage = "all", - const std::string &image_set = "funneled", bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - StringToChar(image_set), decode, sampler, cache); -} - -/// \brief Function to create a LFWDataset. -/// \note When usage is 'people', the generated dataset has two columns ["image", "label"]; -/// When task is 'pairs', the generated dataset has three columns ["image1", "image2", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading LFW, support "people" and "pairs". -/// \param[in] usage The image split to use, support "10fold", "train", "test" and "all". -/// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LFWDataset. -inline std::shared_ptr DATASET_API LFW(const std::string &dataset_dir, const std::string &task, - const std::string &usage, const std::string &image_set, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - StringToChar(image_set), decode, sampler, cache); -} - -/// \brief Function to create a LFWDataset. -/// \note When usage is 'people', the generated dataset has two columns ["image", "label"]; -/// When task is 'pairs', the generated dataset has three columns ["image1", "image2", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading LFW, support "people" and "pairs". -/// \param[in] usage The image split to use, support "10fold", "train", "test" and "all". -/// \param[in] image_set Image set of image funneling to use, support "original", "funneled" and "deepfunneled". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LFWDataset. -inline std::shared_ptr DATASET_API LFW(const std::string &dataset_dir, const std::string &task, - const std::string &usage, const std::string &image_set, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - StringToChar(image_set), decode, sampler, cache); -} - -/// \class LibriTTSDataset -/// \brief A source dataset for reading and parsing LibriTTSDataset dataset. -class DATASET_API LibriTTSDataset : public Dataset { - public: - /// \brief Constructor of LibriTTSDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", - /// "test-other", "train-clean-100", "train-clean-360", "train-other-500" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of LibriTTSDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", - /// "test-other", "train-clean-100", "train-clean-360", "train-other-500" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LibriTTSDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", - /// "test-other", "train-clean-100", "train-clean-360", "train-other-500" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LibriTTSDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of LibriTTSDataset. - ~LibriTTSDataset() override = default; -}; - -/// \brief Function to create a LibriTTSDataset. -/// \note The generated dataset has seven columns ['waveform', 'sample_rate', 'original_text', 'normalized_text', -/// 'speaker_id', 'chapter_id', 'utterance_id']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", "test-other", -/// "train-clean-100", "train-clean-360", "train-other-500", or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LibriTTSDataset. -/// \par Example -/// \code -/// /* Define dataset path and LibriTTS object */ -/// std::string folder_path = "/path/to/libri_tts_dataset_directory"; -/// std::shared_ptr ds = LibriTTS(folder_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In LibriTTS dataset, each data dictionary has seven columns ["waveform", "sample_rate", -/// "original_text", "normalized_text", "speaker_id", "chapter_id", "utterance_id"].*/ -/// auto waveform = row["waveform"]; -/// \endcode -inline std::shared_ptr DATASET_API -LibriTTS(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a LibriTTSDataset. -/// \note The generated dataset has seven columns ['waveform', 'sample_rate', 'original_text', 'normalized_text', -/// 'speaker_id', 'chapter_id', 'utterance_id']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", "test-other", -/// "train-clean-100", "train-clean-360", "train-other-500", or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LibriTTSDataset. -inline std::shared_ptr DATASET_API LibriTTS(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a LibriTTSDataset. -/// \note The generated dataset has seven columns ['waveform', 'sample_rate', 'original_text', 'normalized_text', -/// 'speaker_id', 'chapter_id', 'utterance_id']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of LibriTTS, can be "dev-clean", "dev-other", "test-clean", "test-other", -/// "train-clean-100", "train-clean-360", "train-other-500", or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the LibriTTSDataset. -inline std::shared_ptr DATASET_API LibriTTS(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class LJSpeechDataset -/// \brief A source dataset for reading and parsing LJSpeech dataset. -class DATASET_API LJSpeechDataset : public Dataset { - public: - /// \brief Constructor of LJSpeechDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - LJSpeechDataset(const std::vector &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LJSpeechDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LJSpeechDataset(const std::vector &dataset_dir, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LJSpeechDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LJSpeechDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of LJSpeechDataset. - ~LJSpeechDataset() override = default; -}; - -/// \brief Function to create a LJSpeech Dataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcription", -/// "normalized_transcription"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API -LJSpeech(const std::string &dataset_dir, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a LJSpeech Dataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcription", -/// "normalized_transcription"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API LJSpeech(const std::string &dataset_dir, Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a LJSpeech Dataset. -/// \note The generated dataset has four columns ["waveform", "sample_rate", "transcription", -/// "normalized_transcription"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API LJSpeech(const std::string &dataset_dir, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \class LSUNDataset -/// \brief A source dataset for reading LSUN datast. -class DATASET_API LSUNDataset : public Dataset { - public: - /// \brief Constructor of LSUNDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] classes Classes list to load. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LSUNDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] classes Classes list to load. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of LSUNDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. - /// \param[in] classes Classes list to load. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - LSUNDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &classes, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of LSUNDataset. - ~LSUNDataset() override = default; -}; - -/// \brief Function to create a LSUNDataset. -/// \note The generated dataset has two columns "image" and "label". -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all` (Default=`all`). -/// \param[in] classes Classes list to load, such as 'bedroom', 'classroom' (Default={}, means load all classes). -/// \param[in] decode Decode the images after reading (Default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (Default = RandomSampler()). -/// \param[in] cache Tensor cache to use (Default=nullptr, which means no cache is used). -/// \return Shared pointer to the current LSUNDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/lsun_dataset_directory"; -/// std::shared_ptr ds = LSUN(folder_path, "all"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In LSUNDataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -LSUN(const std::string &dataset_dir, const std::string &usage = "all", const std::vector &classes = {}, - bool decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), VectorStringToChar(classes), - decode, sampler, cache); -} - -/// \brief Function to create a LSUNDataset. -/// \note The generated dataset has two columns "image" and "label". -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. -/// \param[in] classes Classes list to load. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (Default=nullptr, which means no cache is used). -/// \return Shared pointer to the current LSUNDataset. -inline std::shared_ptr DATASET_API LSUN(const std::string &dataset_dir, const std::string &usage, - const std::vector &classes, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), VectorStringToChar(classes), - decode, sampler, cache); -} - -/// \brief Function to create a LSUNDataset. -/// \note The generated dataset has two columns "image" and "label". -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of LSUN, can be `train`, `valid`, `test` or `all`. -/// \param[in] classes Classes list to load. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (Default=nullptr, which means no cache is used). -/// \return Shared pointer to the current LSUNDataset. -inline std::shared_ptr DATASET_API LSUN(const std::string &dataset_dir, const std::string &usage, - const std::vector &classes, bool decode, - const std::reference_wrapper sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), VectorStringToChar(classes), - decode, sampler, cache); -} - -/// \class ManifestDataset -/// \brief A source dataset for reading and parsing Manifest dataset. -class DATASET_API ManifestDataset : public Dataset { - public: - /// \brief Constructor of ManifestDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data (default="train"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder - /// names will be sorted alphabetically and each class will be given a unique index starting from 0). - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ManifestDataset(const std::vector &dataset_file, const std::vector &usage, - const std::shared_ptr &sampler, const std::map, int32_t> &class_indexing, - bool decode, const std::shared_ptr &cache); - - /// \brief Constructor of ManifestDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder - /// names will be sorted alphabetically and each class will be given a unique index starting from 0). - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ManifestDataset(const std::vector &dataset_file, const std::vector &usage, const Sampler *sampler, - const std::map, int32_t> &class_indexing, bool decode, - const std::shared_ptr &cache); - - /// \brief Constructor of ManifestDataset. - /// \param[in] dataset_file The dataset file to be read. - /// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder - /// names will be sorted alphabetically and each class will be given a unique index starting from 0). - /// \param[in] decode Decode the images after reading (default=false). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - ManifestDataset(const std::vector &dataset_file, const std::vector &usage, - const std::reference_wrapper &sampler, - const std::map, int32_t> &class_indexing, bool decode, - const std::shared_ptr &cache); - - /// \brief Destructor of ManifestDataset. - ~ManifestDataset() override = default; -}; - -/// \brief Function to create a ManifestDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_file The dataset file to be read. -/// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data (default="train"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder -/// names will be sorted alphabetically and each class will be given a unique index starting from 0). -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ManifestDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string file_path = "/path/to/manifest_file"; -/// std::shared_ptr ds = Manifest(file_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Manifest dataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Manifest(const std::string &dataset_file, const std::string &usage = "train", - const std::shared_ptr &sampler = std::make_shared(), - const std::map &class_indexing = {}, bool decode = false, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), StringToChar(usage), sampler, - MapStringToChar(class_indexing), decode, cache); -} - -/// \brief Function to create a ManifestDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_file The dataset file to be read. -/// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder -/// names will be sorted alphabetically and each class will be given a unique index starting from 0). -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ManifestDataset. -inline std::shared_ptr DATASET_API Manifest(const std::string &dataset_file, const std::string &usage, - const Sampler *sampler, - const std::map &class_indexing = {}, - bool decode = false, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), StringToChar(usage), sampler, - MapStringToChar(class_indexing), decode, cache); -} - -/// \brief Function to create a ManifestDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_file The dataset file to be read. -/// \param[in] usage Part of dataset of ManifestDataset, can be "train", "eval" or "inference" data. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] class_indexing A str-to-int mapping from label name to index (default={}, the folder -/// names will be sorted alphabetically and each class will be given a unique index starting from 0). -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the ManifestDataset. -inline std::shared_ptr DATASET_API Manifest(const std::string &dataset_file, const std::string &usage, - const std::reference_wrapper &sampler, - const std::map &class_indexing = {}, - bool decode = false, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), StringToChar(usage), sampler, - MapStringToChar(class_indexing), decode, cache); -} - -/// \class MindDataDataset -/// \brief A source dataset for reading and parsing MindRecord dataset. -class DATASET_API MindDataDataset : public Dataset { - public: - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source - /// in the same path will be found and loaded automatically. - /// \param[in] columns_list List of columns to be read (default={}). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()), - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle samples in file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector &dataset_file, const std::vector> &columns_list, - const std::shared_ptr &sampler, const nlohmann::json *padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source - /// in the same path will be found and loaded automatically. - /// \param[in] columns_list List of columns to be read. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle samples in file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector &dataset_file, const std::vector> &columns_list, - const Sampler *sampler, const nlohmann::json *padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source - /// in the same path will be found and loaded automatically. - /// \param[in] columns_list List of columns to be read. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle samples in file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector &dataset_file, const std::vector> &columns_list, - const std::reference_wrapper &sampler, const nlohmann::json *padded_sample, - int64_t num_padded, ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_files List of dataset files to be read directly. - /// \param[in] columns_list List of columns to be read. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle data within each file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, const std::shared_ptr &sampler, - const nlohmann::json *padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_files List of dataset files to be read directly. - /// \param[in] columns_list List of columns to be read. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle data within each file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, const Sampler *sampler, - const nlohmann::json *padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Constructor of MindDataDataset. - /// \param[in] dataset_files List of dataset files to be read directly. - /// \param[in] columns_list List of columns to be read. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. - /// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. - /// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. - /// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// ShuffleMode::kInfile - Shuffle samples in file. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MindDataDataset(const std::vector> &dataset_files, - const std::vector> &columns_list, const std::reference_wrapper &sampler, - const nlohmann::json *padded_sample, int64_t num_padded, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, - const std::shared_ptr &cache = nullptr); - - /// \brief Destructor of MindDataDataset. - ~MindDataDataset() override = default; -}; - -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source -/// in the same path will be found and loaded automatically. -/// \param[in] columns_list List of columns to be read (default={}). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()), -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle samples in file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current MindDataDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string file_path = "/path/to/mindrecord_file"; -/// std::vector column_names = {"data", "file_name", "label"}; -/// std::shared_ptr ds = MindData(file_path, column_names); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, each data dictionary owns keys "data", "file_name" and "label" */ -/// auto data = row["data"]; -/// \endcode -inline std::shared_ptr DATASET_API -MindData(const std::string &dataset_file, const std::vector &columns_list = {}, - const std::shared_ptr &sampler = std::make_shared(), - nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} - -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source -/// in the same path will be found and loaded automatically. -/// \param[in] columns_list List of columns to be read. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle samples in file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MindDataDataset. -inline std::shared_ptr DATASET_API -MindData(const std::string &dataset_file, const std::vector &columns_list, const Sampler *sampler, - nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_file File name of one component of a mindrecord source. Other files with identical source -/// in the same path will be found and loaded automatically. -/// \param[in] columns_list List of columns to be read. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle samples in file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MindDataDataset. -inline std::shared_ptr DATASET_API MindData( - const std::string &dataset_file, const std::vector &columns_list, - const std::reference_wrapper &sampler, nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_file), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} - -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_files List of dataset files to be read directly. -/// \param[in] columns_list List of columns to be read (default={}). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()), -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle samples in file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MindDataDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string file_path1 = "/path/to/mindrecord_file1"; -/// std::string file_path2 = "/path/to/mindrecord_file2"; -/// std::vector file_list = {file_path1, file_path2}; -/// std::vector column_names = {"data", "file_name", "label"}; -/// std::shared_ptr ds = MindData(file_list, column_names); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, each data dictionary owns keys "data", "file_name" and "label" */ -/// auto data = row["data"]; -/// \endcode -inline std::shared_ptr DATASET_API -MindData(const std::vector &dataset_files, const std::vector &columns_list = {}, - const std::shared_ptr &sampler = std::make_shared(), - nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} - -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_files List of dataset files to be read directly. -/// \param[in] columns_list List of columns to be read. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle data within each file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MindDataDataset. -inline std::shared_ptr DATASET_API -MindData(const std::vector &dataset_files, const std::vector &columns_list, - const Sampler *sampler, nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} - -/// \brief Function to create a MindDataDataset. -/// \param[in] dataset_files List of dataset files to be read directly. -/// \param[in] columns_list List of columns to be read. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// supported sampler list: SubsetRandomSampler, PkSampler, RandomSampler, SequentialSampler, DistributedSampler. -/// \param[in] padded_sample Samples will be appended to dataset, where keys are the same as column_list. -/// \param[in] num_padded Number of padding samples. Dataset size plus num_padded should be divisible by num_shards. -/// \param[in] shuffle_mode The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// ShuffleMode::kInfile - Shuffle samples in file. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MindDataDataset. -inline std::shared_ptr DATASET_API MindData( - const std::vector &dataset_files, const std::vector &columns_list, - const std::reference_wrapper &sampler, nlohmann::json *padded_sample = nullptr, int64_t num_padded = 0, - ShuffleMode shuffle_mode = ShuffleMode::kGlobal, const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), VectorStringToChar(columns_list), sampler, - padded_sample, num_padded, shuffle_mode, cache); -} - -/// \class MnistDataset -/// \brief A source dataset for reading and parsing MNIST dataset. -class DATASET_API MnistDataset : public Dataset { - public: - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of MnistDataset. - ~MnistDataset() override = default; -}; - -/// \brief Function to create a MnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MnistDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/mnist_dataset_directory"; -/// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 20)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In MNIST dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Mnist(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a MnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MnistDataset. -inline std::shared_ptr DATASET_API Mnist(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a MnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the MnistDataset. -inline std::shared_ptr DATASET_API Mnist(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class Multi30kDataset -/// \brief A source dataset that reads and parses Multi30k dataset. -class DATASET_API Multi30kDataset : public Dataset { - public: - /// \brief Constructor of Multi30kDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MULTI30K, can be "train", "test", "valid" or "all". - /// \param[in] language_pair List containing text and translation language. - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - Multi30kDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::vector> &language_pair, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of Multi30kDataset. - ~Multi30kDataset() override = default; -}; - -/// \brief Function to create a Multi30kDataset. -/// \note The generated dataset has two columns ["text", "translation"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of MULTI30K, can be "train", "test", "valid" or "all" (default = "all"). -/// \param[in] language_pair List containing text and translation language (default = {"en", "de"}). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the Multi30kDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_dir = "/path/to/multi30k_dataset_directory"; -/// std::shared_ptr ds = Multi30k(dataset_dir, "all"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Multi30kdataset, each dictionary has keys "text" and "translation" */ -/// auto text = row["text"]; -/// \endcode -inline std::shared_ptr DATASET_API -Multi30k(const std::string &dataset_dir, const std::string &usage = "all", - const std::vector &language_pair = {"en", "de"}, int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), - VectorStringToChar(language_pair), num_samples, shuffle, num_shards, - shard_id, cache); -} - -/// \class OmniglotDataset -/// \brief A source dataset for reading and parsing Omniglot dataset. -class DATASET_API OmniglotDataset : public Dataset { - public: - /// \brief Constructor of OmniglotDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] background A flag to use background dataset or evaluation dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of OmniglotDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] background A flag to use background dataset or evaluation dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of OmniglotDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] background A flag to use background dataset or evaluation dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - OmniglotDataset(const std::vector &dataset_dir, bool background, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// Destructor of OmniglotDataset. - ~OmniglotDataset() override = default; -}; - -/// \brief Function to create an OmniglotDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] background A flag to use background dataset or evaluation dataset (Default=true). -/// \param[in] decode Decode the images after reading (Default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current OmniglotDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/omniglot_dataset_directory"; -/// std::shared_ptr ds = Omniglot(folder_path, true, false, std::make_shared(false, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In Omniglot dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Omniglot(const std::string &dataset_dir, bool background = true, bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), background, decode, sampler, cache); -} - -/// \brief Function to create an OmniglotDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] background A flag to use background dataset or evaluation dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current OmniglotDataset. -inline std::shared_ptr DATASET_API Omniglot(const std::string &dataset_dir, bool background, - bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), background, decode, sampler, cache); -} - -/// \brief Function to create an OmniglotDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] background A flag to use background dataset or evaluation dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current OmniglotDataset. -inline std::shared_ptr DATASET_API Omniglot(const std::string &dataset_dir, bool background, - bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), background, decode, sampler, cache); -} - -/// \class PennTreebankDataset -/// \brief A source dataset for reading and parsing PennTreebank dataset. -class DATASET_API PennTreebankDataset : public Dataset { - public: - /// \brief Constructor of PennTreebank Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data list txt file to be read, can be "train", "test", 'valid' or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - PennTreebankDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of PennTreebankDataset. - ~PennTreebankDataset() override = default; -}; - -/// \brief Function to create a PennTreebank Dataset. -/// \note The generated dataset has one column ['text']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train" , 'valid' or "test" (default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode.kFalse - No shuffling is performed. -/// ShuffleMode.kFiles - Shuffle files only. -/// ShuffleMode.kGlobal - Shuffle both the files and samples. -/// \param[in] usage One of "all", "train", "valid" or "test" (default = "all"). -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the TextFileDataset. -inline std::shared_ptr DATASET_API -PennTreebank(const std::string &dataset_dir, const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class PhotoTourDataset -/// \brief A source dataset for reading and parsing PhotoTour dataset. -class DATASET_API PhotoTourDataset : public Dataset { - public: - /// \brief Constructor of PhotoTourDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', - /// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. - /// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test`. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of PhotoTourDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', - /// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. - /// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test`. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of PhotoTourDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', - /// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. - /// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test`. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - PhotoTourDataset(const std::vector &dataset_dir, const std::vector &name, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of PhotoTourDataset. - ~PhotoTourDataset() override = default; -}; - -/// \brief Function to create a PhotoTourDataset. -/// \note If usage is 'train', the generated dataset has one column ["image"], else -/// three columns ["image1", "image2", "matches"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', -/// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. -/// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test` (default="train"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples -/// from the dataset. If sampler is not given, a `RandomSampler` will -/// be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current PhotoTourDataset. -inline std::shared_ptr DATASET_API -PhotoTour(const std::string &dataset_dir, const std::string &name, const std::string &usage = "train", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \brief Function to create a PhotoTourDataset. -/// \note If usage is 'train', the generated dataset has one column ["image"], else -/// three columns ["image1", "image2", "matches"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', -/// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. -/// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test`. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current PhotoTourDataset. -inline std::shared_ptr DATASET_API PhotoTour(const std::string &dataset_dir, const std::string &name, - const std::string &usage, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \brief Function to create a PhotoTourDataset. -/// \note If usage is 'train', the generated dataset has one column ["image"], else -/// three columns ["image1", "image2", "matches"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] name Name of the dataset to load, should be one of 'notredame', 'yosemite', 'liberty', -/// 'notredame_harris', 'yosemite_harris' or 'liberty_harris'. -/// \param[in] usage Part of dataset of PhotoTour, can be `train` or `test`. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current PhotoTourDataset. -inline std::shared_ptr DATASET_API PhotoTour(const std::string &dataset_dir, const std::string &name, - const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(name), StringToChar(usage), sampler, - cache); -} - -/// \class Places365Dataset -/// \brief A source dataset that reads and parses Places365 dataset. -class DATASET_API Places365Dataset : public Dataset { - public: - /// \brief Constructor of Places365Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` or `val`. - /// \param[in] small Use the small images instead of the high resolution ones. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, bool small, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Places365Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` or `val`. - /// \param[in] small Use the small images instead of the high resolution ones. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, bool small, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of Places365Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` or `val`. - /// \param[in] small Use the small images instead of the high resolution ones. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - Places365Dataset(const std::vector &dataset_dir, const std::vector &usage, bool small, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of Places365Dataset. - ~Places365Dataset() override = default; -}; - -/// \brief Function to create a Places365Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` -/// or `val` (default="train-standard"). -/// \param[in] small Use the small images instead of the high resolution ones (default=false). -/// \param[in] decode Decode the images after reading (default=true). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples -/// from the dataset. If sampler is not given, a `RandomSampler` will -/// be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Places365Dataset. -inline std::shared_ptr DATASET_API -Places365(const std::string &dataset_dir, const std::string &usage = "train-standard", const bool small = false, - const bool decode = true, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), small, decode, sampler, - cache); -} - -/// \brief Function to create a Places365Dataset -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` or `val`. -/// \param[in] small Use the small images instead of the high resolution ones. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Places365Dataset. -inline std::shared_ptr DATASET_API Places365(const std::string &dataset_dir, const std::string &usage, - const bool small, const bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), small, decode, sampler, - cache); -} - -/// \brief Function to create a Places365Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Dataset splits of Places365, can be `train-standard`, `train-challenge` or `val`. -/// \param[in] small Use the small images instead of the high resolution ones. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Places365Dataset. -inline std::shared_ptr DATASET_API Places365(const std::string &dataset_dir, const std::string &usage, - const bool small, const bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), small, decode, sampler, - cache); -} - -/// \class QMnistDataset -/// \brief A source dataset that reads and parses QMNIST dataset. -class DATASET_API QMnistDataset : public Dataset { - public: - /// \brief Constructor of QMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all". - /// \param[in] compat Whether the label for each example is class number (compat=true) - /// or the full QMNIST information (compat=false). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of QMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all". - /// \param[in] compat Whether the label for each example is class number (compat=true) - /// or the full QMNIST information (compat=false). - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of QMnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all". - /// \param[in] compat Whether the label for each example is class number (compat=true) - /// or the full QMNIST information (compat=false). - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - QMnistDataset(const std::vector &dataset_dir, const std::vector &usage, bool compat, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of QMnistDataset. - ~QMnistDataset() override = default; -}; - -/// \brief Function to create a QMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all" (default = "all"). -/// \param[in] compat Whether the label for each example is class number or the full QMNIST information -/// (default = true). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the QMnistDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/qmnist_dataset_directory"; -/// std::shared_ptr ds = QMnist(folder_path, "train", true, std::make_shared(false, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In QMNIST dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -QMnist(const std::string &dataset_dir, const std::string &usage = "all", bool compat = true, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), compat, sampler, cache); -} - -/// \brief Function to create a QMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all". -/// \param[in] compat Whether the label for each example is class number or the full QMNIST information. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the QMnistDataset. -inline std::shared_ptr DATASET_API QMnist(const std::string &dataset_dir, const std::string &usage, - bool compat, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), compat, sampler, cache); -} - -/// \brief Function to create a QMnistDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of QMNIST, can be "train", "test", "test10k", "test50k", "nist" or "all". -/// \param[in] compat Whether the label for each example is class number or the full QMNIST information. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the QMnistDataset. -inline std::shared_ptr DATASET_API QMnist(const std::string &dataset_dir, const std::string &usage, - bool compat, const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), compat, sampler, cache); -} - -/// \brief Function to create a ConcatDataset. -/// \note Reload "+" operator to concat two datasets. -/// \param[in] datasets1 Shared pointer to the first dataset to be concatenated. -/// \param[in] datasets2 Shared pointer to the second dataset to be concatenated. -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API operator+(const std::shared_ptr &datasets1, - const std::shared_ptr &datasets2) { - return std::make_shared(std::vector({datasets1, datasets2})); -} - -/// \class RandomDataDataset -/// \brief A source dataset that generates random data. -class DATASET_API RandomDataDataset : public Dataset { - public: - /// \brief Constructor of RandomDataDataset. - /// \param[in] total_rows Number of rows for the dataset to generate (default=0, number of rows is random). - /// \param[in] schema SchemaObj to set column type, data type and data shape. - /// \param[in] columns_list List of columns to be read (default={}, read all columns). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - RandomDataDataset(const int32_t &total_rows, std::shared_ptr schema, - const std::vector> &columns_list, const std::shared_ptr &cache); - - /// \brief Constructor of RandomDataDataset. - /// \param[in] total_rows Number of rows for the dataset to generate (default=0, number of rows is random). - /// \param[in] schema_path Path of schema file. - /// \param[in] columns_list List of columns to be read (default={}, read all columns). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - RandomDataDataset(const int32_t &total_rows, const std::vector &schema_path, - const std::vector> &columns_list, const std::shared_ptr &cache); - - /// \brief Destructor of RandomDataDataset. - ~RandomDataDataset() override = default; -}; - -/// \brief Function to create a RandomDataset. -/// \param[in] total_rows Number of rows for the dataset to generate (default=0, number of rows is random). -/// \param[in] schema SchemaObj to set column type, data type and data shape. -/// \param[in] columns_list List of columns to be read (default={}, read all columns). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the RandomDataset. -/// \par Example -/// \code -/// /* Define MindData objects */ -/// std::shared_ptr schema = Schema(); -/// schema->add_column("column1", mindspore::DataType::kNumberTypeUInt8, {2}); -/// schema->add_column("column2", mindspore::DataType::kNumberTypeUInt8, {1}); -/// std::shared_ptr ds = RandomData(50, schema); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined the schema before, each data dictionary owns keys "column1" and "column2" */ -/// auto column1 = row["column1"]; -/// \endcode -template > -std::shared_ptr DATASET_API RandomData(const int32_t &total_rows = 0, const T &schema = nullptr, - const std::vector &columns_list = {}, - const std::shared_ptr &cache = nullptr) { - std::shared_ptr ds; - if constexpr (std::is_same::value || std::is_same>::value) { - std::shared_ptr schema_obj = schema; - ds = - std::make_shared(total_rows, std::move(schema_obj), VectorStringToChar(columns_list), cache); - } else { - ds = std::make_shared(total_rows, StringToChar(schema), VectorStringToChar(columns_list), cache); - } - return ds; -} - -/// \class RenderedSST2Dataset -/// \brief A source dataset for reading and parsing RenderedSST2 dataset. -class DATASET_API RenderedSST2Dataset : public Dataset { - public: - /// \brief Constructor of RenderedSST2Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of RenderedSST2Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of RenderedSST2Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - RenderedSST2Dataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of RenderedSST2Dataset. - ~RenderedSST2Dataset() override = default; -}; - -/// \brief Function to create a RenderedSST2Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". Default: "all". -/// \param[in] decode Decode the images after reading. Default: false. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset. Default: RandomSampler(). -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the RenderedSST2Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string dataset_path = "/path/to/RenderedSST2_dataset_directory"; -/// std::shared_ptr ds = RenderedSST2(dataset_path); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In RenderedSST2 dataset, each data dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -RenderedSST2(const std::string &dataset_dir, const std::string &usage = "all", bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a RenderedSST2Dataset -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the RenderedSST2Dataset. -inline std::shared_ptr DATASET_API -RenderedSST2(const std::string &dataset_dir, const std::string &usage, bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a RenderedSST2Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of dataset. Acceptable usages include "train", "test", "val" or "all". -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the RenderedSST2Dataset. -inline std::shared_ptr DATASET_API -RenderedSST2(const std::string &dataset_dir, const std::string &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \class SBUDataset -/// \brief A source dataset that reads and parses SBU dataset. -class DATASET_API SBUDataset : public Dataset { - public: - /// \brief Constructor of SBUDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - SBUDataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SBUDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SBUDataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SBUDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SBUDataset(const std::vector &dataset_dir, bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of SBUDataset. - ~SBUDataset() override = default; -}; - -/// \brief Function to create a SBUDataset. -/// \note The generated dataset has two columns ["image", "caption"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading (default=false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current SBUDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/sbu_dataset_directory"; -/// std::shared_ptr ds = SBU(folder_path, true, std::make_shared(false, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In SBU dataset, each dictionary has keys "image" and "caption" */ -/// auto caption = row["caption"]; -/// \endcode -inline std::shared_ptr DATASET_API -SBU(const std::string &dataset_dir, bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a SBUDataset. -/// \note The generated dataset has two columns ["image", "caption"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current SBUDataset. -inline std::shared_ptr DATASET_API SBU(const std::string &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a SBUDataset. -/// \note The generated dataset has two columns ["image", "caption"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current SBUDataset. -inline std::shared_ptr DATASET_API SBU(const std::string &dataset_dir, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \class SemeionDataset -/// \brief A source dataset for reading and parsing Semeion dataset. -class DATASET_API SemeionDataset : public Dataset { - public: - /// \brief Constructor of SemeionDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SemeionDataset(const std::vector &dataset_dir, const ::std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SemeionDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SemeionDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SemeionDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SemeionDataset(const std::vector &dataset_dir, const ::std::reference_wrapper &samlper, - const std::shared_ptr &cache); - - /// \brief Destructor of SemeionDataset. - ~SemeionDataset() override = default; -}; - -/// \brief Function to create a Semeion Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SemeionDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/semeion_dataset_directory"; -/// std::shared_ptr ds = SEMEION(folder_path, std::make_shared(0, 6)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In SEMEION dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Semeion(const std::string &dataset_dir, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a Semeion Dataset -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SemeionDataset. -inline std::shared_ptr DATASET_API Semeion(const std::string &dataset_dir, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a Semeion Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SemeionDataset. -inline std::shared_ptr DATASET_API Semeion(const std::string &dataset_dir, Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \class SogouNewsDataset -/// \brief A source dataset for reading and parsing Sogou News dataset. -class DATASET_API SogouNewsDataset : public Dataset { - public: - /// \brief Constructor of SogouNewsDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SogouNews, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - SogouNewsDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of SogouNewsDataset. - ~SogouNewsDataset() override = default; -}; - -/// \brief Function to create a SogouNewsDataset. -/// \note This dataset includes polarity and full, which can be read according to your own needs. -/// \note The generated dataset has three columns ["index", "title" , "content"]. Their types are all string. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of SogouNews, can be "train", "test" or "all" data (default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SogouNewsDataset. -inline std::shared_ptr DATASET_API SogouNews(const std::string &dataset_dir, - const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class SpeechCommandsDataset. -/// \brief A source dataset that reads and parses SpeechCommands dataset. -class DATASET_API SpeechCommandsDataset : public Dataset { - public: - /// \brief Constructor of SpeechCommandsDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of SpeechCommandsDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SpeechCommandsDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SpeechCommandsDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of SpeechCommandsDataset. - ~SpeechCommandsDataset() override = default; -}; - -/// \brief Function to create a SpeechCommands Dataset. -/// \note The generated dataset has five columns ["waveform", "sample_rate", "label", "speaker_id", "utterance_number"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SpeechCommandsDataset. -inline std::shared_ptr DATASET_API -SpeechCommands(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a SpeechCommands Dataset. -/// \note The generated dataset has five columns ["waveform", "sample_rate", "label", "speaker_id", "utterance_number"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all". -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SpeechCommandsDataset. -inline std::shared_ptr DATASET_API -SpeechCommands(const std::string &dataset_dir, const std::string &usage, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a SpeechCommands Dataset. -/// \note The generated dataset has five columns ["waveform", "sample_rate", "label", "speaker_id", "utterance_number"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of SpeechCommands, can be "train", "test", "valid" or "all". -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SpeechCommandsDataset. -inline std::shared_ptr DATASET_API -SpeechCommands(const std::string &dataset_dir, const std::string &usage, const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class SQuADDataset -/// \brief A source dataset that reads and parses SQuAD dataset. -class DATASET_API SQuADDataset : public Dataset { - public: - /// \brief Constructor of SQuADDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SQuAD, can be "train", "dev" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - SQuADDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of SQuADUDataset. - ~SQuADDataset() override = default; -}; - -/// \brief Function to create a SQuADDataset. -/// \note The generated dataset has four columns ["context", "question", "text", "answer_start"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of SQuAD, can be "train", "dev" or "all" (Default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default=0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default=1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default=0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the SQuADDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/squad_dataset_directory"; -/// std::shared_ptr ds = SQuAD(folder_path, "train", 0, ShuffleMode::kFalse, -/// std::make_shared(0, 6)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In SQuAD dataset, each dictionary has keys "context", "question", "text", "answer_start" */ -/// auto context = row["context"]; -/// \endcode -inline std::shared_ptr DATASET_API SQuAD(const std::string &dataset_dir, const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class SST2Dataset -/// \brief A source dataset for reading and parsing SST2 dataset. -class DATASET_API SST2Dataset : public Dataset { - public: - /// \brief Constructor of SST2Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of SST2, can be "train", "test" or "dev". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kGlobal - Shuffle the samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - SST2Dataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of SST2. - ~SST2Dataset() override = default; -}; - -/// \brief Function to create a SST2Dataset. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of SST2, can be "train", "test" or "dev". Default: "train". -/// \param[in] num_samples The number of samples to be included in the dataset. -/// Default: 0, means all samples. -/// \param[in] shuffle The mode for shuffling data every epoch. Default: ShuffleMode::kGlobal. -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into. Default: 1. -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified. Default: 0. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the SST2Dataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/sst2_dataset_directory"; -/// std::shared_ptr ds = SST2(folder_path, "train"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// \endcode -inline std::shared_ptr DATASET_API SST2(const std::string &dataset_dir, const std::string &usage = "train", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, - int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, num_shards, - shard_id, cache); -} - -/// \class STL10Dataset -/// \brief A source dataset that reads and parses STL10 dataset. -class DATASET_API STL10Dataset : public Dataset { - public: - /// \brief Constructor of STL10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of STL10, can be "train", "test", "unlabeled", "train+unlabeled" - /// or "all". - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of STL10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of STL10, can be "train", "test", "unlabeled", "train+unlabeled" - /// or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of STL10Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of STL10, can be "train", "test", "unlabeled", "train+unlabeled" - /// or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - STL10Dataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of STL10Dataset. - ~STL10Dataset() override = default; -}; - -/// \brief Function to create a STL10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of STL10, can be "train", "test", "unlabeled", "train+unlabeled" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API -STL10(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a STL10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of STL10, can be "train", "test", "unlabeled" or "train+unlabeled" or "all" -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API STL10(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a STL10 Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of STL10, can be "train", "test", "unlabeled", "train+unlabeled" or "all" -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API STL10(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \class SUN397Dataset. -/// \brief A source dataset that reads and parses SUN397 dataset. -class DATASET_API SUN397Dataset : public Dataset { - public: - /// \brief Constructor of SUN397Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - SUN397Dataset(const std::vector &dataset_dir, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SUN397Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SUN397Dataset(const std::vector &dataset_dir, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of SUN397Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - SUN397Dataset(const std::vector &dataset_dir, bool decode, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of SUN397Dataset. - ~SUN397Dataset() override = default; -}; - -/// \brief Function to create a SUN397Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading. Default: true. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples -/// be used to randomly iterate the entire dataset. Default: RandomSampler(). -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the current SUN397Dataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/sun397_dataset_directory"; -/// std::shared_ptr ds = SUN397(folder_path, false, std::make_shared(false, 5)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In SUN397 dataset dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -SUN397(const std::string &dataset_dir, bool decode = true, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a SUN397Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the current SUN397Dataset. -inline std::shared_ptr DATASET_API SUN397(const std::string &dataset_dir, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \brief Function to create a SUN397Dataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. Default: nullptr, which means no cache is used. -/// \return Shared pointer to the current SUN397Dataset. -inline std::shared_ptr DATASET_API SUN397(const std::string &dataset_dir, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), decode, sampler, cache); -} - -/// \class TedliumDataset -/// \brief A source dataset for reading and parsing tedlium dataset. -class DATASET_API TedliumDataset : public Dataset { - public: - /// \brief Constructor of TedliumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] release Release of the dataset, can be "release1", "release2", "release3". - /// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, - /// can be "train", "test" or "all". - /// \param[in] extensions The extensions of audio file. Only support ".sph" now. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - TedliumDataset(const std::vector &dataset_dir, const std::vector &release, const std::vector &usage, - const std::vector &extensions, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of TedliumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] release Release of the dataset, can be "release1", "release2", "release3". - /// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, - /// can be "train", "test" or "all". - /// \param[in] extensions The extensions of audio file. Only support ".sph" now. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - TedliumDataset(const std::vector &dataset_dir, const std::vector &release, const std::vector &usage, - const std::vector &extensions, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of TedliumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] release Release of the dataset, can be "release1", "release2", "release3". - /// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, - /// can be "train", "test" or "all". - /// \param[in] extensions The extensions of audio file. Only support ".sph" now. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - TedliumDataset(const std::vector &dataset_dir, const std::vector &release, const std::vector &usage, - const std::vector &extensions, const std::reference_wrapper &samlper, - const std::shared_ptr &cache); - - /// \brief Destructor of TedliumDataset. - ~TedliumDataset() override = default; -}; - -/// \brief Function to create a TedliumDataset. -/// \note The generated dataset has six columns ["waveform", "sample_rate", "transcript", "talk_id", "speaker_id", -/// "identifier"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] release Release of the dataset, can be "release1", "release2", "release3". -/// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, -/// can be "train", "test" or "all" (default = "all"). -/// \param[in] extensions The extensions of audio file. Only support ".sph" now (default = ".sph"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the TedliumDataset. -inline std::shared_ptr DATASET_API Tedlium( - const std::string &dataset_dir, const std::string &release, const std::string &usage = "all", - const std::string &extensions = ".sph", const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(release), StringToChar(usage), - StringToChar(extensions), sampler, cache); -} - -/// \brief Function to create a TedliumDataset. -/// \note The generated dataset has six columns ["waveform", "sample_rate","transcript", "talk_id", "speaker_id", -/// "identifier"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] release Release of the dataset, can be "release1", "release2", "release3". -/// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, -/// can be "train", "test" or "all". -/// \param[in] extensions The extensions of audio file. Only support ".sph" now. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the TedliumDataset. -inline std::shared_ptr DATASET_API Tedlium(const std::string &dataset_dir, const std::string &release, - const std::string &usage, const std::string &extensions, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(release), StringToChar(usage), - StringToChar(extensions), sampler, cache); -} - -/// \brief Function to create a TedliumDataset. -/// \note The generated dataset has six columns ["waveform", "sample_rate","transcript", "talk_id", "speaker_id", -/// "identifier"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] release Release of the dataset, can be "release1", "release2", "release3". -/// \param[in] usage Part of dataset of TEDLIUM, for release3, only can be "all", for release1 and release2, -/// can be "train", "test" or "all". -/// \param[in] extensions The extensions of audio file. Only support ".sph" now. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the TedliumDataset. -inline std::shared_ptr DATASET_API Tedlium(const std::string &dataset_dir, const std::string &release, - const std::string &usage, const std::string &extensions, - Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(release), StringToChar(usage), - StringToChar(extensions), sampler, cache); -} - -/// \class TextFileDataset -/// \brief A source dataset that reads and parses datasets stored on disk in text format. -class DATASET_API TextFileDataset : public Dataset { - public: - /// \brief Constructor of TextFileDataset. - /// \param[in] dataset_files List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] num_samples The number of samples to be included in the dataset - /// (Default = 0 means all samples). - /// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified (Default = 0). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - TextFileDataset(const std::vector> &dataset_files, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of TextFileDataset. - ~TextFileDataset() override = default; -}; - -/// \brief Function to create a TextFileDataset. -/// \note The generated dataset has one column ['text']. -/// \param[in] dataset_files List of files to be read to search for a pattern of files. The list -/// will be sorted in a lexicographical order. -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0 means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode.kFalse - No shuffling is performed. -/// ShuffleMode.kFiles - Shuffle files only. -/// ShuffleMode.kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the TextFileDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string file_path = "/path/to/text_file_dataset_file"; -/// std::shared_ptr ds = TextFile({file_path}, 2); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In TextFile dataset, each dictionary has key "text" */ -/// auto text = row["text"]; -/// \endcode -inline std::shared_ptr DATASET_API TextFile(const std::vector &dataset_files, - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(VectorStringToChar(dataset_files), num_samples, shuffle, num_shards, - shard_id, cache); -} - -/// \class TFRecordDataset -/// \brief A source dataset for reading and parsing datasets stored on disk in TFData format. -class DATASET_API TFRecordDataset : public Dataset { - public: - /// \brief Constructor of TFRecordDataset. - /// \param[in] dataset_files List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] schema Path to schema file. - /// \param[in] columns_list List of columns to be read. (Default = {}, read all columns). - /// \param[in] num_samples The number of samples to be included in the dataset. - /// (Default = 0 means all samples). - /// Processing priority for `num_samples` is as the following: - /// 1. If `num_samples` is greater than 0, read `num_samples` rows. - /// 2. Otherwise, if numRows (parsed from `schema` ) is greater than 0, read numRows rows. - /// 3. Otherwise, read the full dataset. - /// `num_samples` or numRows (parsed from `schema` ) will be interpreted as number of rows per shard. - /// It is highly recommended to provide `num_samples` or numRows (parsed from `schema` ) - /// when `compression_type` is "GZIP" or "ZLIB" to avoid performance degradation due to multiple - /// decompressions of the same file to obtain the file size. - /// \param[in] shuffle The mode for shuffling data every epoch. (Default = ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1). - /// \param[in] shard_id The shard ID within num_shards. This argument should be specified only - /// when num_shards is also specified. (Default = 0). - /// \param[in] shard_equal_rows Get equal rows for all shards. - /// (Default = false, number of rows of each shard may be not equal). - /// When `compression_type` is "GZIP" or "ZLIB", and `num_samples` or numRows (parsed from `schema` ) is - /// provided, shard_equal_rows` will be implied as true. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] compression_type Compression type to use. - /// (Default = "", which means no compression is used). - /// Can be any of: - /// "" - No compression is used. - /// "GZIP" - GZIP compression is used. - /// "ZLIB" - ZLIB compression is used. - TFRecordDataset(const std::vector> &dataset_files, const std::vector &schema, - const std::vector> &columns_list, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, bool shard_equal_rows, - const std::shared_ptr &cache, const std::vector &compression_type); - - /// \brief Constructor of TFRecordDataset. - /// \note Parameter 'schema' is shared pointer to Schema object - /// \param[in] dataset_files List of files to be read to search for a pattern of files. The list - /// will be sorted in a lexicographical order. - /// \param[in] schema SchemaObj to set column type, data type and data shape. - /// \param[in] columns_list List of columns to be read (Default = {}, read all columns). - /// \param[in] num_samples The number of samples to be included in the dataset - /// (Default = 0 means all samples). - /// Processing priority for `num_samples` is as the following: - /// 1. If `num_samples` is greater than 0, read `num_samples` rows. - /// 2. Otherwise, if numRows (parsed from `schema` ) is greater than 0, read numRows rows. - /// 3. Otherwise, read the full dataset. - /// `num_samples` or numRows (parsed from `schema` ) will be interpreted as number of rows per shard. - /// It is highly recommended to provide `num_samples` or numRows (parsed from `schema` ) - /// when `compression_type` is "GZIP" or "ZLIB" to avoid performance degradation due to multiple - /// decompressions of the same file to obtain the file size. - /// \param[in] shuffle The mode for shuffling data every epoch. (Default = ShuffleMode::kGlobal). - /// Can be any of: - /// ShuffleMode::kFalse - No shuffling is performed. - /// ShuffleMode::kFiles - Shuffle files only. - /// ShuffleMode::kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1). - /// \param[in] shard_id The shard ID within num_shards. This argument should be specified only - /// when num_shards is also specified. (Default = 0). - /// \param[in] shard_equal_rows Get equal rows for all shards. - /// (Default = false, number of rows of each shard may be not equal). - /// When `compression_type` is "GZIP" or "ZLIB", and `num_samples` or numRows (parsed from `schema` ) is - /// provided, shard_equal_rows` will be implied as true. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] compression_type Compression type to use. - /// (Default = "", which means no compression is used). - /// Can be any of: - /// "" - No compression is used. - /// "GZIP" - GZIP compression is used. - /// "ZLIB" - ZLIB compression is used. - TFRecordDataset(const std::vector> &dataset_files, const std::shared_ptr &schema, - const std::vector> &columns_list, int64_t num_samples, ShuffleMode shuffle, - int32_t num_shards, int32_t shard_id, bool shard_equal_rows, - const std::shared_ptr &cache, const std::vector &compression_type); - - /// \brief Destructor of TFRecordDataset. - ~TFRecordDataset() override = default; -}; - -/// \brief Function to create a TFRecordDataset. -/// \param[in] dataset_files List of files to be read to search for a pattern of files. The list -/// will be sorted in a lexicographical order. -/// \param[in] schema SchemaObj or string to schema path. (Default = nullptr, which means that the -/// meta data from the TFData file is considered the schema). -/// \param[in] columns_list List of columns to be read (Default = {}, read all columns). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0 means all samples). -/// Processing priority for `num_samples` is as the following: -/// 1. If `num_samples` is greater than 0, read `num_samples` rows. -/// 2. Otherwise, if numRows (parsed from `schema` ) is greater than 0, read numRows rows. -/// 3. Otherwise, read the full dataset. -/// `num_samples` or numRows (parsed from `schema` ) will be interpreted as number of rows per shard. -/// It is highly recommended to provide `num_samples` or numRows (parsed from `schema` ) -/// when `compression_type` is "GZIP" or "ZLIB" to avoid performance degradation due to multiple -/// decompressions of the same file to obtain the file size. -/// \param[in] shuffle The mode for shuffling data every epoch. (Default = ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into. (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be specified only -/// when num_shards is also specified. (Default = 0). -/// \param[in] shard_equal_rows Get equal rows for all shards. -/// (Default = false, number of rows of each shard may be not equal). -/// When `compression_type` is "GZIP" or "ZLIB", and `num_samples` or numRows (parsed from `schema` ) is -/// provided, shard_equal_rows` will be implied as true. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] compression_type Compression type to use. -/// (Default = "", which means no compression is used). -/// Can be any of: -/// "" - No compression is used. -/// "GZIP" - GZIP compression is used. -/// "ZLIB" - ZLIB compression is used. -/// \return Shared pointer to the TFRecordDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string file_path = "/path/to/tfrecord_file"; -/// std::string schema_path = "/path/to/schema_file"; -/// std::shared_ptr ds = TFRecord({file_path}, schema_path, {"image"}); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: The columns of generated dataset depend on the source TFRecord files. */ -/// auto image = row["image"]; -/// \endcode -template > -std::shared_ptr DATASET_API -TFRecord(const std::vector &dataset_files, const T &schema = nullptr, - const std::vector &columns_list = {}, int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - bool shard_equal_rows = false, const std::shared_ptr &cache = nullptr, - const std::string &compression_type = "") { - std::shared_ptr ds; - if constexpr (std::is_same::value || std::is_same>::value) { - std::shared_ptr schema_obj = schema; - ds = std::make_shared(VectorStringToChar(dataset_files), std::move(schema_obj), - VectorStringToChar(columns_list), num_samples, shuffle, num_shards, shard_id, - shard_equal_rows, cache, StringToChar(compression_type)); - } else { - ds = std::make_shared(VectorStringToChar(dataset_files), StringToChar(schema), - VectorStringToChar(columns_list), num_samples, shuffle, num_shards, shard_id, - shard_equal_rows, cache, StringToChar(compression_type)); - } - return ds; -} - -/// \class UDPOSDataset -/// \brief A source dataset for reading and parsing UDPOS dataset. -class DATASET_API UDPOSDataset : public Dataset { - public: - /// \brief Constructor of UDPOS Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data list txt file to be read, can be "train", "test", 'valid' or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - UDPOSDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of UDPOSDataset. - ~UDPOSDataset() override = default; -}; - -/// \brief Function to create a UDPOSDataset. -/// \note The generated dataset has three column ['word','universal','stanford']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of UDPOS, can be "train", "test", "valid" or "all" (default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch. (Default=ShuffleMode.kGlobal) -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the UDPOSDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/udpos_dataset_directory"; -/// std::shared_ptr ds = UDPOS(dataset_dir, "test", 0, ShuffleMode::kGlobal); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In UDPOS dataset, each dictionary has keys "word", "universal", "stanford" */ -/// auto word = row["word"]; -/// \endcode -inline std::shared_ptr DATASET_API UDPOS(const std::string &dataset_dir, const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class USPSDataset -/// \brief A source dataset that reads and parses USPS datasets. -class DATASET_API USPSDataset : public Dataset { - public: - /// \brief Constructor of USPSDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Usage of USPS, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified (Default = 0). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - USPSDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, const std::shared_ptr &cache); - - /// \brief Destructor of USPSDataset. - ~USPSDataset() override = default; -}; - -/// \brief Function to create a USPSDataset. -/// \note The generated dataset has two columns ["image", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Usage of USPS, can be "train", "test" or "all" (Default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0 means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode.kFalse - No shuffling is performed. -/// ShuffleMode.kFiles - Shuffle files only. -/// ShuffleMode.kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \return Shared pointer to the current USPSDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/usps_dataset_directory"; -/// std::shared_ptr ds = USPS(folder_path, "train"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In USPS dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API USPS(const std::string &dataset_dir, const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, - int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, num_shards, - shard_id, cache); -} - -/// \class VOCDataset -/// \brief A source dataset for reading and parsing VOC dataset. -class DATASET_API VOCDataset : public Dataset { - public: - /// \brief Constructor of VOCDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". - /// \param[in] usage The type of data list text file to be read (default = "train"). - /// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). - VOCDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::map, int32_t> &class_indexing, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache, bool extra_metadata); - - /// \brief Constructor of VOCDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". - /// \param[in] usage The type of data list text file to be read. - /// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). - VOCDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::map, int32_t> &class_indexing, bool decode, const Sampler *sampler, - const std::shared_ptr &cache, bool extra_metadata); - - /// \brief Constructor of VOCDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". - /// \param[in] usage The type of data list text file to be read. - /// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - /// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). - VOCDataset(const std::vector &dataset_dir, const std::vector &task, const std::vector &usage, - const std::map, int32_t> &class_indexing, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache, - bool extra_metadata); - - /// \brief Destructor of VOCDataset. - ~VOCDataset() override = default; -}; - -/// \brief Function to create a VOCDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['label', dtype=uint32], -/// ['difficult', dtype=uint32], ['truncate', dtype=uint32]]. -/// - task='Segmentation', column: [['image', dtype=uint8], ['target',dtype=uint8]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". -/// \param[in] usage The type of data list text file to be read (default = "train"). -/// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). -/// \return Shared pointer to the VOCDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/voc_dataset_directory"; -/// std::shared_ptr ds = VOC(folder_path, "Detection", "train", {}, false, -/// std::make_shared(0, 6)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In VOC dataset, if task='Segmentation', each dictionary has keys "image" and "target" */ -/// /* Note: In VOC dataset, if task='Detection', each dictionary has keys "image" and "annotation" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -VOC(const std::string &dataset_dir, const std::string &task = "Segmentation", const std::string &usage = "train", - const std::map &class_indexing = {}, bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr, bool extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - MapStringToChar(class_indexing), decode, sampler, cache, extra_metadata); -} - -/// \brief Function to create a VOCDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['label', dtype=uint32], -/// ['difficult', dtype=uint32], ['truncate', dtype=uint32]]. -/// - task='Segmentation', column: [['image', dtype=uint8], ['target',dtype=uint8]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". -/// \param[in] usage The type of data list text file to be read. -/// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). -/// \return Shared pointer to the VOCDataset. -inline std::shared_ptr DATASET_API VOC(const std::string &dataset_dir, const std::string &task, - const std::string &usage, - const std::map &class_indexing, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr, - bool extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - MapStringToChar(class_indexing), decode, sampler, cache, extra_metadata); -} - -/// \brief Function to create a VOCDataset. -/// \note The generated dataset has multi-columns : -/// - task='Detection', column: [['image', dtype=uint8], ['bbox', dtype=float32], ['label', dtype=uint32], -/// ['difficult', dtype=uint32], ['truncate', dtype=uint32]]. -/// - task='Segmentation', column: [['image', dtype=uint8], ['target',dtype=uint8]]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] task Set the task type of reading voc data, now only support "Segmentation" or "Detection". -/// \param[in] usage The type of data list text file to be read. -/// \param[in] class_indexing A str-to-int mapping from label name to index, only valid in "Detection" task. -/// \param[in] decode Decode the images after reading. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). -/// \param[in] extra_metadata Flag to add extra meta-data to row (default=false). -/// \return Shared pointer to the VOCDataset. -inline std::shared_ptr DATASET_API VOC(const std::string &dataset_dir, const std::string &task, - const std::string &usage, - const std::map &class_indexing, bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr, - bool extra_metadata = false) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(task), StringToChar(usage), - MapStringToChar(class_indexing), decode, sampler, cache, extra_metadata); -} - -/// \class WIDERFaceDataset -/// \brief A source dataset for reading and parsing WIDERFace dataset. -class DATASET_API WIDERFaceDataset : public Dataset { - public: - /// \brief Constructor of WIDERFaceDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all". "all" will read samples - /// from "train" and "valid". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of WIDERFaceDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all". "all" will read samples - /// from "train" and "valid". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const Sampler *sampler, const std::shared_ptr &cache); - - /// \brief Constructor of WIDERFaceDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all". "all" will read samples - /// from "train" and "valid". - /// \param[in] decode Decode the images after reading. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - WIDERFaceDataset(const std::vector &dataset_dir, const std::vector &usage, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of WIDERFaceDataset. - ~WIDERFaceDataset() override = default; -}; - -/// \brief Function to create a WIDERFace Dataset. -/// \note When usage is "train", "valid" or "all", the generated dataset has eight columns ["image", "bbox", "blur", -/// "expression", "illumination", "occlusion", "pose", "invalid"]. When usage is "test", it only has one column -/// ["image"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all" (default="all"). "all" will -/// read samples from "train" and "valid". -/// \param[in] decode The option to decode the images in dataset (default = false). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the WIDERFaceDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/wider_face_dataset_directory"; -/// std::shared_ptr ds = WIDERFace(folder_path, "train", std::make_shared(0, 2)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In WIDERFace dataset, if task='test', each dictionary has key "image" */ -/// /* Note: In WIDERFace dataset, if task='all', 'train' or 'valid', each dictionary has keys "image", "bbox", -/// "blur", "expression", "illumination", "occlusion", "pose", "invalid" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -WIDERFace(const std::string &dataset_dir, const std::string &usage = "all", bool decode = false, - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a WIDERFace Dataset. -/// \note When usage is "train", "valid" or "all", the generated dataset has eight columns ["image", "bbox", "blur", -/// "expression", "illumination", "occlusion", "pose", "invalid"]. When usage is "test", it only has one column -/// ["image"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all". "all" will read samples -/// from "train" and "valid". -/// \param[in] decode The option to decode the images in dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the WIDERFaceDataset. -inline std::shared_ptr DATASET_API WIDERFace(const std::string &dataset_dir, const std::string &usage, - bool decode, const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \brief Function to create a WIDERFace Dataset. -/// \note When usage is "train", "valid" or "all", the generated dataset has eight columns ["image", "bbox", "blur", -/// "expression", "illumination", "occlusion", "pose", "invalid"]. When usage is "test", it only has one column -/// ["image"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage The type of data to be read, can be "train", "test", "valid" or "all". "all" will read samples -/// from "train" and "valid". -/// \param[in] decode The option to decode the images in dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the WIDERFaceDataset. -inline std::shared_ptr DATASET_API WIDERFace(const std::string &dataset_dir, const std::string &usage, - bool decode, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), decode, sampler, cache); -} - -/// \class WikiTextDataset -/// \brief A source dataset for reading and parsing WikiTextDataset dataset. -class DATASET_API WikiTextDataset : public Dataset { - public: - /// \brief Constructor of WikiTextDataset Dataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage The type of data list txt file to be read, can be "train", "test", 'valid' or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - WikiTextDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of WikiTextDataset. - ~WikiTextDataset() override = default; -}; - -/// \brief Function to create a WikiText Dataset. -/// \note The generated dataset has one column ['text']. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage One of "all", "train" , 'valid' or "test" (default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode.kFalse - No shuffling is performed. -/// ShuffleMode.kFiles - Shuffle files only. -/// ShuffleMode.kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the WikiTextDataset. -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/wiki_dataset_directory"; -/// std::shared_ptr ds = WikiText(folder_path, "all"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In WikiText dataset, each dictionary has key "text" */ -/// auto text = row["text"]; -/// \endcode -inline std::shared_ptr DATASET_API WikiText(const std::string &dataset_dir, - const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class YahooAnswersDataset -/// \brief A source dataset for reading and parsing YahooAnswers dataset. -class DATASET_API YahooAnswersDataset : public Dataset { - public: - /// \brief Constructor of YahooAnswersDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of YahooAnswers, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - YahooAnswersDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of YahooAnswers. - ~YahooAnswersDataset() override = default; -}; - -/// \brief Function to create a YahooAnswersDataset. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of YahooAnswers, can be "train", "test" or "all" (default = "all"). -/// \param[in] num_samples The number of samples to be included in the dataset. -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode::kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the YahooAnswersDataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/yahoo_answers_dataset_directory"; -/// std::shared_ptr ds = YahooAnswers(folder_path, "train"); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, each data dictionary owns keys "class", "title", "content", "answer" */ -/// auto title = row["title"]; -/// \endcode -inline std::shared_ptr DATASET_API -YahooAnswers(const std::string &dataset_dir, const std::string &usage = "all", int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class YelpReviewDataset -/// \brief A source dataset for reading and parsing Yelp Review dataset. -class DATASET_API YelpReviewDataset : public Dataset { - public: - /// \brief Constructor of YelpReviewDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of YelpReview, can be "train", "test" or "all". - /// \param[in] num_samples The number of samples to be included in the dataset. - /// \param[in] shuffle The mode for shuffling data every epoch. - /// Can be any of: - /// ShuffleMode.kFalse - No shuffling is performed. - /// ShuffleMode.kFiles - Shuffle files only. - /// ShuffleMode.kGlobal - Shuffle both the files and samples. - /// \param[in] num_shards Number of shards that the dataset should be divided into. - /// \param[in] shard_id The shard ID within num_shards. This argument should be - /// specified only when num_shards is also specified. - /// \param[in] cache Tensor cache to use. - YelpReviewDataset(const std::vector &dataset_dir, const std::vector &usage, int64_t num_samples, - ShuffleMode shuffle, int32_t num_shards, int32_t shard_id, - const std::shared_ptr &cache); - - /// \brief Destructor of YelpReviewDataset. - ~YelpReviewDataset() override = default; -}; - -/// \brief Function to create a YelpReviewDataset. -/// \note This dataset includes polarity and full, which can be read according to your own needs. -/// \note The generated dataset has two columns ["label", "text"]. Their types are all string. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] usage Part of dataset of YelpReview, can be "train", "test" or "all" (default="all"). -/// \param[in] num_samples The number of samples to be included in the dataset -/// (Default = 0, means all samples). -/// \param[in] shuffle The mode for shuffling data every epoch (Default=ShuffleMode.kGlobal). -/// Can be any of: -/// ShuffleMode::kFalse - No shuffling is performed. -/// ShuffleMode::kFiles - Shuffle files only. -/// ShuffleMode::kGlobal - Shuffle both the files and samples. -/// \param[in] num_shards Number of shards that the dataset should be divided into (Default = 1). -/// \param[in] shard_id The shard ID within num_shards. This argument should be -/// specified only when num_shards is also specified (Default = 0). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the YelpReviewDataset. -inline std::shared_ptr DATASET_API YelpReview(const std::string &dataset_dir, - const std::string &usage = "all", - int64_t num_samples = 0, - ShuffleMode shuffle = ShuffleMode::kGlobal, - int32_t num_shards = 1, int32_t shard_id = 0, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), num_samples, shuffle, - num_shards, shard_id, cache); -} - -/// \class YesNoDataset. -/// \brief A source dataset for reading and parsing YesNo dataset. -class DATASET_API YesNoDataset : public Dataset { - public: - /// \brief Constructor of YesNoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset. - /// \param[in] cache Tensor cache to use. - YesNoDataset(const std::vector &dataset_dir, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of YesNoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - YesNoDataset(const std::vector &dataset_dir, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of YesNoDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use. - YesNoDataset(const std::vector &dataset_dir, const std::reference_wrapper &sampler, - const std::shared_ptr &cache); - - /// \brief Destructor of YesNoDataset. - ~YesNoDataset() override = default; -}; - -/// \brief Function to create a YesNo Dataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API -YesNo(const std::string &dataset_dir, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a YesNo Dataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API YesNo(const std::string &dataset_dir, Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a YesNo Dataset. -/// \note The generated dataset has three columns ["waveform", "sample_rate", "label"]. -/// \param[in] dataset_dir Path to the root directory that contains the dataset. -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use (default=nullptr, which means no cache is used). -/// \return Shared pointer to the current Dataset. -inline std::shared_ptr DATASET_API YesNo(const std::string &dataset_dir, - const std::reference_wrapper &sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), sampler, cache); -} - -/// \brief Function to create a cache to be attached to a dataset. -/// \note The reason for providing this API is that std::string will be constrained by the -/// compiler option '_GLIBCXX_USE_CXX11_ABI' while char is free of this restriction. -/// Check API `mindspore::dataset::CreateDatasetCache` and find more usage. -/// \param[in] id A user assigned session id for the current pipeline. -/// \param[in] mem_sz Size of the memory set aside for the row caching (default=0 which means unlimited, -/// note that it might bring in the risk of running out of memory on the machine). -/// \param[in] spill Spill to disk if out of memory. -/// \param[in] hostname optional host name (default=std::nullopt). -/// \param[in] port optional port (default=std::nullopt, means to use 50052). -/// \param[in] num_connections optional number of connections (default=std::nullopt, means to use 12). -/// \param[in] prefetch_sz optional prefetch size (default=std::nullopt, means to use 20). -/// \return Shared pointer to DatasetCache. If error, nullptr is returned. -std::shared_ptr DATASET_API CreateDatasetCacheCharIF( - session_id_type id, uint64_t mem_sz, bool spill, const std::optional> &hostname = std::nullopt, - const std::optional &port = std::nullopt, const std::optional &num_connections = std::nullopt, - const std::optional &prefetch_sz = std::nullopt); - -/// \brief Function the create a cache to be attached to a dataset. -/// \param[in] id A user assigned session id for the current pipeline. -/// \param[in] mem_sz Size of the memory set aside for the row caching (default=0 which means unlimited, -/// note that it might bring in the risk of running out of memory on the machine). -/// \param[in] spill Spill to disk if out of memory. -/// \param[in] hostname optional host name (default=std::nullopt). -/// \param[in] port optional port (default=std::nullopt, means to use 50052). -/// \param[in] num_connections optional number of connections (default=std::nullopt, means to use 12). -/// \param[in] prefetch_sz optional prefetch size (default=std::nullopt, means to use 20). -/// \return Shared pointer to DatasetCache. If error, nullptr is returned. -inline std::shared_ptr DATASET_API CreateDatasetCache( - session_id_type id, uint64_t mem_sz, bool spill, const std::optional &hostname = std::nullopt, - const std::optional &port = std::nullopt, const std::optional &num_connections = std::nullopt, - const std::optional &prefetch_sz = std::nullopt) { - std::optional> hostname_c = std::nullopt; - if (hostname != std::nullopt) { - hostname_c = std::vector(hostname->begin(), hostname->end()); - } - return CreateDatasetCacheCharIF(id, mem_sz, spill, hostname_c, port, num_connections, prefetch_sz); -} - -/// \brief Function to create a ZipDataset. -/// \note Applies zip to the dataset. -/// \param[in] datasets List of shared pointers to the datasets that we want to zip. -/// \return Shared pointer to the ZipDataset. -inline std::shared_ptr DATASET_API Zip(const std::vector> &datasets) { - return std::make_shared(datasets); -} -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATASETS_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/execute.h b/mindspore-lite/minddata/dataset/include/dataset/execute.h deleted file mode 100644 index a21888077..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/execute.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_EXECUTE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_EXECUTE_H_ - -#include -#include -#include -#include - -#include "include/api/context.h" -#include "include/api/visible.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "runtime/hardware_abstract/device_context/device_context.h" -#include "runtime/hardware_abstract/device_context/device_context_manager.h" -#endif - -namespace mindspore { -namespace dataset { -class DeviceResource; -class Tensor; -class TensorOp; - -// class to run tensor operations in eager mode -class DATASET_API Execute { - public: - /// \brief Constructor. - /// \param[in] op TensorOperation to be applied in Eager mode, it accepts operation in type of shared pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::shared_ptr &op, MapTargetDevice device_type = MapTargetDevice::kCpu, - uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] op TensorTransform to be applied in Eager mode, it accepts operation in type of shared pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::shared_ptr &op, MapTargetDevice device_type = MapTargetDevice::kCpu, - uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] op TensorTransform to be applied in Eager mode, it accepts operation in type of reference. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::reference_wrapper &op, - MapTargetDevice device_type = MapTargetDevice::kCpu, uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] op TensorTransform to be applied in Eager mode, it accepts operation in type of raw pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(TensorTransform *op, MapTargetDevice device_type = MapTargetDevice::kCpu, uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] ops A vector of TensorOperations to be applied in Eager mode, it accepts operation - /// in type of shared pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::vector> &ops, - MapTargetDevice device_type = MapTargetDevice::kCpu, uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] ops A vector of TensorTransforms to be applied in Eager mode, it accepts operation - /// in type of shared pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::vector> &ops, - MapTargetDevice device_type = MapTargetDevice::kCpu, uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] ops A vector of TensorTransforms to be applied in Eager mode, it accepts operation - /// in type of raw pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::vector> &ops, - MapTargetDevice device_type = MapTargetDevice::kCpu, uint32_t device_id = 0); - - /// \brief Constructor. - /// \param[in] ops A vector of TensorTransforms to be applied in Eager mode, it accepts operation - /// in type of raw pointer. - /// \param[in] device_type Target device environment to perform operation, can be kCPU/kGPU/kAscend310 (default=kCPU). - /// \param[in] device_id Target device ID to perform operation, only valid when device_type=kAscend310 (default=0). - explicit Execute(const std::vector &ops, MapTargetDevice device_type = MapTargetDevice::kCpu, - uint32_t device_id = 0); - - /// \brief Destructor. - ~Execute(); - - // Update the TensorOperation - Status UpdateOperation(const std::shared_ptr &op); - - /// \brief Callable function to execute the TensorTransform in eager mode. - /// \param[in] input Tensor to be transformed. - /// \param[out] output Transformed tensor. - /// \return Status error code, returns OK if no error encountered. - /// \par Example - /// \code - /// /* Usage of Execute */ - /// std::shared_ptr decode = std::make_shared(); - /// std::shared_ptr center_crop(new vision::CenterCrop({30})); - /// std::shared_ptr rescale = std::make_shared(1. / 3, 0.5); - /// mindspore::dataset::Execute transform = Execute({decode, center_crop, rescale}); - /// - /// /* Apply transforms */ - /// mindspore::MSTensor image = ReadFileToTensor("apple.jpg"); - /// Status rc = transform(image, &image); - /// \endcode - Status operator()(const mindspore::MSTensor &input, mindspore::MSTensor *output); - - /// \brief Callable function to execute the TensorTransform in eager mode. - /// \param[in] input_tensor_list List of Tensor to be transformed. - /// \param[out] out Result tensor after transform. - /// \return Status error code, returns OK if no error encountered. - /// \par Example - /// \code - /// /* Usage of Execute */ - /// auto tokenizer = text::BasicTokenizer(); - /// mindspore::dataset::Execute transform = Execute({tokenizer}); - /// - /// /* Apply transforms */ - /// std::vector txt = ReadTextToTensor("demo.txt"); - /// std::vector txt_result; - /// Status rc = transform1({txt}, &txt_result); - /// \endcode - Status operator()(const std::vector &input_tensor_list, std::vector *out); - - /// \brief Given a set of Executes, run them - static Status Run(const std::vector> &data_graph, - const std::vector &inputs, std::vector *outputs); - - /// \brief The function to release device memory on Ascend310. - Status DeviceMemoryRelease(); - - /// \brief The function to generate AIPP configuration. - std::string AippCfgGenerator(); - - protected: - /// \brief The function to convert TensorTransforms into TensorOperations and then build TensorOps. - Status BuildTransforms(std::vector> *transforms_rt); - - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - Status ParseTransforms(); - - /// \brief The function to validate target device setting is valid or not. - Status ValidateDevice(); - - /// \brief Initialize 310 resource - Status InitResource(MapTargetDevice device_type, uint32_t device_id = 0); - - std::vector> transforms_; - std::vector> ops_; - MapTargetDevice device_type_; - - // Ascend310 - std::shared_ptr device_resource_ = nullptr; - struct ExtraInfo; - std::shared_ptr info_; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Ascend910B - device::DeviceContext *device_context_ = nullptr; - size_t stream_id_; -#endif -}; - -class PyExecute : public Execute { - public: - // inherit base class constructors - using Execute::Execute; - - /// \brief Callable function to execute the TensorTransform in eager mode (only cpu). - /// \param[in] input_tensor_list List of Tensors to be transformed. - /// \param[out] out Result tensors list after transform. - /// \return Status error code, returns OK if no error encountered. - Status operator()(const std::vector> &input_tensor_list, - std::vector> *out); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_EXECUTE_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/iterator.h b/mindspore-lite/minddata/dataset/include/dataset/iterator.h deleted file mode 100644 index 3dce9d911..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/iterator.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_ITERATOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_ITERATOR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" -#include "include/api/types.h" - -namespace mindspore { -namespace dataset { -// Forward declare -class ExecutionTree; -class DatasetOp; -class Tensor; - -class NativeRuntimeContext; -class IteratorConsumer; -class PullBasedIteratorConsumer; - -class Dataset; - -using MSTensorMap = std::unordered_map; -using MSTensorMapChar = std::map, mindspore::MSTensor>; -using MSTensorVec = std::vector; - -// Abstract class for iterating over the dataset. -class DATASET_API Iterator { - public: - /// \brief Constructor. - Iterator(); - - /// \brief Destructor. - virtual ~Iterator(); - - /// \brief Method for building and launching the pipeline. - /// \param[in] ds The last DatasetOp in the dataset pipeline. - /// \param[in] num_epochs Number of epochs passed down to EpochCtrlNode (default=-1, which means infinite epochs). - /// \return Status error code, returns OK if no error encountered. - virtual Status BuildAndLaunchTree(const std::shared_ptr &ds, int32_t num_epochs); - - /// \brief Function to get the next row from the data pipeline. - /// \note Type of return data is a unordered_map(with column name). - /// \param[out] row The output tensor row. - /// \return Status error code, returns OK if no error encountered. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreateIterator(); - /// std::unordered_map row; - /// iter->GetNextRow(&row); - /// \endcode - Status GetNextRow(MSTensorMap *row) { - if (row == nullptr) { - return Status(kMDUnexpectedError, "Got nullptr when GetNext row."); - } - MSTensorMapChar row_; - row_.clear(); - row->clear(); - Status s = GetNextRowCharIF(&row_); - TensorMapCharToString(&row_, row); - return s; - } - - /// \brief Char interface(CharIF) of GetNextRow. - /// \note The reason for using this API is that std::string will be constrained by the - /// compiler option '_GLIBCXX_USE_CXX11_ABI' while char is free of this restriction. - Status GetNextRowCharIF(MSTensorMapChar *row); - - /// \brief Function to get the next row from the data pipeline. - /// \note Type of return data is a vector(without column name). - /// \param[out] row The output tensor row. - /// \return Status error code, returns OK if no error encountered. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreateIterator(); - /// std::vector row; - /// iter->GetNextRow(&row); - /// \endcode - virtual Status GetNextRow(MSTensorVec *row); - - /// \brief Function to shut down the data pipeline. - void Stop(); - - /// \brief Inter class as iterator of Iterator. - class _Iterator { - public: - /// \brief Constructor - explicit _Iterator(Iterator *lt); - - /// \brief Destructor - ~_Iterator() { - if (cur_row_ != nullptr) { - delete cur_row_; - cur_row_ = nullptr; - } - } - - /// \brief prefix ++ overload - _Iterator &operator++(); - - /// \brief dereference operator - MSTensorMap &operator*() { return *cur_row_; } - - /// \brief dereference operator - MSTensorMap *operator->() { return cur_row_; } - - /// \brief bool operator - bool operator!=(const _Iterator &rhs) { return cur_row_ != rhs.cur_row_; } - - private: - int ind_; // the cur node our Iterator points to - Iterator *lt_; - MSTensorMap *cur_row_; - }; - - /// \brief Function to return the iterator points to the begin of Iterator. - _Iterator begin() { return _Iterator(this); } - - /// \brief Function to return the iterator points to the end of Iterator. - _Iterator end() { return _Iterator(nullptr); } - - private: - std::unique_ptr runtime_context_; - IteratorConsumer *consumer_; -}; - -class DATASET_API PullIterator : public Iterator { - public: - /// \brief Constructor. - PullIterator(); - - /// \brief Destructor. - ~PullIterator() override; - - /// \brief Function to get next row from the data pipeline. - /// \note Type of return data is a vector(without column name). - /// \param[out] row The output tensor row. - /// \return Status error code, returns OK if no error encountered else false. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreatePullBasedIterator(); - /// std::vector row; - /// iter->GetNextRow(&row); - /// \endcode - Status GetNextRow(MSTensorVec *const row) override; - - /// \brief Function to get specified rows from the data pipeline. - /// \note Type of return data is a vector(without column name). This behavior is subject to change. - /// \param[in] num_rows The number of rows to fetch. - /// \param[out] row The output tensor row. - /// \return Status error code, returns OK if no error encountered else false. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreatePullBasedIterator(); - /// std::vector> rows; - /// iter->GetNextRow(5, &rows); - /// \endcode - Status GetRows(int32_t num_rows, std::vector *const row); - - /// \brief Method for building and launching the pipeline. - /// \note Consider making this function protected. - /// \param[in] ds The root node that calls the function. - /// \param[in] num_epochs Number of epochs passed down to EpochCtrlNode (default=-1, which means infinite epochs). - /// \return Status error code, returns OK if no error encountered. - Status BuildAndLaunchTree(const std::shared_ptr &ds, int32_t num_epochs) override; - - private: - std::unique_ptr pull_consumer_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_ITERATOR_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/samplers.h b/mindspore-lite/minddata/dataset/include/dataset/samplers.h deleted file mode 100644 index d0061f97e..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/samplers.h +++ /dev/null @@ -1,346 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_SAMPLERS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_SAMPLERS_H_ - -#include -#include - -#include "include/api/visible.h" -#include "include/api/status.h" -#include "include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -// Forward declare -class SamplerObj; - -// Abstract class to represent a sampler in the data pipeline. -/// \class Sampler samplers.h -/// \brief An abstract base class to represent a sampler in the data pipeline. -class DATASET_API Sampler : std::enable_shared_from_this { - friend class AlbumDataset; - friend class Caltech256Dataset; - friend class CelebADataset; - friend class Cifar10Dataset; - friend class Cifar100Dataset; - friend class CityscapesDataset; - friend class CLUEDataset; - friend class CMUArcticDataset; - friend class CocoDataset; - friend class CSVDataset; - friend class DIV2KDataset; - friend class EMnistDataset; - friend class FakeImageDataset; - friend class FashionMnistDataset; - friend class FlickrDataset; - friend class Food101Dataset; - friend class GTZANDataset; - friend class ImageFolderDataset; - friend class IMDBDataset; - friend class KITTIDataset; - friend class KMnistDataset; - friend class LFWDataset; - friend class LibriTTSDataset; - friend class LJSpeechDataset; - friend class LSUNDataset; - friend class ManifestDataset; - friend class MindDataDataset; - friend class MnistDataset; - friend class OmniglotDataset; - friend class PhotoTourDataset; - friend class Places365Dataset; - friend class QMnistDataset; - friend class RandomDataDataset; - friend class RenderedSST2Dataset; - friend class SBUDataset; - friend class SemeionDataset; - friend class SpeechCommandsDataset; - friend class SST2Dataset; - friend class STL10Dataset; - friend class SUN397Dataset; - friend class TedliumDataset; - friend class TextFileDataset; - friend class TFRecordDataset; - friend class USPSDataset; - friend class VOCDataset; - friend class WIDERFaceDataset; - friend class YesNoDataset; - - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - Sampler() = default; - - /// \brief Destructor - virtual ~Sampler() = default; - - /// \brief A virtual function to add a child sampler. - /// \param[in] child The child sampler to be added as a children of this sampler. - virtual void AddChild(const std::shared_ptr &child) { children_.push_back(child); } - - protected: - /// \brief Pure virtual function to convert a Sampler class into an IR Sampler object. - /// \return shared pointer to the newly created TensorOperation. - virtual std::shared_ptr Parse() const = 0; - - /// \brief A function that calls Parse() on the children of this sampler - /// \param[in] sampler The samplerIR object built from this sampler - /// \return the Status code returned - Status BuildChildren(std::shared_ptr *const sampler) const; - - std::vector> children_; -}; - -/// \brief A class to represent a Distributed Sampler in the data pipeline. -/// \note A Sampler that accesses a shard of the dataset. -class DATASET_API DistributedSampler final : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] num_shards Number of shards to divide the dataset into. - /// \param[in] shard_id Shard ID of the current shard within num_shards. - /// \param[in] shuffle_mode If not kFalse, the indices are shuffled (default=kGlobal). - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \param[in] seed The seed in use when shuffle_mode is not kFalse (default=1). - /// \param[in] offset The starting position where access to elements in the dataset begins (default=-1). - /// \param[in] even_dist If true, each shard would return the same number of rows (default=true). - /// If false the total rows returned by all the shards would not have overlap. - /// \par Example - /// \code - /// /* creates a distributed sampler with 2 shards in total. This shard is shard 0 */ - /// std::string file_path = "/path/to/test.mindrecord"; - /// std::shared_ptr ds = MindData(file_path, {}, std::make_shared(2, 0, false)); - /// \endcode - DistributedSampler(int64_t num_shards, int64_t shard_id, - dataset::ShuffleMode shuffle_mode = dataset::ShuffleMode::kGlobal, int64_t num_samples = 0, - uint32_t seed = 1, int64_t offset = -1, bool even_dist = true); - /// \brief Destructor. - ~DistributedSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - private: - int64_t num_shards_; - int64_t shard_id_; - dataset::ShuffleMode shuffle_mode_; - int64_t num_samples_; - uint32_t seed_; - int64_t offset_; - bool even_dist_; -}; - -/// \brief A class to represent a PK Sampler in the data pipeline. -/// \note Samples K elements for each P class in the dataset. -/// This will sample all classes. -class DATASET_API PKSampler final : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] num_val Number of elements to sample for each class. - /// \param[in] shuffle If true, the class IDs are shuffled (default=false). - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \par Example - /// \code - /// /* creates a PKSampler that will get 3 samples from every class. */ - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, true, std::make_shared(3)); - /// \endcode - explicit PKSampler(int64_t num_val, bool shuffle = false, int64_t num_samples = 0); - - /// \brief Destructor. - ~PKSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - private: - int64_t num_val_; - bool shuffle_; - int64_t num_samples_; -}; - -/// \brief A class to represent a Random Sampler in the data pipeline. -/// \note Samples the elements randomly. -class DATASET_API RandomSampler final : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] replacement If true, put the sample ID back for the next draw (default=false). - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \par Example - /// \code - /// /* creates a RandomSampler that will get 10 samples randomly */ - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, true, std::make_shared(false, 10)); - /// \endcode - explicit RandomSampler(bool replacement = false, int64_t num_samples = 0, - dataset::ShuffleMode shuffle_mode = dataset::ShuffleMode::kGlobal); - - /// \brief Destructor. - ~RandomSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - private: - bool replacement_; - int64_t num_samples_; - dataset::ShuffleMode shuffle_mode_; -}; - -/// \brief A class to represent a Sequential Sampler in the data pipeline. -/// \note Samples the dataset elements sequentially, same as not having a sampler. -class DATASET_API SequentialSampler final : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] start_index Index to start sampling at (default=0, start at first id). - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \par Example - /// \code - /// /* creates a SequentialSampler that will get 2 samples sequentially in original dataset */ - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, false, std::make_shared(0, 2)); - /// \endcode - explicit SequentialSampler(int64_t start_index = 0, int64_t num_samples = 0); - - /// \brief Destructor. - ~SequentialSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - private: - int64_t start_index_; - int64_t num_samples_; -}; - -/// \brief A class to represent a Subset Sampler in the data pipeline. -/// \note Samples the elements from a sequence of indices. -class DATASET_API SubsetSampler : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] indices A vector sequence of indices. - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \par Example - /// \code - /// /* creates a SubsetSampler, will sample from the provided indices */ - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, false, std::make_shared({0, 2, 5})); - /// \endcode - explicit SubsetSampler(const std::vector &indices, int64_t num_samples = 0); - - /// \brief Destructor. - ~SubsetSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - std::vector indices_; - int64_t num_samples_; -}; - -/// \brief A class to represent a Subset Random Sampler in the data pipeline. -/// \note Samples the elements randomly from a sequence of indices. -class DATASET_API SubsetRandomSampler final : public SubsetSampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] indices A vector sequence of indices. - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \par Example - /// \code - /// /* create a SubsetRandomSampler, will sample from the provided indices */ - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, false, std::make_shared({2, 7})); - /// \endcode - explicit SubsetRandomSampler(const std::vector &indices, int64_t num_samples = 0); - - /// \brief Destructor. - ~SubsetRandomSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; -}; - -/// \brief A class to represent a Weighted Random Sampler in the data pipeline. -/// \note Samples the elements from [0, len(weights) - 1] randomly with the given -/// weights (probabilities). -class DATASET_API WeightedRandomSampler final : public Sampler { - friend std::shared_ptr SelectSampler(int64_t num_samples, dataset::ShuffleMode shuffle_mode, - int32_t num_shards, int32_t shard_id); - - public: - /// \brief Constructor - /// \param[in] weights A vector sequence of weights, not necessarily summing up to 1. - /// \param[in] num_samples The number of samples to draw (default=0, return all samples). - /// \param[in] replacement If true, put the sample ID back for the next draw (default=true). - /// \par Example - /// \code - /// /* creates a WeightedRandomSampler that will sample 4 elements without replacement */ - /// std::vector weights = {0.9, 0.8, 0.68, 0.7, 0.71, 0.6, 0.5, 0.4, 0.3, 0.5, 0.2, 0.1}; - /// sampler = std::make_shared(weights, 4); - /// std::string folder_path = "/path/to/image/folder"; - /// std::shared_ptr ds = ImageFolder(folder_path, false, sampler); - /// \endcode - explicit WeightedRandomSampler(const std::vector &weights, int64_t num_samples = 0, bool replacement = true); - - /// \brief Destructor. - ~WeightedRandomSampler() override = default; - - protected: - /// \brief The function to convert a Sampler into an IR SamplerObj. - /// \return shared pointer to the newly created SamplerObj. - std::shared_ptr Parse() const override; - - private: - std::vector weights_; - int64_t num_samples_; - bool replacement_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_SAMPLERS_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/text.h b/mindspore-lite/minddata/dataset/include/dataset/text.h deleted file mode 100644 index 244770b0a..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/text.h +++ /dev/null @@ -1,1092 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TEXT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TEXT_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" - -namespace mindspore { -namespace dataset { -class TensorOperation; -class Vectors; - -using WordIdType = int32_t; -using WordType = std::string; - -/// \brief Vocab object that is used to save pairs of words and ids. -/// \note It contains a map that maps each word(str) to an id(int) or reverse. -class Vocab { - public: - /// \brief Build a vocab from an unordered_map. IDs should be no duplicate and continuous. - /// \param[in] words An unordered_map containing word id pair. - /// \param[out] vocab A vocab object. - /// \return Status code. - /// \par Example - /// \code - /// // Build a map - /// std::unordered_map dict; - /// dict["banana"] = 0; - /// dict["apple"] = 1; - /// dict["cat"] = 2; - /// dict["dog"] = 3; - /// // Build vocab from map - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromUnorderedMap(dict, &vocab); - /// \endcode - static Status BuildFromUnorderedMap(const std::unordered_map &words, - std::shared_ptr *vocab); - - /// \brief Build a vocab from a c++ vector. id no duplicate and continuous. - /// \param[in] words A vector of string containing words. - /// \param[in] special_tokens A vector of string containing special tokens. - /// \param[in] prepend_special Whether the special_tokens will be prepended/appended to vocab. - /// \param[out] vocab A vocab object. - /// \return Status code. - /// \par Example - /// \code - /// // Build vocab from a vector of words, special tokens are prepended to vocab - /// std::vector list = {"apple", "banana", "cat", "dog", "egg"}; - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromVector(list, {""}, true, &vocab); - /// \endcode - static Status BuildFromVector(const std::vector &words, const std::vector &special_tokens, - bool prepend_special, std::shared_ptr *vocab); - - /// \brief Build a vocab from vocab file, IDs will be automatically assigned. - /// \param[in] path Path to vocab file, each line in file is assumed as a word (including space). - /// \param[in] delimiter Delimiter to break each line, characters after the delimiter will be deprecated. - /// \param[in] vocab_size Number of lines to be read from file. - /// \param[in] special_tokens A vector of string containing special tokens. - /// \param[in] prepend_special Whether the special_tokens will be prepended/appended to vocab. - /// \param[out] vocab A vocab object. - /// \return Status code. - /// \par Example - /// \code - /// // Build vocab from local file - /// std::string vocab_dir = datasets_root_path_ + "/testVocab/vocab_list.txt"; - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromFile(vocab_dir, ",", -1, {"", ""}, true, &vocab); - /// \endcode - static Status BuildFromFile(const std::string &path, const std::string &delimiter, int32_t vocab_size, - const std::vector &special_tokens, bool prepend_special, - std::shared_ptr *vocab); - - /// Lookup the id of a word, if the word doesn't exist in vocab, return -1. - /// \param word Word to be looked up. - /// \return ID of the word in the vocab. - /// \par Example - /// \code - /// // lookup, convert token to id - /// auto single_index = vocab->TokensToIds("home"); - /// single_index = vocab->TokensToIds("hello"); - /// \endcode - WordIdType TokensToIds(const WordType &word) const; - - /// Lookup the id of a word, if the word doesn't exist in vocab, return -1. - /// \param words Words to be looked up. - /// \return ID of the word in the vocab. - /// \par Example - /// \code - /// // lookup multiple tokens - /// auto multi_indexs = vocab->TokensToIds(std::vector{"", "behind"}); - /// std::vector expected_multi_indexs = {0, 4}; - /// multi_indexs = vocab->TokensToIds(std::vector{"", "apple"}); - /// expected_multi_indexs = {0, -1}; - /// \endcode - std::vector TokensToIds(const std::vector &words) const; - - /// Lookup the word of an ID, if ID doesn't exist in vocab, return empty string. - /// \param id ID to be looked up. - /// \return Indicates the word corresponding to the ID. - /// \par Example - /// \code - /// // reverse lookup, convert id to token - /// auto single_word = vocab->IdsToTokens(2); - /// single_word = vocab->IdsToTokens(-1); - /// \endcode - WordType IdsToTokens(const WordIdType &id); - - /// Lookup the word of an ID, if ID doesn't exist in vocab, return empty string. - /// \param ids ID to be looked up. - /// \return Indicates the word corresponding to the ID. - /// \par Example - /// \code - /// // reverse lookup multiple ids - /// auto multi_words = vocab->IdsToTokens(std::vector{0, 4}); - /// std::vector expected_multi_words = {"", "behind"}; - /// multi_words = vocab->IdsToTokens(std::vector{0, 99}); - /// expected_multi_words = {"", ""}; - /// \endcode - std::vector IdsToTokens(const std::vector &ids); - - /// Constructor, shouldn't be called directly, can't be private due to std::make_unique(). - /// \param map Sanitized word2id map. - explicit Vocab(std::unordered_map map); - - /// \brief Add one word to vocab, increment it's index automatically. - /// \param word Word to be added, word will skip if word already exists. - void AppendWord(const std::string &word); - - /// \brief Return a read-only vocab in unordered_map type. - /// \return A unordered_map of word2id. - const std::unordered_map &GetVocab() const { return word2id_; } - - /// \brief Constructor. - Vocab() = default; - - /// \brief Destructor. - ~Vocab() = default; - - static const WordIdType kNoTokenExists; - static const WordType kNoIdExists; - - private: - std::unordered_map word2id_; - std::unordered_map id2word_; -}; - -/// \brief SentencePiece object that is used to do words segmentation. -class SentencePieceVocab { - public: - /// \brief Build a SentencePiece object from a file. - /// \param[in] path_list Path to the file which contains the SentencePiece list. - /// \param[in] vocab_size Vocabulary size. - /// \param[in] character_coverage Amount of characters covered by the model, good defaults are: 0.9995 for - /// languages with rich character set like Japanese or Chinese and 1.0 for other languages with small - /// character set. - /// \param[in] model_type It can be any of [SentencePieceModel::kUnigram, SentencePieceModel::kBpe, - /// SentencePieceModel::kChar, SentencePieceModel::kWord], default is SentencePieceModel::kUnigram. The - /// input sentence must be pre-tokenized when using SentencePieceModel.WORD type. - /// - SentencePieceModel.kUnigram, Unigram Language Model means the next word in the sentence is assumed - /// to be independent of the previous words generated by the model. - /// - SentencePieceModel.kBpe, refers to byte pair encoding algorithm, which replaces the most frequent - /// pair of bytes in a sentence with a single, unused byte. - /// - SentencePieceModel.kChar, refers to char based sentencePiece Model type. - /// - SentencePieceModel.kWord, refers to word based sentencePiece Model type. - /// \param[in] params A dictionary with no incoming parameters(The parameters are derived from SentencePiece library). - /// \param[out] vocab A SentencePieceVocab object. - /// \return SentencePieceVocab, vocab built from the file. - /// \par Example - /// \code - /// std::string dataset_path; - /// dataset_path = datasets_root_path_ + "/test_sentencepiece/vocab.txt"; - /// std::vector path_list; - /// path_list.emplace_back(dataset_path); - /// std::unordered_map param_map; - /// std::shared_ptr spm = std::make_unique(); - /// Status rc = SentencePieceVocab::BuildFromFile(path_list, 5000, 0.9995, - /// SentencePieceModel::kUnigram, param_map, &spm); - /// \endcode - static Status BuildFromFile(const std::vector &path_list, int32_t vocab_size, float character_coverage, - const SentencePieceModel &model_type, - const std::unordered_map ¶ms, - std::shared_ptr *vocab); - - /// \brief Save the SentencePiece model into given file path. - /// \param[in] vocab A SentencePiece object to be saved. - /// \param[in] path Path to store the model. - /// \param[in] filename The save name of model file. - /// \par Example - /// \code - /// // Save vocab model to local - /// vocab->SaveModel(&vocab, datasets_root_path_ + "/test_sentencepiece", "m.model"); - /// \endcode - static Status SaveModel(const std::shared_ptr *vocab, const std::string &path, - const std::string &filename); - - /// \brief Constructor. - SentencePieceVocab(); - - /// \brief Destructor. - ~SentencePieceVocab() = default; - - const std::string &model_proto(); - - void set_model_proto(const std::string &model_proto); - - private: - std::string model_proto_; -}; - -// Transform operations for text -namespace text { -/// \brief Add token to beginning or end of sequence. -class DATASET_API AddToken final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] token The token to be added. - /// \param[in] begin Whether to insert token at start or end of sequence. Default: true. - /// \par Example - /// \code - /// /* Define operations */ - /// auto add_token_op = text::AddToken(token='TOKEN', begin=True); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({add_token_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit AddToken(const std::string &token, bool begin = true); - - /// \brief Destructor. - ~AddToken() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -#ifndef _WIN32 -/// \brief Tokenize a scalar tensor of UTF-8 string by specific rules. -/// \note BasicTokenizer is not supported on the Windows platform yet. -class DATASET_API BasicTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] lower_case If true, apply CaseFold, NormalizeUTF8 (NFD mode) and RegexReplace operations to - /// the input text to fold the text to lower case and strip accents characters. If false, only apply - /// the NormalizeUTF8('normalization_form' mode) operation to the input text (default=false). - /// \param[in] keep_whitespace If true, the whitespace will be kept in output tokens (default=false). - /// \param[in] normalize_form This parameter is used to specify a specific normalize mode. This is only effective - /// when 'lower_case' is false. See NormalizeUTF8 for details (default=NormalizeForm::kNone). - /// \param[in] preserve_unused_token If true, do not split special tokens like '[CLS]', '[SEP]', '[UNK]', '[PAD]' and - /// '[MASK]' (default=true). - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::BasicTokenizer(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit BasicTokenizer(bool lower_case = false, bool keep_whitespace = false, - NormalizeForm normalize_form = NormalizeForm::kNone, bool preserve_unused_token = true, - bool with_offsets = false); - - /// \brief Destructor - ~BasicTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief A tokenizer used for Bert text process. -/// \note BertTokenizer is not supported on the Windows platform yet. -class DATASET_API BertTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] vocab A Vocab object. - /// \param[in] suffix_indicator This parameter is used to show that the sub-word - /// is the last part of a word (default='##'). - /// \param[in] max_bytes_per_token Tokens exceeding this length will not be further split (default=100). - /// \param[in] unknown_token When a token cannot be found, return the token directly if 'unknown_token' is an empty - /// string, else return the specified string (default='[UNK]'). - /// \param[in] lower_case If true, apply CaseFold, NormalizeUTF8 (NFD mode) and RegexReplace operations to - /// the input text to fold the text to lower case and strip accents characters. If false, only apply - /// the NormalizeUTF8('normalization_form' mode) operation to the input text (default=false). - /// \param[in] keep_whitespace If true, the whitespace will be kept in output tokens (default=false). - /// \param[in] normalize_form This parameter is used to specify a specific normalize mode. This is only effective - /// when 'lower_case' is false. See NormalizeUTF8 for details (default=NormalizeForm::kNone). - /// \param[in] preserve_unused_token If true, do not split special tokens like '[CLS]', '[SEP]', '[UNK]', '[PAD]' and - /// '[MASK]' (default=true). - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// std::vector list = {"a", "b", "c", "d"}; - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromVector(list, {}, true, &vocab); - /// auto tokenizer_op = text::BertTokenizer(vocab); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit BertTokenizer(const std::shared_ptr &vocab, const std::string &suffix_indicator = "##", - int32_t max_bytes_per_token = 100, const std::string &unknown_token = "[UNK]", - bool lower_case = false, bool keep_whitespace = false, - const NormalizeForm normalize_form = NormalizeForm::kNone, bool preserve_unused_token = true, - bool with_offsets = false) - : BertTokenizer(vocab, StringToChar(suffix_indicator), max_bytes_per_token, StringToChar(unknown_token), - lower_case, keep_whitespace, normalize_form, preserve_unused_token, with_offsets) {} - /// \brief Constructor. - /// \param[in] vocab A Vocab object. - /// \param[in] suffix_indicator This parameter is used to show that the sub-word - /// is the last part of a word (default='##'). - /// \param[in] max_bytes_per_token Tokens exceeding this length will not be further split (default=100). - /// \param[in] unknown_token When a token cannot be found, return the token directly if 'unknown_token' is an empty - /// string, else return the specified string (default='[UNK]'). - /// \param[in] lower_case If true, apply CaseFold, NormalizeUTF8 (NFD mode) and RegexReplace operations to - /// the input text to fold the text to lower case and strip accents characters. If false, only apply - /// the NormalizeUTF8('normalization_form' mode) operation to the input text (default=false). - /// \param[in] keep_whitespace If true, the whitespace will be kept in output tokens (default=false). - /// \param[in] normalize_form This parameter is used to specify a specific normalize mode. This is only effective - /// when 'lower_case' is false. See NormalizeUTF8 for details (default=NormalizeForm::kNone). - /// \param[in] preserve_unused_token If true, do not split special tokens like '[CLS]', '[SEP]', '[UNK]', '[PAD]' and - /// '[MASK]' (default=true). - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - BertTokenizer(const std::shared_ptr &vocab, const std::vector &suffix_indicator, - int32_t max_bytes_per_token, const std::vector &unknown_token, bool lower_case, - bool keep_whitespace, NormalizeForm normalize_form, bool preserve_unused_token, bool with_offsets); - - /// \brief Destructor - ~BertTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply case fold operation on UTF-8 string tensors. -class DATASET_API CaseFold final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto casefold_op = text::CaseFold(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({casefold_op}, // operations - /// {"text"}); // input columns - /// \endcode - CaseFold(); - - /// \brief Destructor - ~CaseFold() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Filter wikipedia xml lines. -class FilterWikipediaXML final : public TensorTransform { - public: - /// \brief Constructor. - FilterWikipediaXML(); - - /// \brief Destructor - ~FilterWikipediaXML() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; -}; -#endif - -/// \brief Tokenize a Chinese string into words based on the dictionary. -/// \note The integrity of the HMMSegment algorithm and MPSegment algorithm files must be confirmed. -class DATASET_API JiebaTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] hmm_path Dictionary file is used by the HMMSegment algorithm. The dictionary can be obtained on the - /// official website of cppjieba (https://github.com/yanyiwu/cppjieba). - /// \param[in] mp_path Dictionary file is used by the MPSegment algorithm. The dictionary can be obtained on the - /// official website of cppjieba (https://github.com/yanyiwu/cppjieba). - /// \param[in] mode Valid values can be any of JiebaMode.kMP, JiebaMode.kHMM and JiebaMode.kMIX - /// (default=JiebaMode.kMIX). - /// - JiebaMode.kMP, tokenizes with MPSegment algorithm. - /// - JiebaMode.kHMM, tokenizes with Hidden Markov Model Segment algorithm. - /// - JiebaMode.kMIX, tokenizes with a mix of MPSegment and HMMSegment algorithms. - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::JiebaTokenizer("/path/to/hmm/file", "/path/to/mp/file"); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - JiebaTokenizer(const std::string &hmm_path, const std::string &mp_path, const JiebaMode &mode = JiebaMode::kMix, - bool with_offsets = false) - : JiebaTokenizer(StringToChar(hmm_path), StringToChar(mp_path), mode, with_offsets) {} - - /// \brief Constructor. - /// \param[in] hmm_path Dictionary file is used by the HMMSegment algorithm. The dictionary can be obtained on the - /// official website of cppjieba (https://github.com/yanyiwu/cppjieba). - /// \param[in] mp_path Dictionary file is used by the MPSegment algorithm. The dictionary can be obtained on the - /// official website of cppjieba (https://github.com/yanyiwu/cppjieba). - /// \param[in] mode Valid values can be any of JiebaMode.kMP, JiebaMode.kHMM and JiebaMode.kMIX - /// (default=JiebaMode.kMIX). - /// - JiebaMode.kMP, tokenizes with MPSegment algorithm. - /// - JiebaMode.kHMM, tokenizes with Hidden Markov Model Segment algorithm. - /// - JiebaMode.kMIX, tokenizes with a mix of MPSegment and HMMSegment algorithms. - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - JiebaTokenizer(const std::vector &hmm_path, const std::vector &mp_path, const JiebaMode &mode, - bool with_offsets); - - /// \brief Destructor - ~JiebaTokenizer() override = default; - - /// \brief Add a user defined word to the JiebaTokenizer's dictionary. - /// \param[in] word The word to be added to the JiebaTokenizer instance. - /// The added word will not be written into the built-in dictionary on disk. - /// \param[in] freq The frequency of the word to be added. The higher the frequency, - /// the better chance the word will be tokenized (default=None, use default frequency). - /// \return Status error code, returns OK if no error is encountered. - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::JiebaTokenizer("/path/to/hmm/file", "/path/to/mp/file"); - /// - /// Status s = tokenizer_op.AddWord("hello", 2); - /// \endcode - Status AddWord(const std::string &word, int64_t freq = 0) { return AddWordChar(StringToChar(word), freq); } - - /// \brief Add a user defined dictionary of word-freq pairs to the JiebaTokenizer's dictionary. - /// \param[in] user_dict Vector of word-freq pairs to be added to the JiebaTokenizer's dictionary. - /// \return Status error code, returns OK if no error is encountered. - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::JiebaTokenizer("/path/to/hmm/file", "/path/to/mp/file"); - /// - /// std::vector> user_dict = {{"a", 1}, {"b", 2}, {"c", 3}}; - /// Status s = tokenizer_op.AddDict(user_dict); - /// \endcode - Status AddDict(const std::vector> &user_dict) { - return AddDictChar(PairStringInt64ToPairCharInt64(user_dict)); - } - - /// \brief Add user defined dictionary of word-freq pairs to the JiebaTokenizer's dictionary from a file. - /// Only valid word-freq pairs in user defined file will be added into the dictionary. - /// Rows containing invalid inputs will be ignored, no error nor warning status is returned. - /// \param[in] file_path Path to the dictionary which includes user defined word-freq pairs. - /// \return Status error code, returns OK if no error is encountered. - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::JiebaTokenizer("/path/to/hmm/file", "/path/to/mp/file"); - /// - /// Status s = tokenizer_op.AddDict("/path/to/dict/file"); - /// \endcode - Status AddDict(const std::string &file_path) { return AddDictChar(StringToChar(file_path)); } - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - /// \brief Parser user defined words by files. - /// \param[in] file_path Path to the user defined file. - /// \param[in] user_dict Vector of word-freq pairs extracted from the user defined file. - Status ParserFile(const std::string &file_path, std::vector> *const user_dict); - - /// \brief Used to translate all API strings to vector of char and reverse. - Status AddWordChar(const std::vector &word, int64_t freq = 0); - - /// \brief Used to translate all API strings to vector of char and reverse. - Status AddDictChar(const std::vector, int64_t>> &user_dict); - - /// \brief Used to translate all API strings to vector of char and reverse. - Status AddDictChar(const std::vector &file_path); - - struct Data; - std::shared_ptr data_; -}; - -/// \brief Look up a word into an id according to the input vocabulary table. -class DATASET_API Lookup final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] vocab a Vocab object. - /// \param[in] unknown_token Word is used for lookup. In case of the word is out of vocabulary (OOV), - /// the result of lookup will be replaced to unknown_token. If the unknown_token is not specified or it is OOV, - /// runtime error will be thrown (default={}, means no unknown_token is specified). - /// \param[in] data_type mindspore::DataType of the tensor after lookup; must be numeric, including bool. - /// (default=mindspore::DataType::kNumberTypeInt32). - /// \par Example - /// \code - /// /* Define operations */ - /// std::vector list = {"a", "b", "c", "d"}; - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromVector(list, {}, true, &vocab); - /// auto lookup_op = text::Lookup(vocab, "[unk]"); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({lookup_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit Lookup(const std::shared_ptr &vocab, const std::optional &unknown_token = {}, - mindspore::DataType data_type = mindspore::DataType::kNumberTypeInt32) { - std::optional> unknown_token_c = std::nullopt; - if (unknown_token != std::nullopt) { - unknown_token_c = std::vector(unknown_token->begin(), unknown_token->end()); - } - new (this) Lookup(vocab, unknown_token_c, data_type); - } - - /// \brief Constructor. - /// \param[in] vocab a Vocab object. - /// \param[in] unknown_token Word is used for lookup. In case of the word is out of vocabulary (OOV), - /// the result of lookup will be replaced to unknown_token. If the unknown_token is not specified or it is OOV, - /// runtime error will be thrown (default={}, means no unknown_token is specified). - /// \param[in] data_type mindspore::DataType of the tensor after lookup; must be numeric, including bool. - /// (default=mindspore::DataType::kNumberTypeInt32). - Lookup(const std::shared_ptr &vocab, const std::optional> &unknown_token, - mindspore::DataType data_type = mindspore::DataType::kNumberTypeInt32); - - /// \brief Destructor - ~Lookup() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Generate n-gram from a 1-D string Tensor. -class DATASET_API Ngram final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] ngrams ngrams is a vector of positive integers. For example, if ngrams={4, 3}, then the result - /// would be a 4-gram followed by a 3-gram in the same tensor. If the number of words is not enough to make up - /// a n-gram, an empty string will be returned. - /// \param[in] left_pad {"pad_token", pad_width}. Padding performed on left side of the sequence. pad_width will - /// be capped at n-1. left_pad=("_",2) would pad the left side of the sequence with "__" (default={"", 0}}). - /// \param[in] right_pad {"pad_token", pad_width}. Padding performed on right side of the sequence.pad_width will - /// be capped at n-1. right_pad=("-",2) would pad the right side of the sequence with "--" (default={"", 0}}). - /// \param[in] separator Symbol used to join strings together (default=" "). - /// \par Example - /// \code - /// /* Define operations */ - /// auto ngram_op = text::Ngram({2, 3}, {"&", 2}, {"&", 2}, "-"); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({ngram_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit Ngram(const std::vector &ngrams, const std::pair &left_pad = {"", 0}, - const std::pair &right_pad = {"", 0}, const std::string &separator = " ") - : Ngram(ngrams, PairStringToChar(left_pad), PairStringToChar(right_pad), StringToChar(separator)) {} - - /// \brief Constructor. - /// \param[in] ngrams ngrams is a vector of positive integers. For example, if ngrams={4, 3}, then the result - /// would be a 4-gram followed by a 3-gram in the same tensor. If the number of words is not enough to make up - /// a n-gram, an empty string will be returned. - /// \param[in] left_pad {"pad_token", pad_width}. Padding performed on left side of the sequence. pad_width will - /// be capped at n-1. left_pad=("_",2) would pad the left side of the sequence with "__" (default={"", 0}}). - /// \param[in] right_pad {"pad_token", pad_width}. Padding performed on right side of the sequence.pad_width will - /// be capped at n-1. right_pad=("-",2) would pad the right side of the sequence with "--" (default={"", 0}}). - /// \param[in] separator Symbol used to join strings together (default=" "). - Ngram(const std::vector &ngrams, const std::pair, int32_t> &left_pad, - const std::pair, int32_t> &right_pad, const std::vector &separator); - - /// \brief Destructor - ~Ngram() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -#ifndef _WIN32 -/// \brief Apply normalize operation to UTF-8 string tensors. -class DATASET_API NormalizeUTF8 final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] normalize_form Valid values can be any of [NormalizeForm::kNone,NormalizeForm::kNfc, - /// NormalizeForm::kNfkc, NormalizeForm::kNfd, NormalizeForm::kNfkd](default=NormalizeForm::kNfkc). - /// See for details. - /// - NormalizeForm.kNone, remain the input string tensor unchanged. - /// - NormalizeForm.kNfc, normalizes with Normalization Form C. - /// - NormalizeForm.kNfkc, normalizes with Normalization Form KC. - /// - NormalizeForm.kNfd, normalizes with Normalization Form D. - /// - NormalizeForm.kNfkd, normalizes with Normalization Form KD. - /// \par Example - /// \code - /// /* Define operations */ - /// auto normalizeutf8_op = text::NormalizeUTF8(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({normalizeutf8_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit NormalizeUTF8(NormalizeForm normalize_form = NormalizeForm::kNfkc); - - /// \brief Destructor - ~NormalizeUTF8() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Replace a UTF-8 string tensor with 'replace' according to regular expression 'pattern'. -class DATASET_API RegexReplace final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] pattern The regex expression patterns. - /// \param[in] replace The string to replace the matched element. - /// \param[in] replace_all Confirm whether to replace all. If false, only replace the first matched element; - /// if true, replace all matched elements (default=true). - /// \par Example - /// \code - /// /* Define operations */ - /// auto regex_op = text::RegexReplace("\\s+", "_", true); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({regex_op}, // operations - /// {"text"}); // input columns - /// \endcode - RegexReplace(const std::string &pattern, const std::string &replace, bool replace_all = true) - : RegexReplace(StringToChar(pattern), StringToChar(replace), replace_all) {} - - /// \brief Constructor. - /// \param[in] pattern The regex expression patterns. Type should be char of vector. - /// \param[in] replace The string to replace the matched element. - /// \param[in] replace_all Confirm whether to replace all. If false, only replace the first matched element; - /// if true, replace all matched elements (default=true). - RegexReplace(const std::vector &pattern, const std::vector &replace, bool replace_all); - - /// \brief Destructor - ~RegexReplace() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Tokenize a scalar tensor of UTF-8 string by the regex expression pattern. -class DATASET_API RegexTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] delim_pattern The pattern of regex delimiters. - /// \param[in] keep_delim_pattern The string matched with 'delim_pattern' can be kept as a token if it can be - /// matched by 'keep_delim_pattern'. The default value is an empty string (""). - /// which means that delimiters will not be kept as an output token (default=""). - /// \param[in] with_offsets Whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto regex_op = text::RegexTokenizer("\\s+", "\\s+", false); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({regex_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit RegexTokenizer(const std::string &delim_pattern, const std::string &keep_delim_pattern = "", - bool with_offsets = false) - : RegexTokenizer(StringToChar(delim_pattern), StringToChar(keep_delim_pattern), with_offsets) {} - - explicit RegexTokenizer(const std::vector &delim_pattern, const std::vector &keep_delim_pattern, - bool with_offsets); - - /// \brief Destructor - ~RegexTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; -#endif - -/// \brief Tokenize a scalar token or a 1-D token to tokens by sentencepiece. -class DATASET_API SentencePieceTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] vocab a SentencePieceVocab object. - /// \param[in] out_type The type of the output. - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr ds_vocab = TextFile({"/path/to/vocab/file"}, 0, ShuffleMode::kFalse); - /// std::shared_ptr vocab = - /// ds_vocab->BuildSentencePieceVocab({}, 0, 0.9995, SentencePieceModel::kUnigram, {}); - /// auto tokenizer_op = text::SentencePieceTokenizer(vocab, mindspore::dataset::SPieceTokenizerOutType::kString); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - SentencePieceTokenizer(const std::shared_ptr &vocab, - mindspore::dataset::SPieceTokenizerOutType out_type); - - /// \brief Constructor. - /// \param[in] vocab_path vocab model file path. - /// \param[in] out_type The type of the output. - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::SentencePieceTokenizer("/path/to/model", - /// mindspore::dataset::SPieceTokenizerOutType::kInt); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - SentencePieceTokenizer(const std::string &vocab_path, mindspore::dataset::SPieceTokenizerOutType out_type) - : SentencePieceTokenizer(StringToChar(vocab_path), out_type) {} - - /// \brief Constructor. - /// \param[in] vocab_path vocab model file path. type should be char of vector. - /// \param[in] out_type The type of the output. - SentencePieceTokenizer(const std::vector &vocab_path, mindspore::dataset::SPieceTokenizerOutType out_type); - - /// \brief Destructor - ~SentencePieceTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Construct a tensor from data (only 1-D for now), where each element in the dimension -/// axis is a slice of data starting at the corresponding position, with a specified width. -class DATASET_API SlidingWindow final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] width The width of the window. It must be an integer and greater than zero. - /// \param[in] axis The axis where the sliding window is computed (default=0), axis only - /// supports 0 or -1 for now. - /// \par Example - /// \code - /// /* Define operations */ - /// auto slidingwindow_op = text::SlidingWindow(5, 0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({slidingwindow_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit SlidingWindow(int32_t width, int32_t axis = 0); - - /// \brief Destructor - ~SlidingWindow() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Convert every element in a string tensor to a number. -/// Strings are cast according to the rules specified in the following links: -/// https://en.cppreference.com/w/cpp/string/basic_string/stof, -/// https://en.cppreference.com/w/cpp/string/basic_string/stoul, -/// except that any strings which represent negative numbers cannot be cast to an unsigned integer type. -class DATASET_API ToNumber final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] data_type mindspore::DataType of the tensor to be cast to. Must be a numeric type, excluding bool. - /// \par Example - /// \code - /// /* Define operations */ - /// auto to_number_op = text::ToNumber(mindspore::DataType::kNumberTypeInt8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({to_number_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit ToNumber(mindspore::DataType data_type); - - /// \brief Destructor - ~ToNumber() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Look up a token into an vector according to the input Vectors table. -class DATASET_API ToVectors final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] vectors A Vectors object. - /// \param[in] unk_init In case of the token is out-of-vectors (OOV), the result will be initialized with `unk_init`. - /// (default={}, means to initialize with zero vectors). - /// \param[in] lower_case_backup Whether to look up the token in the lower case (default=false). - explicit ToVectors(const std::shared_ptr &vectors, const std::vector &unk_init = {}, - bool lower_case_backup = false); - - /// \brief Destructor - ~ToVectors() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Truncate the input sequence so that it does not exceed the maximum length. -class DATASET_API Truncate final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] max_seq_len Maximum allowable length. - /// \par Example - /// \code - /// /* Define operations */ - /// auto truncate_op = text::Truncate(5); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({truncate_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit Truncate(int32_t max_seq_len); - - /// \brief Destructor. - ~Truncate() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Truncate a pair of rank-1 tensors such that the total length is less than max_length. -class DATASET_API TruncateSequencePair final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] max_length Maximum length required. - /// \par Example - /// \code - /// /* Define operations */ - /// auto truncate_op = text::TruncateSequencePair(5); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({truncate_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit TruncateSequencePair(int32_t max_length); - - /// \brief Destructor - ~TruncateSequencePair() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Tokenize a scalar tensor of UTF-8 string to Unicode characters. -class DATASET_API UnicodeCharTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] with_offsets whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::UnicodeCharTokenizer(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit UnicodeCharTokenizer(bool with_offsets = false); - - /// \brief Destructor - ~UnicodeCharTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Tokenize scalar token or 1-D tokens to 1-D sub-word tokens. -class DATASET_API WordpieceTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] vocab A Vocab object. - /// \param[in] suffix_indicator This parameter is used to show that the sub-word - /// is the last part of a word (default='##'). - /// \param[in] max_bytes_per_token Tokens exceeding this length will not be further split (default=100). - /// \param[in] unknown_token When a token cannot be found, return the token directly if 'unknown_token' is an empty - /// string, else return the specified string (default='[UNK]'). - /// \param[in] with_offsets whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// std::vector word_list = {"book", "apple", "rabbit"}; - /// std::shared_ptr vocab = std::make_shared(); - /// Status s = Vocab::BuildFromVector(word_list, {}, true, &vocab); - /// auto tokenizer_op = text::WordpieceTokenizer(vocab); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit WordpieceTokenizer(const std::shared_ptr &vocab, const std::string &suffix_indicator = "##", - int32_t max_bytes_per_token = 100, const std::string &unknown_token = "[UNK]", - bool with_offsets = false) - : WordpieceTokenizer(vocab, StringToChar(suffix_indicator), max_bytes_per_token, StringToChar(unknown_token), - with_offsets) {} - - explicit WordpieceTokenizer(const std::shared_ptr &vocab, const std::vector &suffix_indicator, - int32_t max_bytes_per_token, const std::vector &unknown_token, bool with_offsets); - - /// \brief Destructor - ~WordpieceTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -#ifndef _WIN32 -/// \brief Tokenize a scalar tensor of UTF-8 string on Unicode script boundaries. -class DATASET_API UnicodeScriptTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] keep_whitespace whether to emit whitespace tokens (default=false). - /// \param[in] with_offsets whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::UnicodeScriptTokenizer(false, true); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit UnicodeScriptTokenizer(bool keep_whitespace = false, bool with_offsets = false); - - /// \brief Destructor - ~UnicodeScriptTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Tokenize a scalar tensor of UTF-8 string on ICU4C defined whitespaces. -class DATASET_API WhitespaceTokenizer final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] with_offsets whether to output offsets of tokens (default=false). - /// \par Example - /// \code - /// /* Define operations */ - /// auto tokenizer_op = text::WhitespaceTokenizer(false); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({tokenizer_op}, // operations - /// {"text"}); // input columns - /// \endcode - explicit WhitespaceTokenizer(bool with_offsets = false); - - /// \brief Destructor - ~WhitespaceTokenizer() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to the TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; -#endif -} // namespace text -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TEXT_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/transforms.h b/mindspore-lite/minddata/dataset/include/dataset/transforms.h deleted file mode 100644 index 24242834c..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/transforms.h +++ /dev/null @@ -1,638 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TRANSFORMS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TRANSFORMS_H_ - -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" -#include "include/api/types.h" -#include "include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -class TensorOperation; - -// We need the following two groups of forward declaration to friend the class in class TensorTransform. -namespace transforms { -class Compose; -class RandomApply; -class RandomChoice; -} // namespace transforms - -namespace vision { -class BoundingBoxAugment; -class RandomSelectSubpolicy; -class UniformAugment; -} // namespace vision - -// Abstract class to represent a tensor transform operation in the data pipeline. -/// \class TensorTransform transforms.h -/// \brief A base class to represent a tensor transform operation in the data pipeline. -class DATASET_API TensorTransform : public std::enable_shared_from_this { - friend class Dataset; - friend class Execute; - friend class transforms::Compose; - friend class transforms::RandomApply; - friend class transforms::RandomChoice; - friend class vision::BoundingBoxAugment; - friend class vision::RandomSelectSubpolicy; - friend class vision::UniformAugment; - - public: - /// \brief Constructor - TensorTransform() = default; - - /// \brief Destructor - virtual ~TensorTransform() = default; - - protected: - /// \brief Pure virtual function to convert a TensorTransform class into a IR TensorOperation object. - /// \return shared pointer to the newly created TensorOperation. - virtual std::shared_ptr Parse() = 0; - - /// \brief Virtual function to convert a TensorTransform class into a IR TensorOperation object. - /// \param[in] env A string to determine the running environment - /// \return shared pointer to the newly created TensorOperation. - virtual std::shared_ptr Parse(const MapTargetDevice &env) { return nullptr; } -}; - -/// \brief Slice object used in SliceOption. -class DATASET_API Slice { - public: - /// \brief Constructor, with start, stop and step default to 0. - Slice() : start_(0), stop_(0), step_(0) {} - - /// \brief Constructor. - /// \param[in] start Starting integer specifying where to start the slicing. - /// \param[in] stop Ending integer specifying where to stop the slicing. - /// \param[in] step An integer specifying the step of the slicing. - /// \par Example - /// \code - /// /* Slice dimension from 2 to 10 with step 2. */ - /// Slice(0, 10, 2); - /// \endcode - Slice(dsize_t start, dsize_t stop, dsize_t step) : start_(start), stop_(stop), step_(step) {} - - /// \brief Constructor, with step=1 - /// \param[in] start Starting integer specifying where to start the slicing. - /// \param[in] stop Ending integer specifying where to stop the slicing. - /// \par Example - /// \code - /// /* Slice dimension from 5 to 10 with step 1. */ - /// Slice(5, 10); - /// \endcode - Slice(dsize_t start, dsize_t stop) : start_(start), stop_(stop), step_(1) {} - - /// \brief Constructor, with start=0 and step=1 - /// \param[in] stop Ending integer specifying where to stop the slicing. - /// \par Example - /// \code - /// /* Slice dimension from 0 to 5 with step 1. */ - /// Slice(5); - /// \endcode - explicit Slice(dsize_t stop) : start_(0), stop_(stop), step_(1) {} - - Slice(Slice const &slice) = default; - - Slice &operator=(const Slice &slice) = default; - - ~Slice() = default; - - bool valid() const { return step_ != 0; } - dsize_t start_; - dsize_t stop_; - dsize_t step_; -}; - -/// \brief SliceOption used in Slice TensorTransform. -class DATASET_API SliceOption { - public: - /// \param[in] all Slice the whole dimension - /// \par Example - /// \code - /// /* Slice all the data. */ - /// SliceOption slice_option = SliceOption(True); - /// \endcode - explicit SliceOption(bool all) : all_(all) {} - - /// \param[in] indices Slice these indices along the dimension. Negative indices are supported. - /// \par Example - /// \code - /// /* Slice the given dimensions. */ - /// std::vector indices = {0, 3, 6, 7}; - /// SliceOption slice_option = SliceOption(indices); - /// \endcode - explicit SliceOption(const std::vector &indices) : indices_(indices) {} - - /// \param[in] slice Slice the generated indices from the slice object along the dimension. - /// \par Example - /// \code - /// /* Slice dimension from 2 to 10 with step 2. */ - /// SliceOption slice_option = SliceOption(Slice(0, 10, 2)); - /// transforms::Slice slice_op = transforms::Slice({slice_option}); - /// \endcode - explicit SliceOption(const Slice &slice) : slice_(slice) {} - - SliceOption(SliceOption const &slice) = default; - - SliceOption &operator=(const SliceOption &slice) = default; - - ~SliceOption() = default; - - // only one of the following will be valid - // given indices to slice the Tensor. - std::vector indices_ = {}; - // Slice object. All start, stop and step are 0 if invalid. - Slice slice_; - bool all_ = false; -}; - -// Transform operations for performing data transformation. -namespace transforms { - -/// \brief Compose a list of transforms into a single transform. -class DATASET_API Compose final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] transforms A vector of raw pointers to TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// auto resize_op(new vision::Resize({30, 30})); - /// auto center_crop_op(new vision::CenterCrop({16, 16})); - /// auto compose_op(new transforms::Compose({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({compose_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Compose(const std::vector &transforms); - - /// \brief Constructor. - /// \param[in] transforms A vector of shared pointers to TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr resize_op(new vision::Resize({30, 30})); - /// std::shared_ptr center_crop_op(new vision::CenterCrop({16, 16})); - /// std::shared_ptr compose_op(new transforms::Compose({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({compose_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Compose(const std::vector> &transforms); - - /// \brief Constructor. - /// \param[in] transforms A vector of TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// vision::Resize resize_op = vision::Resize({30, 30}); - /// vision::CenterCrop center_crop_op = vision::CenterCrop({16, 16}); - /// transforms::Compose compose_op = transforms::Compose({resize_op, center_crop_op}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({compose_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Compose(const std::vector> &transforms); - - /// \brief Destructor - ~Compose() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Concatenate all tensors into a single tensor. -class DATASET_API Concatenate final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] axis Concatenate the tensors along given axis, only support 0 or -1 so far (default=0). - /// \param[in] prepend MSTensor to be prepended to the concatenated tensors (default={}). - /// \param[in] append MSTensor to be appended to the concatenated tensors (default={}). - /// \par Example - /// \code - /// /* Define operations */ - /// mindspore::MSTensor append_MSTensor; - /// mindspore::MSTensor prepend_MSTensor; - /// auto concatenate_op = transforms::Concatenate(0, append_MSTensor, prepend_MSTensor); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({concatenate_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit Concatenate(int8_t axis = 0, const MSTensor &prepend = {}, const MSTensor &append = {}); - - /// \brief Destructor - ~Concatenate() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Duplicate the input tensor to a new output tensor. -/// The input tensor is carried over to the output list. -class DATASET_API Duplicate final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto duplicate_op = transforms::Duplicate(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({duplicate_op}, // operations - /// {"column"}, // input columns - /// {"column", "column_copy"}); // output columns - /// \endcode - Duplicate(); - - /// \brief Destructor - ~Duplicate() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Fill all elements in the tensor with the specified value. -/// The output tensor will have the same shape and type as the input tensor. -class DATASET_API Fill final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] fill_value Scalar value to fill the tensor with. - /// It can only be MSTensor of the following types from mindspore::DataType: - /// String, Bool, Int8/16/32/64, UInt8/16/32/64, Float16/32/64. - /// \par Example - /// \code - /// /* Define operations */ - /// mindspore::MSTensor tensor; - /// auto fill_op = transforms::Fill(tensor); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({fill_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit Fill(const MSTensor &fill_value); - - /// \brief Destructor - ~Fill() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Mask content of the input tensor with the given predicate. -/// Any element of the tensor that matches the predicate will be evaluated to True, otherwise False. -class DATASET_API Mask final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] op One of the relational operators: EQ, NE LT, GT, LE or GE. - /// \param[in] constant Constant to be compared to. It can only be MSTensor of the following types - /// from mindspore::DataType: String, Int, Float, Bool. - /// \param[in] ms_type Type of the generated mask. It can only be numeric or boolean datatype. - /// (default=mindspore::DataType::kNumberTypeBool) - /// \par Example - /// \code - /// /* Define operations */ - /// mindspore::MSTensor constant; - /// auto mask_op = transforms::Mask(RelationalOp::kEqual, constant); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({mask_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit Mask(RelationalOp op, const MSTensor &constant, - mindspore::DataType ms_type = mindspore::DataType(mindspore::DataType::kNumberTypeBool)); - - /// \brief Destructor - ~Mask() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Convert the labels into OneHot format. -class DATASET_API OneHot final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] num_classes number of classes. - /// \param[in] smoothing_rate smoothing rate default(0.0). - /// \par Example - /// \code - /// /* Define operations */ - /// mindspore::MSTensor constant; - /// auto one_hot_op = transforms::OneHot(10); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({one_hot_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit OneHot(int32_t num_classes, double smoothing_rate = 0.0); - - /// \brief Destructor - ~OneHot() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Pad input tensor according to pad_shape -class DATASET_API PadEnd final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] pad_shape List of integers representing the shape needed, need to have same rank with input tensor. - /// Dimensions that set to `-1` will not be padded (i.e., original dim will be used). - /// Shorter dimensions will truncate the values. - /// \param[in] pad_value Value used to pad (default={}). - /// \par Example - /// \code - /// /* Define operations */ - /// mindspore::MSTensor constant; - /// auto pad_end_op = transforms::PadEnd({224, 224, 1}, {constant}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({pad_end_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit PadEnd(const std::vector &pad_shape, const MSTensor &pad_value = {}); - - /// \brief Destructor - ~PadEnd() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly perform a series of transforms with a given probability. -class DATASET_API RandomApply final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] transforms A vector of raw pointers to TensorTransform objects to be applied. - /// \param[in] prob The probability to apply the transformation list (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// auto resize_op(new vision::Resize({30, 30})); - /// auto center_crop_op(new vision::CenterCrop({16, 16})); - /// auto random_op(new transforms::RandomApply({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomApply(const std::vector &transforms, double prob = 0.5); - - /// \brief Constructor. - /// \param[in] transforms A vector of shared pointers to TensorTransform objects to be applied. - /// \param[in] prob The probability to apply the transformation list (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr resize_op(new vision::Resize({30, 30})); - /// std::shared_ptr center_crop_op(new vision::CenterCrop({16, 16})); - /// std::shared_ptr random_op(new transforms::RandomApply({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomApply(const std::vector> &transforms, double prob = 0.5); - - /// \brief Constructor. - /// \param[in] transforms A vector of TensorTransform objects to be applied. - /// \param[in] prob The probability to apply the transformation list (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// vision::Resize resize_op = vision::Resize({30, 30}); - /// vision::CenterCrop center_crop_op = vision::CenterCrop({16, 16}); - /// transforms::RandomApply random_op = transforms::RandomApply({resize_op, center_crop_op}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomApply(const std::vector> &transforms, double prob = 0.5); - - /// \brief Destructor - ~RandomApply() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly select one transform from a list of transforms to perform on the input tensor. -class DATASET_API RandomChoice final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] transforms A vector of raw pointers to TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// auto resize_op(new vision::Resize({30, 30})); - /// auto center_crop_op(new vision::CenterCrop({16, 16})); - /// auto random_op(new transforms::RandomChoice({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomChoice(const std::vector &transforms); - - /// \brief Constructor. - /// \param[in] transforms A vector of shared pointers to TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr resize_op(new vision::Resize({30, 30})); - /// std::shared_ptr center_crop_op(new vision::CenterCrop({16, 16})); - /// std::shared_ptr random_op(new transforms::RandomChoice({resize_op, center_crop_op})); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomChoice(const std::vector> &transforms); - - /// \brief Constructor. - /// \param[in] transforms A vector of TensorTransform objects to be applied. - /// \par Example - /// \code - /// /* Define operations */ - /// vision::Resize resize_op = vision::Resize({30, 30}); - /// vision::CenterCrop center_crop_op = vision::CenterCrop({16, 16}); - /// transforms::RandomChoice random_op = transforms::RandomChoice({resize_op, center_crop_op}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomChoice(const std::vector> &transforms); - - /// \brief Destructor - ~RandomChoice() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Extract a tensor out using the given n slices. -/// The functionality of Slice is similar to the feature of indexing of NumPy. -/// (Currently only rank-1 tensors are supported). -class DATASET_API Slice final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] slice_input Vector of SliceOption. - /// \par Example - /// \code - /// /* Define operations */ - /// SliceOption slice_option = SliceOption(Slice(0, 3, 2)); - /// transforms::Slice slice_op = transforms::Slice({slice_option}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({slice_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit Slice(const std::vector &slice_input); - - /// \brief Destructor - ~Slice() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Cast the MindSpore data type of a tensor to another. -class DATASET_API TypeCast final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] data_type mindspore::DataType to be cast to. - /// \par Example - /// \code - /// /* Define operations */ - /// auto typecast_op = transforms::TypeCast(mindspore::DataType::kNumberTypeUInt8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({typecast_op}, // operations - /// {"column"}); // input columns - /// \endcode - explicit TypeCast(mindspore::DataType data_type); - - /// \brief Destructor - ~TypeCast() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Return an output tensor that contains all the unique elements of the input tensor in -/// the same order as they appear in the input tensor. -class DATASET_API Unique final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto unique_op = transforms::Unique(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({unique_op}, // operations - /// {"column"}); // input columns - /// \endcode - Unique(); - - /// \brief Destructor - ~Unique() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; -} // namespace transforms -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_TRANSFORMS_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/vision.h b/mindspore-lite/minddata/dataset/include/dataset/vision.h deleted file mode 100755 index 10e00e544..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/vision.h +++ /dev/null @@ -1,2129 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/status.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" -#include "include/dataset/vision_lite.h" - -namespace mindspore { -namespace dataset { -class TensorOperation; - -// Transform operations for performing computer vision. -namespace vision { -/// \brief Apply brightness adjustment on input image. -class DATASET_API AdjustBrightness final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] brightness_factor Adjusts image brightness, non negative real number. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_brightness_op = vision::AdjustBrightness(2.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_brightness_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustBrightness(float brightness_factor); - - /// \brief Destructor. - ~AdjustBrightness() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply contrast adjustment on input image. -class DATASET_API AdjustContrast final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] contrast_factor Adjusts image contrast, non negative real number. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_contrast_op = vision::AdjustContrast(10.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_contrast_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustContrast(float contrast_factor); - - /// \brief Destructor. - ~AdjustContrast() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief AdjustGamma TensorTransform. -/// \note Apply gamma correction on input image. -class DATASET_API AdjustGamma final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] gamma Non negative real number, which makes the output image pixel value - /// exponential in relation to the input image pixel value. - /// \param[in] gain The constant multiplier. Default: 1.0. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_gamma_op = vision::AdjustGamma(10.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_gamma_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustGamma(float gamma, float gain = 1.0); - - /// \brief Destructor. - ~AdjustGamma() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \note Apply hue adjustment on input image. -class DATASET_API AdjustHue final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] hue_factor How much to shift the hue channel, must be in the interval [-0.5, 0.5]. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_hue_op = vision::AdjustHue(0.2); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_contrast_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustHue(float hue_factor); - - /// \brief Destructor. - ~AdjustHue() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Adjust the color saturation of the input image. -class DATASET_API AdjustSaturation final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] saturation_factor Adjust image saturation, non negative real number. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_saturation_op = vision::AdjustSaturation(2.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_saturation_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustSaturation(float saturation_factor); - - /// \brief Destructor. - ~AdjustSaturation() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply adjust sharpness on input image. Input image is expected to be in [H, W, C] or [H, W] format. -class DATASET_API AdjustSharpness final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] sharpness_factor How much to adjust the sharpness. Can be any Non negative real number. - /// 0 gives a blurred image, 1 gives the original image while 2 increases the Sharpness by a factor of 2. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto adjust_sharpness_op = vision::AdjustSharpness(2.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, adjust_sharpness_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AdjustSharpness(float sharpness_factor); - - /// \brief Destructor. - ~AdjustSharpness() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply AutoAugment data augmentation method. -class DATASET_API AutoAugment final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] policy An enum for the data auto augmentation policy (default=AutoAugmentPolicy::kImageNet). - /// - AutoAugmentPolicy::kImageNet, AutoAugment policy learned on the ImageNet dataset. - /// - AutoAugmentPolicy::kCifar10, AutoAugment policy learned on the Cifar10 dataset. - /// - AutoAugmentPolicy::kSVHN, AutoAugment policy learned on the SVHN dataset. - /// \param[in] interpolation An enum for the mode of interpolation (default=InterpolationMode::kNearestNeighbour). - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// \param[in] fill_value A vector representing the pixel intensity of the borders (default={0, 0, 0}). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto auto_augment_op = vision::AutoAugment(AutoAugmentPolicy::kImageNet, - /// InterpolationMode::kNearestNeighbour, {0, 0, 0}); - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, auto_augment_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AutoAugment(AutoAugmentPolicy policy = AutoAugmentPolicy::kImageNet, - InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, - const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~AutoAugment() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply automatic contrast on the input image. -class DATASET_API AutoContrast final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] cutoff Percent of pixels to cut off from the histogram, the valid range of cutoff value is 0 to 50. - /// \param[in] ignore Pixel values to ignore. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto autocontrast_op = vision::AutoContrast(10.0, {10, 20}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, autocontrast_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit AutoContrast(float cutoff = 0.0, const std::vector &ignore = {}); - - /// \brief Destructor. - ~AutoContrast() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief BoundingBoxAugment TensorTransform. -/// \note Apply a given image transform on a random selection of bounding box regions of a given image. -class DATASET_API BoundingBoxAugment final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] transform Raw pointer to the TensorTransform operation. - /// \param[in] ratio Ratio of bounding boxes to apply augmentation on. Range: [0, 1] (default=0.3). - /// \par Example - /// \code - /// /* Define operations */ - /// TensorTransform *rotate_op = new vision::RandomRotation({-180, 180}); - /// auto bbox_aug_op = vision::BoundingBoxAugment(rotate_op, 0.5); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({bbox_aug_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit BoundingBoxAugment(TensorTransform *transform, float ratio = 0.3); - - /// \brief Constructor. - /// \param[in] transform Smart pointer to the TensorTransform operation. - /// \param[in] ratio Ratio of bounding boxes where augmentation is applied to. Range: [0, 1] (default=0.3). - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr flip_op = std::make_shared(0.5); - /// std::shared_ptr bbox_aug_op = std::make_shared(flip_op, 0.1); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({bbox_aug_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit BoundingBoxAugment(const std::shared_ptr &transform, float ratio = 0.3); - - /// \brief Constructor. - /// \param[in] transform Object pointer to the TensorTransform operation. - /// \param[in] ratio Ratio of bounding boxes where augmentation is applied to. Range: [0, 1] (default=0.3). - /// \par Example - /// \code - /// /* Define operations */ - /// vision::RandomColor random_color_op = vision::RandomColor(0.5, 1.0); - /// vision::BoundingBoxAugment bbox_aug_op = vision::BoundingBoxAugment(random_color_op, 0.8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({bbox_aug_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit BoundingBoxAugment(const std::reference_wrapper &transform, float ratio = 0.3); - - /// \brief Destructor. - ~BoundingBoxAugment() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Change the color space of the image. -class DATASET_API ConvertColor final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] convert_mode The mode of image channel conversion. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared(ConvertMode::COLOR_BGR2RGB)}, // operations - /// {"image"}); // input columns - /// \endcode - explicit ConvertColor(ConvertMode convert_mode); - - /// \brief Destructor. - ~ConvertColor() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Mask a random section of each image with the corresponding part of another randomly -/// selected image in that batch. -class DATASET_API CutMixBatch final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] image_batch_format The format of the batch. - /// \param[in] alpha The hyperparameter of beta distribution (default = 1.0). - /// \param[in] prob The probability by which CutMix is applied to each image (default = 1.0). - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Batch(5); - /// dataset = dataset->Map({std::make_shared(ImageBatchFormat::kNHWC)}, // operations - /// {"image", "label"}); // input columns - /// \endcode - explicit CutMixBatch(ImageBatchFormat image_batch_format, float alpha = 1.0, float prob = 1.0); - - /// \brief Destructor. - ~CutMixBatch() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly cut (mask) out a given number of square patches from the input image. -class DATASET_API CutOut final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] length Integer representing the side length of each square patch. - /// \param[in] num_patches Integer representing the number of patches to be cut out of an image. - /// \param[in] is_hwc A boolean to indicate whether the input image is in HWC format (true) or CHW - /// format (false) (default = true). - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared(1, 4, true)}, // operations - /// {"image"}); // input columns - /// \endcode - explicit CutOut(int32_t length, int32_t num_patches = 1, bool is_hwc = true); - - /// \brief Destructor. - ~CutOut() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -#ifdef ENABLE_FFMPEG -/// \brief Decode the input video. -class DATASET_API DecodeVideo final : public TensorTransform { - public: - /// \brief Constructor. It will decode a vector containing a raw video tensor into a vector containing two tensors. - /// The raw video tensor in the input vector should be 1D array of UINT8. - /// The first tensor in the output vector is a visual tensor, the shape is , the type is DE_UINT8. Pixel - /// order is RGB. The second tensor in the output vector is an audio tensor, the shape is . - /// \par Example - /// \code - /// /* Read video file into tensor */ - /// mindspore::MSTensor video; - /// ASSERT_OK(mindspore::dataset::vision::ReadFile("/path/to/video/file", &video)); - /// std::vector input_tensor; - /// std::vector output_tensor; - /// input_tensor.push_back(video); - /// auto decode_video = vision::DecodeVideo(); - /// auto transform = Execute(decode_video); - /// Status rc = transform(input_tensor, &output_tensor); - /// \endcode - DecodeVideo(); - - /// \brief Destructor. - ~DecodeVideo() = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; -#endif - -/// \brief Encode the image as JPEG data. -/// \param[in] image The image to be encoded. -/// \param[out] output The Tensor data. -/// \param[in] quality The quality for the output tensor, in range of [1, 100]. Default: 75. -/// \return The status code. -Status DATASET_API EncodeJpeg(const mindspore::MSTensor &image, mindspore::MSTensor *output, int quality = 75); - -/// \brief Encode the image as PNG data. -/// \param[in] image The image to be encoded. -/// \param[out] output The Tensor data. -/// \param[in] compression_level The compression_level for encoding, in range of [0, 9]. Default: 6. -/// \return The status code. -Status DATASET_API EncodePng(const mindspore::MSTensor &image, mindspore::MSTensor *output, int compression_level = 6); - -/// \brief Apply histogram equalization on the input image. -class DATASET_API Equalize final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared()}, // operations - /// {"image"}); // input columns - /// \endcode - Equalize(); - - /// \brief Destructor. - ~Equalize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Erase the input image with given value. -class DATASET_API Erase final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] top Vertical ordinate of the upper left corner of erased region. - /// \param[in] left Horizontal ordinate of the upper left corner of erased region. - /// \param[in] height Height of erased region. - /// \param[in] width Width of erased region. - /// \param[in] value Pixel value used to pad the erased area. - /// If a single integer is provided, it will be used for all RGB channels. - /// If a sequence of length 3 is provided, it will be used for R, G, B channels respectively. Default: 0. - /// \param[in] inplace Whether to erase inplace. Default: False. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared(10, 10, 10, 10)}, // operations - /// {"image"}); // input columns - /// \endcode - Erase(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value = {0., 0., 0.}, - bool inplace = false); - - /// \brief Destructor. - ~Erase() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Get the number of input image channels. -/// \param[in] image Tensor of the image. -/// \param[out] channels Channels of the image. -/// \return The status code. -Status DATASET_API GetImageNumChannels(const mindspore::MSTensor &image, dsize_t *channels); - -/// \brief Get the size of input image. -/// \param[in] image Tensor of the image. -/// \param[out] size Size of the image as [height, width]. -/// \return The status code. -Status DATASET_API GetImageSize(const mindspore::MSTensor &image, std::vector *size); - -/// \brief Flip the input image horizontally. -class DATASET_API HorizontalFlip final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared()}, // operations - /// {"image"}); // input columns - /// \endcode - HorizontalFlip(); - - /// \brief Destructor. - ~HorizontalFlip() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Apply invert on the input image in RGB mode. -class DATASET_API Invert final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared()}, // operations - /// {"image"}); // input columns - /// \endcode - Invert(); - - /// \brief Destructor. - ~Invert() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Apply MixUp transformation on an input batch of images and labels. The labels must be in -/// one-hot format and Batch must be called before calling this function. -class DATASET_API MixUpBatch final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] alpha hyperparameter of beta distribution (default = 1.0). - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Batch(5); - /// dataset = dataset->Map({std::make_shared()}, // operations - /// {"image"}); // input columns - /// \endcode - explicit MixUpBatch(float alpha = 1.0); - - /// \brief Destructor. - ~MixUpBatch() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Normalize the input image with respect to mean and standard deviation and pads an extra -/// channel with value zero. -class DATASET_API NormalizePad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] mean A vector of mean values for each channel, with respect to channel order. - /// The mean values must be in range [0.0, 255.0]. - /// \param[in] std A vector of standard deviations for each channel, with respect to channel order. - /// The standard deviation values must be in range (0.0, 255.0]. - /// \param[in] dtype The output datatype of Tensor. - /// The standard deviation values must be "float32" or "float16"(default = "float32"). - /// \param[in] is_hwc A boolean to indicate whether the input image is in HWC format (true) or CHW - /// format (false) (default = true). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto normalize_pad_op = vision::NormalizePad({121.0, 115.0, 100.0}, {70.0, 68.0, 71.0}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, normalize_pad_op}, // operations - /// {"image"}); // input columns - /// \endcode - NormalizePad(const std::vector &mean, const std::vector &std, const std::string &dtype = "float32", - bool is_hwc = true) - : NormalizePad(mean, std, StringToChar(dtype), is_hwc) {} - - NormalizePad(const std::vector &mean, const std::vector &std, const std::vector &dtype, - bool is_hwc = true); - - /// \brief Destructor. - ~NormalizePad() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Pad the image to a fixed size. -class DATASET_API PadToSize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A two element vector representing the target size to pad, in order of [height, width]. - /// \param[in] offset A two element vector representing the lengths to pad on the top and left, - /// in order of [top, left]. Default: {}, means to pad symmetrically, keeping the original image in center. - /// \param[in] fill_value A vector representing the pixel intensity of the borders. Only valid if the - /// padding_mode is BorderType.kConstant. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. Default: {0}. - /// \param[in] padding_mode The method of padding, which can be one of BorderType.kConstant, BorderType.kEdge, - /// BorderType.kReflect or BorderType.kSymmetric. Default: BorderType.kConstant. - /// - BorderType.kConstant, pads with a constant value. - /// - BorderType.kEdge, pads with the last value at the edge of the image. - /// - BorderType.kReflect, pads with reflection of the image omitting the last value on the edge. - /// - BorderType.kSymmetric, pads with reflection of the image repeating the last value on the edge. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto pad_to_size_op = vision::PadToSize({256, 256}, {10, 20}, {255, 255, 255}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, pad_to_size_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit PadToSize(const std::vector &size, const std::vector &offset = {}, - const std::vector &fill_value = {0}, BorderType padding_mode = BorderType::kConstant); - - /// \brief Destructor. - ~PadToSize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Perform perspective transform on the image. -class DATASET_API Perspective final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] start_points List containing four lists of two integers corresponding to four - /// corners [top-left, top-right, bottom-right, bottom-left] of the original image. - /// \param[in] end_points List containing four lists of two integers corresponding to four - /// corners [top-left, top-right, bottom-right, bottom-left] of the transformed image. - /// \param[in] interpolation An enum for the mode of interpolation. Default: InterpolationMode::kLinear. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// std::vector> start_points = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; - /// std::vector> end_points = {{0, 2}, {2, 0}, {2, 2}, {0, 2}}; - /// auto perspective_op = vision::Perspective(start_points, end_points, InterpolationMode::kLinear); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, perspective_op}, // operations - /// {"image"}); // input columns - /// \endcode - Perspective(const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation); - - /// \brief Destructor. - ~Perspective() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Posterize an image by reducing the number of bits for each color channel. -class DATASET_API Posterize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] bits The number of bits to keep for each channel, - /// should be in range of [0, 8]. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto posterize_op = vision::Posterize(8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, posterize_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Posterize(uint8_t bits); - - /// \brief Destructor. - ~Posterize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply RandAugment data augmentation method. -class DATASET_API RandAugment final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] num_ops Number of augmentation transformations to apply sequentially. Default: 2. - /// \param[in] magnitude Magnitude for all the transformations. Default: 9. - /// \param[in] num_magnitude_bins The number of different magnitude values. Default: 31. - /// \param[in] interpolation An enum for the mode of interpolation. Default: InterpolationMode::kNearestNeighbour. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// \param[in] fill_value A vector representing the pixel intensity of the borders. Default: {0, 0, 0}. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rand_augment_op = vision::RandAugment(); - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rand_augment_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandAugment(int32_t num_ops = 2, int32_t magnitude = 9, int32_t num_magnitude_bins = 31, - InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, - const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~RandAugment() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Automatically adjust the contrast of the image with a given probability. -class DATASET_API RandomAutoContrast final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] cutoff Percent of the lightest and darkest pixels to be cut off from - /// the histogram of the input image. The value must be in range of [0.0, 50.0) (default=0.0). - /// \param[in] ignore The background pixel values to be ignored, each of which must be - /// in range of [0, 255] (default={}). - /// \param[in] prob A float representing the probability of AutoContrast, which must be - /// in range of [0, 1] (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_auto_contrast_op = vision::RandomAutoContrast(5.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_auto_contrast_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomAutoContrast(float cutoff = 0.0, const std::vector &ignore = {}, float prob = 0.5); - - /// \brief Destructor. - ~RandomAutoContrast() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly adjust the sharpness of the input image with a given probability. -class DATASET_API RandomAdjustSharpness final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] degree A float representing sharpness adjustment degree, which must be non negative. - /// \param[in] prob A float representing the probability of the image being sharpness adjusted, which - /// must in range of [0, 1] (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_adjust_sharpness_op = vision::RandomAdjustSharpness(30.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_adjust_sharpness_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomAdjustSharpness(float degree, float prob = 0.5); - - /// \brief Destructor. - ~RandomAdjustSharpness() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Blend an image with its grayscale version with random weights -/// t and 1 - t generated from a given range. If the range is trivial -/// then the weights are determinate and t equals to the bound of the interval. -class DATASET_API RandomColor final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] t_lb Lower bound random weights. - /// \param[in] t_ub Upper bound random weights. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_color_op = vision::RandomColor(5.0, 50.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_color_op}, // operations - /// {"image"}); // input columns - /// \endcode - RandomColor(float t_lb, float t_ub); - - /// \brief Destructor. - ~RandomColor() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly adjust the brightness, contrast, saturation, and hue of the input image. -class DATASET_API RandomColorAdjust final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] brightness Brightness adjustment factor. Must be a vector of one or two values - /// if it is a vector of two values it needs to be in the form of [min, max] (Default={1, 1}). - /// \param[in] contrast Contrast adjustment factor. Must be a vector of one or two values - /// if it is a vector of two values, it needs to be in the form of [min, max] (Default={1, 1}). - /// \param[in] saturation Saturation adjustment factor. Must be a vector of one or two values - /// if it is a vector of two values, it needs to be in the form of [min, max] (Default={1, 1}). - /// \param[in] hue Hue adjustment factor. Must be a vector of one or two values - /// if it is a vector of two values, it must be in the form of [min, max] where -0.5 <= min <= max <= 0.5 - /// (Default={0, 0}). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_color_adjust_op = vision::RandomColorAdjust({1.0, 5.0}, {10.0, 20.0}, {40.0, 40.0}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_color_adjust_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomColorAdjust(const std::vector &brightness = {1.0, 1.0}, - const std::vector &contrast = {1.0, 1.0}, - const std::vector &saturation = {1.0, 1.0}, - const std::vector &hue = {0.0, 0.0}); - - /// \brief Destructor. - ~RandomColorAdjust() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop the input image at a random location. -class DATASET_API RandomCrop final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] padding A vector representing the number of pixels to pad the image. - /// If the vector has one value, it pads all sides of the image with that value. - /// If the vector has two values, it pads left and right with the first and - /// top and bottom with the second value. - /// If the vector has four values, it pads left, top, right, and bottom with - /// those values respectively. - /// \param[in] pad_if_needed A boolean indicating that whether to pad the image - /// if either side is smaller than the given output size. - /// \param[in] fill_value A vector representing the pixel intensity of the borders if the padding_mode is - /// BorderType.kConstant. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \param[in] padding_mode The method of padding (default=BorderType::kConstant).It can be any of - /// [BorderType::kConstant, BorderType::kEdge, BorderType::kReflect, BorderType::kSymmetric]. - /// - BorderType::kConstant, Fill the border with constant values. - /// - BorderType::kEdge, Fill the border with the last value on the edge. - /// - BorderType::kReflect, Reflect the values on the edge omitting the last value of edge. - /// - BorderType::kSymmetric, Reflect the values on the edge repeating the last value of edge. - /// \note If the input image is more than one, then make sure that the image size is the same. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_crop_op = vision::RandomCrop({255, 255}, {10, 10, 10, 10}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_crop_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomCrop(const std::vector &size, const std::vector &padding = {0, 0, 0, 0}, - bool pad_if_needed = false, const std::vector &fill_value = {0, 0, 0}, - BorderType padding_mode = BorderType::kConstant); - - /// \brief Destructor. - ~RandomCrop() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Equivalent to RandomResizedCrop TensorTransform, but crop the image before decoding. -class DATASET_API RandomCropDecodeResize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] scale Range [min, max) of respective size of the - /// original size to be cropped (default=(0.08, 1.0)). - /// \param[in] ratio Range [min, max) of aspect ratio to be - /// cropped (default=(3. / 4., 4. / 3.)). - /// \param[in] interpolation An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \param[in] max_attempts The maximum number of attempts to propose a valid crop_area (default=10). - /// If exceeded, fall back to use center_crop instead. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomCropDecodeResize({255, 255}, {0.1, 0.5}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomCropDecodeResize(const std::vector &size, const std::vector &scale = {0.08, 1.0}, - const std::vector &ratio = {3. / 4., 4. / 3.}, - InterpolationMode interpolation = InterpolationMode::kLinear, - int32_t max_attempts = 10); - - /// \brief Destructor. - ~RandomCropDecodeResize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop the input image at a random location and adjust bounding boxes accordingly. -/// If the cropped area is out of bbox, the returned bbox will be empty. -class DATASET_API RandomCropWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] padding A vector representing the number of pixels to pad the image - /// If the vector has one value, it pads all sides of the image with that value. - /// If the vector has two values, it pads left and right with the first and - /// top and bottom with the second value. - /// If the vector has four values, it pads left, top, right, and bottom with - /// those values respectively. - /// \param[in] pad_if_needed A boolean indicating that whether to pad the image - /// if either side is smaller than the given output size. - /// \param[in] fill_value A vector representing the pixel intensity of the borders. Only valid - /// if the padding_mode is BorderType.kConstant. If 1 value is provided, it is used for all - /// RGB channels. If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \param[in] padding_mode The method of padding (default=BorderType::kConstant).It can be any of - /// [BorderType::kConstant, BorderType::kEdge, BorderType::kReflect, BorderType::kSymmetric]. - /// - BorderType::kConstant, Fill the border with constant values. - /// - BorderType::kEdge, Fill the border with the last value on the edge. - /// - BorderType::kReflect, Reflect the values on the edge omitting the last value of edge. - /// - BorderType::kSymmetric, Reflect the values on the edge repeating the last value of edge. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomCropWithBBox({224, 224}, {0, 0, 0, 0}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit RandomCropWithBBox(const std::vector &size, const std::vector &padding = {0, 0, 0, 0}, - bool pad_if_needed = false, const std::vector &fill_value = {0, 0, 0}, - BorderType padding_mode = BorderType::kConstant); - - /// \brief Destructor. - ~RandomCropWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly apply histogram equalization on the input image with a given probability. -class DATASET_API RandomEqualize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of equalization, which - /// must be in range of [0, 1] (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomEqualize(0.5); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomEqualize(float prob = 0.5); - - /// \brief Destructor. - ~RandomEqualize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly flip the input image horizontally with a given probability. -class DATASET_API RandomHorizontalFlip final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of flip. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomHorizontalFlip(0.8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomHorizontalFlip(float prob = 0.5); - - /// \brief Destructor. - ~RandomHorizontalFlip() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly flip the input image horizontally with a given probability and adjust bounding boxes accordingly. -class DATASET_API RandomHorizontalFlipWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of flip. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomHorizontalFlipWithBBox(1.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit RandomHorizontalFlipWithBBox(float prob = 0.5); - - /// \brief Destructor. - ~RandomHorizontalFlipWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly invert the input image with a given probability. -class DATASET_API RandomInvert final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of the image being inverted, which - /// must be in range of [0, 1] (default=0.5). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomInvert(0.8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomInvert(float prob = 0.5); - - /// \brief Destructor. - ~RandomInvert() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Add AlexNet-style PCA-based noise to an image. -class DATASET_API RandomLighting final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] alpha A float representing the intensity of the image (default=0.05). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomLighting(0.1); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomLighting(float alpha = 0.05); - - /// \brief Destructor. - ~RandomLighting() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Reduce the number of bits for each color channel randomly. -class DATASET_API RandomPosterize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] bit_range Range of random posterize to compress image. - /// uint8_t vector representing the minimum and maximum bit in range of [1,8] (Default={4, 8}). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomPosterize({4, 8}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomPosterize(const std::vector &bit_range = {4, 8}); - - /// \brief Destructor. - ~RandomPosterize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Resize the input image using a randomly selected interpolation mode. -class DATASET_API RandomResize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the resized image. - /// If the size is a single value, the smaller edge of the image will be resized to this value with - /// the same image aspect ratio. If the size has 2 values, it should be (height, width). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomResize({32, 32}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomResize(const std::vector &size); - - /// \brief Destructor. - ~RandomResize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Resize the input image using a randomly selected interpolation mode and adjust -/// bounding boxes accordingly. -class DATASET_API RandomResizeWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the resized image. - /// If the size is a single value, the smaller edge of the image will be resized to this value with - /// the same image aspect ratio. If the size has 2 values, it should be (height, width). - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomResizeWithBBox({50, 50}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit RandomResizeWithBBox(const std::vector &size); - - /// \brief Destructor. - ~RandomResizeWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop the input image to a random size and aspect ratio. -class DATASET_API RandomResizedCrop final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] scale Range [min, max) of respective size of the original - /// size to be cropped (default=(0.08, 1.0)). - /// \param[in] ratio Range [min, max) of aspect ratio to be cropped - /// (default=(3. / 4., 4. / 3.)). - /// \param[in] interpolation Image interpolation mode (default=InterpolationMode::kLinear). - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \param[in] max_attempts The maximum number of attempts to propose a valid. - /// crop_area (default=10). If exceeded, fall back to use center_crop instead. - /// \note If the input image is more than one, then make sure that the image size is the same. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomResizedCrop({32, 32}, {0.08, 1.0}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomResizedCrop(const std::vector &size, const std::vector &scale = {0.08, 1.0}, - const std::vector &ratio = {3. / 4., 4. / 3.}, - InterpolationMode interpolation = InterpolationMode::kLinear, int32_t max_attempts = 10); - - /// \brief Destructor. - ~RandomResizedCrop() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop the input image to a random size and aspect ratio. -/// If cropped area is out of bbox, the return bbox will be empty. -class DATASET_API RandomResizedCropWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] scale Range [min, max) of respective size of the original - /// size to be cropped (default=(0.08, 1.0)). - /// \param[in] ratio Range [min, max) of aspect ratio to be cropped - /// (default=(3. / 4., 4. / 3.)). - /// \param[in] interpolation Image interpolation mode (default=InterpolationMode::kLinear). - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \param[in] max_attempts The maximum number of attempts to propose a valid - /// crop_area (default=10). If exceeded, fall back to use center_crop instead. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomResizedCropWithBBox({50, 50}, {0.05, 0.5}, {0.2, 0.4}, - /// InterpolationMode::kCubic); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit RandomResizedCropWithBBox(const std::vector &size, const std::vector &scale = {0.08, 1.0}, - const std::vector &ratio = {3. / 4., 4. / 3.}, - InterpolationMode interpolation = InterpolationMode::kLinear, - int32_t max_attempts = 10); - - /// \brief Destructor. - ~RandomResizedCropWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Rotate the image according to parameters. -class DATASET_API RandomRotation final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] degrees A float vector of size 2, representing the starting and ending degrees. - /// \param[in] resample An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \param[in] expand A boolean representing whether the image is expanded after rotation. - /// \param[in] center A float vector of size 2 or empty, representing the x and y center of rotation - /// or the center of the image. - /// \param[in] fill_value A vector representing the value to fill the area outside the transform - /// in the output image. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomRotation({30, 60}, InterpolationMode::kNearestNeighbour); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomRotation(const std::vector °rees, - InterpolationMode resample = InterpolationMode::kNearestNeighbour, bool expand = false, - const std::vector ¢er = {}, const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~RandomRotation() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Choose a random sub-policy from a list to be applied on the input image. A sub-policy is a list of tuples -/// (operation, prob), where operation is a TensorTransform operation and prob is the probability that this -/// operation will be applied. Once a sub-policy is selected, each operation within the sub-policy with be -/// applied in sequence according to its probability. -class DATASET_API RandomSelectSubpolicy final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] policy Vector of sub-policies to choose from, in which the TensorTransform objects are raw pointers. - /// \par Example - /// \code - /// /* Define operations */ - /// auto invert_op(new vision::Invert()); - /// auto equalize_op(new vision::Equalize()); - /// - /// std::vector> policy = {{invert_op, 0.5}, {equalize_op, 0.4}}; - /// vision::RandomSelectSubpolicy random_select_subpolicy_op = vision::RandomSelectSubpolicy({policy}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_select_subpolicy_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomSelectSubpolicy(const std::vector>> &policy); - - /// \brief Constructor. - /// \param[in] policy Vector of sub-policies to choose from, in which the TensorTransform objects are shared pointers. - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr invert_op(new vision::Invert()); - /// std::shared_ptr equalize_op(new vision::Equalize()); - /// std::shared_ptr resize_op(new vision::Resize({15, 15})); - /// - /// auto random_select_subpolicy_op = vision::RandomSelectSubpolicy({ - /// {{invert_op, 0.5}, {equalize_op, 0.4}}, - /// {{resize_op, 0.1}} - /// }); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_select_subpolicy_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomSelectSubpolicy( - const std::vector, double>>> &policy); - - /// \brief Constructor. - /// \param[in] policy Vector of sub-policies to choose from, in which the TensorTransform objects are object pointers. - /// \par Example - /// \code - /// /* Define operations */ - /// vision::Invert invert_op = vision::Invert(); - /// vision::Equalize equalize_op = vision::Equalize(); - /// vision::Resize resize_op = vision::Resize({15, 15}); - /// - /// auto random_select_subpolicy_op = vision::RandomSelectSubpolicy({ - /// {{invert_op, 0.5}, {equalize_op, 0.4}}, - /// {{resize_op, 0.1}} - /// }); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_select_subpolicy_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomSelectSubpolicy( - const std::vector, double>>> &policy); - - /// \brief Destructor. - ~RandomSelectSubpolicy() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Adjust the sharpness of the input image by a fixed or random degree. -class DATASET_API RandomSharpness final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] degrees A float vector of size 2, representing the range of random sharpness - /// adjustment degrees. It should be in (min, max) format. If min=max, then it is a - /// single fixed magnitude operation (default = (0.1, 1.9)). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomSharpness({0.1, 1.5}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomSharpness(const std::vector °rees = {0.1, 1.9}); - - /// \brief Destructor. - ~RandomSharpness() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Invert pixels randomly within a specified range. -class DATASET_API RandomSolarize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] threshold A vector with two elements specifying the pixel range to invert. - /// Threshold values should always be in (min, max) format. - /// If min=max, it will to invert all pixels above min(max). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomSharpness({0, 255}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomSolarize(const std::vector &threshold = {0, 255}); - - /// \brief Destructor. - ~RandomSolarize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly flip the input image vertically with a given probability. -class DATASET_API RandomVerticalFlip final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of flip. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomVerticalFlip(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomVerticalFlip(float prob = 0.5); - - /// \brief Destructor. - ~RandomVerticalFlip() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly flip the input image vertically with a given probability and adjust bounding boxes accordingly. -class DATASET_API RandomVerticalFlipWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] prob A float representing the probability of flip. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::RandomVerticalFlipWithBBox(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit RandomVerticalFlipWithBBox(float prob = 0.5); - - /// \brief Destructor. - ~RandomVerticalFlipWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Reads a file in binary mode. -/// \param[in] filename The path to the file to be read. -/// \param[out] output The binary data. -/// \return The status code. -Status DATASET_API ReadFile(const std::string &filename, mindspore::MSTensor *output); - -/// \brief Read a image file and decode it into one or three channels data. -/// \param[in] filename The path to the file to be read. -/// \param[out] output The Tensor data. -/// \param[in] mode The read mode used for optionally converting the image, can be one of -/// [ImageReadMode::kUNCHANGED, ImageReadMode::kGRAYSCALE, ImageReadMode::kCOLOR]. Default: -/// ImageReadMode::kUNCHANGED. -/// - ImageReadMode::kUNCHANGED, remain the output in the original format. -/// - ImageReadMode::kGRAYSCALE, convert the output into one channel grayscale data. -/// - ImageReadMode::kCOLOR, convert the output into three channels RGB color data. -/// \return The status code. -Status DATASET_API ReadImage(const std::string &filename, mindspore::MSTensor *output, - ImageReadMode mode = ImageReadMode::kUNCHANGED); - -#ifdef ENABLE_FFMPEG -/// \brief Read the video, audio, metadata from a video file. It supports AVI, H264, H265, MOV, MP4, WMV file formats. -/// \param[in] filename The path to the videoe file to be read. -/// \param[out] video_output The video frames of the video file. -/// \param[out] audio_output The audio frames of the video file. -/// \param[out] metadata_output The metadata contains video_fps, audio_fps. -/// \param[in] start_pts The start presentation timestamp of the video. Default: 0.0. -/// \param[in] end_pts The end presentation timestamp of the video. Default: 2147483647.0. -/// \param[in] pts_unit The unit for the timestamps, can be one of ["pts", "sec"]. Default: "pts". -/// \return The status code. -Status DATASET_API ReadVideo(const std::string &filename, mindspore::MSTensor *video_output, - mindspore::MSTensor *audio_output, std::map *metadata_output, - float start_pts = 0.0, float end_pts = 2147483647.0, const std::string &pts_unit = "pts"); - -/// \brief Read the timestamps and frame rate of a video file. It supports AVI, H264, H265, MOV, MP4, WMV files. -/// \param[in] filename The path to the videoe file to be read. -/// \param[out] output The tuple(video_timestamps, video_fps) of the video. -/// \param[in] pts_unit The unit for the timestamps, can be one of ["pts", "sec"]. Default: "pts". -/// \return The status code. -Status DATASET_API ReadVideoTimestamps(const std::string &filename, std::tuple, float> *output, - const std::string &pts_unit = "pts"); -#endif - -/// \brief Crop the given image and zoom to the specified size. -class DATASET_API ResizedCrop final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] top Horizontal ordinate of the upper left corner of the crop image. - /// \param[in] left Vertical ordinate of the upper left corner of the crop image. - /// \param[in] height Height of cropped image. - /// \param[in] width Width of cropped image. - /// \param[in] size A vector representing the output size of the image. - /// If the size is a single value, a squared resized of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \param[in] interpolation Image interpolation mode. Default: InterpolationMode::kLinear. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \note If the input image is more than one, then make sure that the image size is the same. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto resized_crop_op = vision::ResizedCrop(128, 128, 256, 256, {128, 128}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, resized_crop_op}, // operations - /// {"image"}); // input columns - /// \endcode - ResizedCrop(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &size, - InterpolationMode interpolation = InterpolationMode::kLinear); - - /// \brief Destructor. - ~ResizedCrop() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Resize the input image to the given size and adjust bounding boxes accordingly. -class DATASET_API ResizeWithBBox final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size The output size of the resized image. - /// If the size is an integer, smaller edge of the image will be resized to this value with the same image aspect - /// ratio. If the size is a sequence of length 2, it should be (height, width). - /// \param[in] interpolation An enum for the mode of interpolation (default=InterpolationMode::kLinear). - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \par Example - /// \code - /// /* Define operations */ - /// auto random_op = vision::ResizeWithBBox({100, 100}, InterpolationMode::kNearestNeighbour); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({random_op}, // operations - /// {"image", "bbox"}); // input columns - /// \endcode - explicit ResizeWithBBox(const std::vector &size, - InterpolationMode interpolation = InterpolationMode::kLinear); - - /// \brief Destructor. - ~ResizeWithBBox() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Change the format of input tensor from 4-channel RGBA to 3-channel BGR. -class DATASET_API RGBA2BGR final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rgb2bgr_op = vision::RGBA2BGR(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rgb2bgr_op}, // operations - /// {"image"}); // input columns - /// \endcode - RGBA2BGR(); - - /// \brief Destructor. - ~RGBA2BGR() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Change the input 4 channel RGBA tensor to 3 channel RGB. -class DATASET_API RGBA2RGB final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rgba2rgb_op = vision::RGBA2RGB(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rgba2rgb_op}, // operations - /// {"image"}); // input columns - /// \endcode - RGBA2RGB(); - - /// \brief Destructor. - ~RGBA2RGB() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \note Slice the tensor to multiple patches in horizontal and vertical directions. -class DATASET_API SlicePatches final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] num_height The number of patches in vertical direction (default=1). - /// \param[in] num_width The number of patches in horizontal direction (default=1). - /// \param[in] slice_mode An enum for the mode of slice (default=SliceMode::kPad). - /// \param[in] fill_value A value representing the pixel to fill the padding area in right and - /// bottom border if slice_mode is kPad. Then padded tensor could be just sliced to multiple patches (default=0). - /// \note The usage scenerio is suitable to tensor with large height and width. The tensor will keep the same - /// if set both num_height and num_width to 1. And the number of output tensors is equal to num_height*num_width. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto slice_patch_op = vision::SlicePatches(255, 255); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, slice_patch_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit SlicePatches(int32_t num_height = 1, int32_t num_width = 1, SliceMode slice_mode = SliceMode::kPad, - uint8_t fill_value = 0); - - /// \brief Destructor. - ~SlicePatches() override = default; - - protected: - /// \brief Function to convert TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Invert pixels within a specified range. -class DATASET_API Solarize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] threshold A vector with two elements specifying the pixel range to invert. - /// Threshold values should always be in (min, max) format. - /// If min=max, it will to invert all pixels above min(max). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto solarize_op = vision::Solarize({0, 255}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, solarize_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Solarize(const std::vector &threshold); - - /// \brief Destructor. - ~Solarize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Divide the pixel values by 255 and convert from HWC format to CHW format with required datatype. -class DATASET_API ToTensor final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] output_type The type of the output tensor of type mindspore::DataType or String - /// (default=mindspore::DataType::kNumberTypeFloat32). - /// \par Example - /// \code - /// /* Define operations */ - /// auto to_tensor_op = vision::ToTensor(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({to_tensor_op}, // operations - /// {"image"}); // input columns - /// \endcode - ToTensor(); - explicit ToTensor(std::string output_type); - explicit ToTensor(mindspore::DataType output_type); - - /// \brief Destructor. - ~ToTensor() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Dataset-independent data-augmentation with TrivialAugment Wide. -class DATASET_API TrivialAugmentWide final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] num_magnitude_bins The number of different magnitude values. Default: 31. - /// \param[in] interpolation An enum for the mode of interpolation. Default: InterpolationMode::kNearestNeighbour. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// \param[in] fill_value A vector representing the pixel intensity of the borders. Default: {0, 0, 0}. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto trivial_augment_wide_op = vision::TrivialAugmentWide(); - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, trivial_augment_wide_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit TrivialAugmentWide(int32_t num_magnitude_bins = 31, - InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, - const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~TrivialAugmentWide() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Randomly perform transformations, as selected from input transform list, on the input tensor. -class DATASET_API UniformAugment final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] transforms Raw pointer to vector of TensorTransform operations. - /// \param[in] num_ops An integer representing the number of operations to be selected and applied. - /// \par Example - /// \code - /// /* Define operations */ - /// auto resize_op(new vision::Resize({30, 30})); - /// auto random_crop_op(new vision::RandomCrop({28, 28})); - /// auto center_crop_op(new vision::CenterCrop({16, 16})); - /// auto uniform_op(new vision::UniformAugment({random_crop_op, center_crop_op}, 2)); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({resize_op, uniform_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit UniformAugment(const std::vector &transforms, int32_t num_ops = 2); - - /// \brief Constructor. - /// \param[in] transforms Smart pointer to vector of TensorTransform operations. - /// \param[in] num_ops An integer representing the number of operations to be selected and applied. - /// \par Example - /// \code - /// /* Define operations */ - /// std::shared_ptr resize_op(new vision::Resize({30, 30})); - /// std::shared_ptr random_crop_op(new vision::RandomCrop({28, 28})); - /// std::shared_ptr center_crop_op(new vision::CenterCrop({16, 16})); - /// std::shared_ptr uniform_op(new vision::UniformAugment({random_crop_op, center_crop_op}, 2)); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({resize_op, uniform_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit UniformAugment(const std::vector> &transforms, int32_t num_ops = 2); - - /// \brief Constructor. - /// \param[in] transforms Object pointer to vector of TensorTransform operations. - /// \param[in] num_ops An integer representing the number of operations to be selected and applied. - /// \par Example - /// \code - /// /* Define operations */ - /// vision::Resize resize_op = vision::Resize({30, 30}); - /// vision::RandomCrop random_crop_op = vision::RandomCrop({28, 28}); - /// vision::CenterCrop center_crop_op = vision::CenterCrop({16, 16}); - /// vision::UniformAugment uniform_op = vision::UniformAugment({random_crop_op, center_crop_op}, 2); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({resize_op, uniform_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit UniformAugment(const std::vector> &transforms, int32_t num_ops = 2); - - /// \brief Destructor. - ~UniformAugment() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Flip the input image vertically. -class DATASET_API VerticalFlip final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto flip_op = vision::VerticalFlip(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, flip_op}, // operations - /// {"image"}); // input columns - /// \endcode - VerticalFlip(); - - /// \brief Destructor. - ~VerticalFlip() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Write the one dimension uint8 data into a file using binary mode. -/// \param[in] filename The path to the file to be written. -/// \param[in] data The tensor data. -/// \return The status code. -Status DATASET_API WriteFile(const std::string &filename, const mindspore::MSTensor &data); - -/// \brief Write the image data into a JPEG file. -/// \param[in] filename The path to the file to be written. -/// \param[in] image The data tensor. -/// \param[in] quality The quality for JPEG file, in range of [1, 100]. Default: 75. -/// \return The status code. -Status DATASET_API WriteJpeg(const std::string &filename, const mindspore::MSTensor &image, int quality = 75); - -/// \brief Write the image into a PNG file. -/// \param[in] filename The path to the file to be written. -/// \param[in] image The data tensor. -/// \param[in] compression_level The compression level for PNG file, in range of [0, 9]. Default: 6. -/// \return The status code. -Status DATASET_API WritePng(const std::string &filename, const mindspore::MSTensor &image, int compression_level = 6); -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h b/mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h deleted file mode 100644 index 5093ad598..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/vision_ascend.h +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_ASCEND_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_ASCEND_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" - -namespace mindspore { -namespace dataset { -// Transform operations for performing computer vision. -namespace vision { -/* ##################################### API class ########################################### */ - -/// \brief Decode and resize JPEG image using the hardware algorithm of -/// Ascend series chip DVPP module. -class DATASET_API DvppDecodeResizeJpeg final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] resize Parameter vector of two integers for each dimension, with respect to H,W order. - /// \par Example - /// \code - /// /* Define operations */ - /// auto dvpp_op = vision::DvppDecodeResizeJpeg({255, 255}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({dvpp_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit DvppDecodeResizeJpeg(const std::vector &resize); - - /// \brief Destructor. - ~DvppDecodeResizeJpeg() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Decode, resize and crop JPEG image using the hardware algorithm of -/// Ascend series chip DVPP module. -class DATASET_API DvppDecodeResizeCropJpeg final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] crop Parameter vector of two integers for each dimension after final crop, with respect to H,W order. - /// \param[in] resize Parameter vector of two integers for each dimension after resize, with respect to H,W order. - /// \par Example - /// \code - /// /* Define operations */ - /// auto dvpp_op = vision::DvppDecodeResizeCropJpeg({50, 50}, {100, 100}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({dvpp_op}, // operations - /// {"image"}); // input columns - /// \endcode - DvppDecodeResizeCropJpeg(const std::vector &crop, const std::vector &resize); - - /// \brief Destructor. - ~DvppDecodeResizeCropJpeg() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Decode H264/H265 video using the hardware algorithm of -/// DVPP module on Ascend series chip. -class DATASET_API DvppDecodeVideo final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size Parameter vector of two integers for each dimension of input video frames, with respect to H,W - /// order. - /// \param[in] type An enum for the coding protocol of video. - /// - VdecStreamFormat::kH265MainLevel, video coding protocol is H265-main level. - /// - VdecStreamFormat::kH264BaselineLevel, video coding protocol is H264-baseline level. - /// - VdecStreamFormat::kH264MainLevel, video coding protocol is H264-main level. - /// - VdecStreamFormat::kH264HighLevel, video coding protocol is H264-high level. - /// \param[in] out_format An enum for the format of output image (default=VdecOutputFormat::kYUV_SEMIPLANAR_420). - /// - VdecOutputFormat::kYUV_SEMIPLANAR_420, format of output image is YUV420SP NV12 8bit. - /// - VdecOutputFormat::kYVU_SEMIPLANAR_420, format of output image is YUV420SP NV21 8bit. - /// \param[in] output The output path of the decoded images corresponds to video frames. - /// \par Example - /// \code - /// namespace ds = mindspore::dataset; - /// - /// /* Define operations */ - /// std::shared_ptr dvpp_decode(new ds::vision::DvppDecodeVideo({1080, 1920}, - /// ds::VdecStreamFormat::kH265MainLevel)); - /// - /// /* define preprocessor */ - /// ds::Execute preprocessor({dvpp_decode}, ds::MapTargetDevice::kCpu, 0); - /// - /// \endcode - - DvppDecodeVideo(const std::vector &size, VdecStreamFormat type, - VdecOutputFormat out_format = VdecOutputFormat::kYuvSemiplanar420, - const std::string &output = "./output") - : DvppDecodeVideo(size, type, out_format, StringToChar(output)) {} - - /// \brief Constructor. - /// \param[in] size Parameter vector of two integers for each dimension of input video frames, with respect to H,W - /// order. - /// \param[in] type An enum for the coding protocol of video. - /// - VdecStreamFormat::kH265MainLevel, video coding protocol is H265-main level. - /// - VdecStreamFormat::kH264BaselineLevel, video coding protocol is H264-baseline level. - /// - VdecStreamFormat::kH264MainLevel, video coding protocol is H264-main level. - /// - VdecStreamFormat::kH264HighLevel, video coding protocol is H264-high level. - /// \param[in] output The output path of the decoded images corresponds to video frames. - /// \par Example - /// \code - /// namespace ds = mindspore::dataset; - /// - /// /* Define operations */ - /// std::shared_ptr dvpp_decode(new ds::vision::DvppDecodeVideo({1080, 1920})); - /// - /// /* define preprocessor */ - /// ds::Execute preprocessor({dvpp_decode}, ds::MapTargetDevice::kCpu, 0); - /// - /// \endcode - - DvppDecodeVideo(const std::vector &size, VdecStreamFormat type, const std::string &output = "./output") - : DvppDecodeVideo(size, type, VdecOutputFormat::kYuvSemiplanar420, StringToChar(output)) {} - - DvppDecodeVideo(const std::vector &size, VdecStreamFormat type, VdecOutputFormat out_format, - const std::vector &output); - - /// \brief Destructor. - ~DvppDecodeVideo() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Decode PNG image using the hardware algorithm of -/// Ascend series chip DVPP module. -class DATASET_API DvppDecodePng final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto dvpp_op = vision::DvppDecodePng(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({dvpp_op}, // operations - /// {"image"}); // input columns - /// \endcode - DvppDecodePng(); - - /// \brief Destructor. - ~DvppDecodePng() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_ASCEND_H_ diff --git a/mindspore-lite/minddata/dataset/include/dataset/vision_lite.h b/mindspore-lite/minddata/dataset/include/dataset/vision_lite.h deleted file mode 100644 index 1fafb68cb..000000000 --- a/mindspore-lite/minddata/dataset/include/dataset/vision_lite.h +++ /dev/null @@ -1,625 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_LITE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_LITE_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "include/dataset/constants.h" -#include "include/dataset/transforms.h" - -namespace mindspore { -namespace dataset { -// Transform operations for performing computer vision. -namespace vision { -// Forward Declarations -class RotateOperation; - -/// \brief Apply affine transform on the input image. -class DATASET_API Affine final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] degrees The degrees to rotate the image. - /// \param[in] translation The values representing vertical and horizontal translation (default = {0.0, 0.0}). - /// The first value represents the x axis translation while the second represents the y axis translation. - /// \param[in] scale The scaling factor for the image (default = 0.0). - /// \param[in] shear A float vector of size 2, representing the shear degrees (default = {0.0, 0.0}). - /// \param[in] interpolation An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation (Only supports this mode in Lite). - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// \param[in] fill_value A vector representing the value to fill the area outside the transformation - /// in the output image. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto affine_op = vision::Affine(30, {0.0, 0.0}, 0.8); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, affine_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Affine(float_t degrees, const std::vector &translation = {0.0, 0.0}, float scale = 0.0, - const std::vector &shear = {0.0, 0.0}, - InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, - const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~Affine() override = default; - - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop the input image at the center to the given size. -class DATASET_API CenterCrop final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the cropped image. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto crop_op = vision::CenterCrop({32, 32}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, crop_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit CenterCrop(const std::vector &size); - - /// \brief Destructor. - ~CenterCrop() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Crop an image based on location and crop size. -class DATASET_API Crop final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] coordinates Starting location of crop. Must be a vector of two values, in the form of {x_coor, y_coor}. - /// \param[in] size Size of the cropped area. - /// If the size is a single value, a squared crop of size (size, size) is returned. - /// If the size has 2 values, it should be (height, width). - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto crop_op = vision::Crop({0, 0}, {32, 32}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, crop_op}, // operations - /// {"image"}); // input columns - /// \endcode - Crop(const std::vector &coordinates, const std::vector &size); - - /// \brief Destructor. - ~Crop() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Decode the input image in RGB mode. -class DATASET_API Decode final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] rgb A boolean indicating whether to decode the image in RGB mode or not. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Decode(bool rgb = true); - - /// \brief Destructor. - ~Decode() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Blur the input image with the specified Gaussian kernel. -class DATASET_API GaussianBlur final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] kernel_size A vector of Gaussian kernel size for width and height. The value must be positive and odd. - /// \param[in] sigma A vector of Gaussian kernel standard deviation sigma for width and height. The values must be - /// positive. Using default value 0 means to calculate the sigma according to the kernel size. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto gaussian_op = vision::GaussianBlur({7, 7}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, gaussian_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit GaussianBlur(const std::vector &kernel_size, const std::vector &sigma = {0., 0.}); - - /// \brief Destructor. - ~GaussianBlur() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Transpose the input image; shape (H, W, C) to shape (C, H, W). -class DATASET_API HWC2CHW final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({std::make_shared(), - /// std::make_shared()}, // operations - /// {"image"}); // input columns - /// \endcode - HWC2CHW(); - - /// \brief Destructor. - ~HWC2CHW() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Normalize the input image with respect to mean and standard deviation. -class DATASET_API Normalize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] mean A vector of mean values for each channel, with respect to channel order. - /// The mean values must be in range [0.0, 255.0]. - /// \param[in] std A vector of standard deviations for each channel, with respect to channel order. - /// The standard deviation values must be in range (0.0, 255.0]. - /// \param[in] is_hwc A boolean to indicate whether the input image is in HWC format (true) or CHW - /// format (false) (Lite only supports true) (default = true). - - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto normalize_op = vision::Normalize({128, 128, 128}, {1.0, 1.0, 1.0}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, normalize_op}, // operations - /// {"image"}); // input columns - /// \endcode - Normalize(const std::vector &mean, const std::vector &std, bool is_hwc = true); - - /// \brief Destructor. - ~Normalize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Pad the image according to padding parameters. -class DATASET_API Pad final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] padding A vector representing the number of pixels to pad the image. - /// If the vector has one value, it pads all sides of the image with that value. - /// If the vector has two values, it pads left and right with the first and - /// top and bottom with the second value. - /// If the vector has four values, it pads left, top, right, and bottom with - /// those values respectively. - /// \param[in] fill_value A vector representing the pixel intensity of the borders. Only valid if the - /// padding_mode is BorderType.kConstant. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \param[in] padding_mode The method of padding (default=BorderType.kConstant). - /// Can be any of - /// [BorderType.kConstant, BorderType.kEdge, BorderType.kReflect, BorderType.kSymmetric] - /// - BorderType.kConstant, means it fills the border with constant values - /// - BorderType.kEdge, means it pads with the last value on the edge - /// - BorderType.kReflect, means it reflects the values on the edge omitting the last value of edge - /// - BorderType.kSymmetric, means it reflects the values on the edge repeating the last value of edge - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto pad_op = vision::Pad({10, 10, 10, 10}, {255, 255, 255}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, pad_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Pad(const std::vector &padding, const std::vector &fill_value = {0}, - BorderType padding_mode = BorderType::kConstant); - - /// \brief Destructor. - ~Pad() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Apply a Random Affine transformation on the input image in RGB or Greyscale mode. -class DATASET_API RandomAffine final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] degrees A float vector of size 2, representing the starting and ending degree. - /// \param[in] translate_range A float vector of size 2 or 4, representing percentages of translation on x and y axes. - /// If the size is 2, (min_dx, max_dx, 0, 0). - /// If the size is 4, (min_dx, max_dx, min_dy, max_dy), - /// all values are in range [-1, 1]. - /// \param[in] scale_range A float vector of size 2, representing the starting and ending scales in the range. - /// \param[in] shear_ranges A float vector of size 2 or 4, representing the starting and ending shear degrees - /// vertically and horizontally. - /// If the size is 2, (min_shear_x, max_shear_x, 0, 0), - /// if the size is 4, (min_shear_x, max_shear_x, min_shear_y, max_shear_y). - /// \param[in] interpolation An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation (Only supports this mode in Lite). - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// \param[in] fill_value A vector representing the value to fill the area outside the transform - /// in the output image. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G and B channels respectively. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto random_op = vision::RandomAffine({45, 90}); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, random_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit RandomAffine(const std::vector °rees, - const std::vector &translate_range = {0.0, 0.0, 0.0, 0.0}, - const std::vector &scale_range = {1.0, 1.0}, - const std::vector &shear_ranges = {0.0, 0.0, 0.0, 0.0}, - InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, - const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~RandomAffine() override = default; - - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Rescale the pixel value of input image. -class DATASET_API Rescale final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] rescale Rescale factor. - /// \param[in] shift Shift factor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rescale_op = vision::Rescale(1.0, 0.0); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rescale_op}, // operations - /// {"image"}); // input columns - /// \endcode - Rescale(float rescale, float shift); - - /// \brief Destructor. - ~Rescale() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Resize the input image to the given size. -class DATASET_API Resize final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] size A vector representing the output size of the resized image. - /// If the size is a single value, the image will be resized to this value with - /// the same image aspect ratio. If the size has 2 values, it should be (height, width). - /// \param[in] interpolation An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation (Only supports this mode in Lite). - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// - InterpolationMode::kCubicPil, Interpolation method is bicubic interpolation like implemented in pillow. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto resize_op = vision::Resize({224, 224}, InterpolationMode::kLinear); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, resize_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Resize(const std::vector &size, InterpolationMode interpolation = InterpolationMode::kLinear); - - /// \brief Destructor. - ~Resize() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - std::shared_ptr Parse(const MapTargetDevice &env) override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief Keep the original picture ratio and fills the rest. -class DATASET_API ResizePreserveAR final : public TensorTransform { - public: - /// \brief Constructor. - /// \param[in] height The height of image output value after resizing. - /// \param[in] width The width of image output value after resizing. - /// \param[in] img_orientation optional rotation angle. - /// - img_orientation = 1, Rotate 0 degree. - /// - img_orientation = 2, Rotate 0 degree and apply horizontal flip. - /// - img_orientation = 3, Rotate 180 degree. - /// - img_orientation = 4, Rotate 180 degree and apply horizontal flip. - /// - img_orientation = 5, Rotate 90 degree and apply horizontal flip. - /// - img_orientation = 6, Rotate 90 degree. - /// - img_orientation = 7, Rotate 270 degree and apply horizontal flip. - /// - img_orientation = 8, Rotate 270 degree. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto resize_op = vision::ResizePreserveAR(224, 224); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, resize_op}, // operations - /// {"image"}); // input columns - /// \endcode - ResizePreserveAR(int32_t height, int32_t width, int32_t img_orientation = 0); - - /// \brief Destructor. - ~ResizePreserveAR() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - struct Data; - std::shared_ptr data_; -}; - -/// \brief RGB2BGR TensorTransform. -/// \note Convert the format of input image from RGB to BGR. -class DATASET_API RGB2BGR final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rgb2bgr_op = vision::RGB2BGR(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rgb2bgr_op}, // operations - /// {"image"}); // input columns - /// \endcode - RGB2BGR() = default; - - /// \brief Destructor. - ~RGB2BGR() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief RGB2GRAY TensorTransform. -/// \note Convert RGB image or color image to grayscale image. -/// \brief Convert a RGB image or color image to a grayscale one. -class DATASET_API RGB2GRAY final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rgb2gray_op = vision::RGB2GRAY(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rgb2gray_op}, // operations - /// {"image"}); // input columns - /// \endcode - RGB2GRAY() = default; - - /// \brief Destructor. - ~RGB2GRAY() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; - -/// \brief Rotate the input image according to parameters. -class DATASET_API Rotate final : public TensorTransform { - public: - /// \brief Constructor. - /// \note This api is only used in Lite, the interpolation mode is bilinear. - /// \param[in] angle_id The fix rotation angle. - /// - FixRotationAngle::k0Degree = 1, Rotate 0 degree. - /// - FixRotationAngle::k0DegreeAndMirror = 2, Rotate 0 degree and apply horizontal flip. - /// - FixRotationAngle::k180Degree = 3, Rotate 180 degree. - /// - FixRotationAngle::k180DegreeAndMirror = 4, Rotate 180 degree and apply horizontal flip. - /// - FixRotationAngle::k90DegreeAndMirror = 5, Rotate 90 degree and apply horizontal flip. - /// - FixRotationAngle::k90Degree = 6, Rotate 90 degree. - /// - FixRotationAngle::k270DegreeAndMirror = 7, Rotate 270 degree and apply horizontal flip. - /// - FixRotationAngle::k270Degree = 8, Rotate 270 degree. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rotate_op = vision::Rotate(FixRotationAngle::k90Degree); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rotate_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Rotate(FixRotationAngle angle_id = FixRotationAngle::k0Degree); - - /// \brief Constructor. - /// \param[in] degrees A float value, representing the rotation degrees. - /// \param[in] resample An enum for the mode of interpolation. - /// - InterpolationMode::kLinear, Interpolation method is blinear interpolation. - /// - InterpolationMode::kNearestNeighbour, Interpolation method is nearest-neighbor interpolation. - /// - InterpolationMode::kCubic, Interpolation method is bicubic interpolation. - /// - InterpolationMode::kArea, Interpolation method is pixel area interpolation. - /// \param[in] expand A boolean representing whether the image is expanded after rotation. - /// \param[in] center A float vector of size 2 or empty, representing the x and y center of rotation - /// or the center of the image. - /// \param[in] fill_value A vector representing the value to fill the area outside the transform - /// in the output image. If 1 value is provided, it is used for all RGB channels. - /// If 3 values are provided, it is used to fill R, G, B channels respectively. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto rotate_op = vision::Rotate(90); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, rotate_op}, // operations - /// {"image"}); // input columns - /// \endcode - explicit Rotate(float degrees, InterpolationMode resample = InterpolationMode::kNearestNeighbour, bool expand = false, - const std::vector ¢er = {}, const std::vector &fill_value = {0, 0, 0}); - - /// \brief Destructor. - ~Rotate() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; - - private: - std::shared_ptr op_; - struct Data; - std::shared_ptr data_; -}; - -/// \brief Swap the red and blue channels of the input image. -class DATASET_API SwapRedBlue final : public TensorTransform { - public: - /// \brief Constructor. - /// \par Example - /// \code - /// /* Define operations */ - /// auto decode_op = vision::Decode(); - /// auto swap_red_blue_op = vision::SwapRedBlue(); - /// - /// /* dataset is an instance of Dataset object */ - /// dataset = dataset->Map({decode_op, swap_red_blue_op}, // operations - /// {"image"}); // input columns - /// \endcode - SwapRedBlue(); - - /// \brief Destructor. - ~SwapRedBlue() override = default; - - protected: - /// \brief The function to convert a TensorTransform object into a TensorOperation object. - /// \return Shared pointer to TensorOperation object. - std::shared_ptr Parse() override; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_VISION_LITE_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/c_func_op.cc b/mindspore-lite/minddata/dataset/kernels/c_func_op.cc deleted file mode 100644 index c025dc2b8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/c_func_op.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/c_func_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status CFuncOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - try { - *output = c_func_ptr_(input); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Error raised, " + std::string(e.what())); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/c_func_op.h b/mindspore-lite/minddata/dataset/kernels/c_func_op.h deleted file mode 100644 index 38484216c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/c_func_op.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_C_FUNC_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_C_FUNC_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class CFuncOp : public TensorOp { - public: - explicit CFuncOp(const std::function &func) : c_func_ptr_(func) {} - - ~CFuncOp() override = default; - - uint32_t NumInput() override { return 0; } - - uint32_t NumOutput() override { return 0; } - - // Calls c_func_ptr and returns the result - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kCFuncOp; } - - private: - std::function c_func_ptr_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_C_FUNC_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/CMakeLists.txt b/mindspore-lite/minddata/dataset/kernels/data/CMakeLists.txt deleted file mode 100644 index d356088b2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -file(GLOB_RECURSE _CURRENT_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") -set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_MD) -if(NOT (CMAKE_SYSTEM_NAME MATCHES "Windows")) - set(ABSL_DEPEND_FILES - parse_example_op.cc) -endif() -add_library(kernels-data OBJECT - concatenate_op.cc - data_utils.cc - duplicate_op.cc - fill_op.cc - mask_op.cc - one_hot_op.cc - pad_end_op.cc - slice_op.cc - to_float16_op.cc - type_cast_op.cc - unique_op.cc - ${ABSL_DEPEND_FILES} - ) diff --git a/mindspore-lite/minddata/dataset/kernels/data/compose_op.cc b/mindspore-lite/minddata/dataset/kernels/data/compose_op.cc deleted file mode 100644 index bb19aa4ed..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/compose_op.cc +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status ComposeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - std::vector in_shapes = inputs; - for (auto &op : ops_) { - RETURN_IF_NOT_OK(op->OutputShape(in_shapes, outputs)); - in_shapes = std::move(outputs); // outputs become empty after move - } - outputs = std::move(in_shapes); - return Status::OK(); -} - -Status ComposeOp::OutputType(const std::vector &inputs, std::vector &outputs) { - std::vector in_types = inputs; - for (auto &op : ops_) { - RETURN_IF_NOT_OK(op->OutputType(in_types, outputs)); - in_types = std::move(outputs); // outputs become empty after move - } - outputs = std::move(in_types); - return Status::OK(); -} - -Status ComposeOp::Compute(const TensorRow &inputs, TensorRow *outputs) { - IO_CHECK_VECTOR(inputs, outputs); - CHECK_FAIL_RETURN_UNEXPECTED(!ops_.empty(), "Compose: transform list should not be empty."); - TensorRow in_rows = inputs; - for (auto &op : ops_) { - RETURN_IF_NOT_OK(op->Compute(in_rows, outputs)); - in_rows = std::move(*outputs); // after move, *outputs become empty - } - (*outputs) = std::move(in_rows); - return Status::OK(); -} - -ComposeOp::ComposeOp(const std::vector> &ops) : ops_(ops) {} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/compose_op.h b/mindspore-lite/minddata/dataset/kernels/data/compose_op.h deleted file mode 100644 index 61d199c16..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/compose_op.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_COMPOSE_OP_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_COMPOSE_OP_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class ComposeOp : public TensorOp { - public: - /// constructor - /// \param[in] ops list of TensorOps to compose into 1 TensorOp - explicit ComposeOp(const std::vector> &ops); - - /// default destructor - ~ComposeOp() override = default; - - /// return the number of inputs the first tensorOp in compose takes - /// \return number of input tensors - uint32_t NumInput() override { return ops_.front()->NumInput(); } - - /// return the number of outputs the last tensorOp in compose produces - /// \return number of output tensors - uint32_t NumOutput() override { return ops_.back()->NumOutput(); } - - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - /// \param[in] input - /// \param[out] output - /// \return Status code - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kComposeOp; } - - // Currently maybe 910b dvpp ops & cpu ops mixed used - bool IsMixedOps() { - bool have_dvpp = false; - bool have_cpu = false; - for (auto &item : ops_) { - if (item->IsDvppOp()) { - have_dvpp = true; - } else { - have_cpu = true; - } - } - - if (have_dvpp && have_cpu) { - MS_LOG(ERROR) << "Currently, it is not supported to mix DVPP transforms with CPU transforms in Compose."; - return true; - } - return false; - } - - // Check whether compose contains dvpp ops. - virtual bool IsDvppOp() { - bool have_dvpp = false; - for (auto &item : ops_) { - if (item->IsDvppOp()) { - have_dvpp = true; - break; - } - } - - if (have_dvpp) { - return true; - } - return false; - } - - std::vector> GetOps() { return ops_; } - - private: - std::vector> ops_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_COMPOSE_OP_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.cc b/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.cc deleted file mode 100644 index 4eff0249b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.cc +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status ConcatenateOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(Concatenate(input, output, axis_, prepend_, append_)); - return Status::OK(); -} - -Status ConcatenateOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Concatenate: inputs can not be empty."); - - std::vector inputs_copy; - inputs_copy.push_back(inputs[0].Squeeze()); - - CHECK_FAIL_RETURN_UNEXPECTED(inputs.at(0).Rank() == 1, - "Concatenate: only 1D input supported, got rank:" + std::to_string(inputs.at(0).Rank())); - - outputs.clear(); - dsize_t output_shape = 0; - output_shape = output_shape + inputs.at(0).NumOfElements(); - if (prepend_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(prepend_->shape().Rank() == 1, "Concatenate: only 1D prepend supported, got rank: " + - std::to_string(prepend_->shape().Rank())); - CHECK_FAIL_RETURN_UNEXPECTED( - (std::numeric_limits::max() - output_shape) > prepend_->shape().NumOfElements(), - "Concatenate: append parameter is too large to pend."); - output_shape = output_shape + prepend_->shape().NumOfElements(); - } - if (append_ != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED(append_->shape().Rank() == 1, "Concatenate: only 1D append supported, got rank: " + - std::to_string(append_->shape().Rank())); - CHECK_FAIL_RETURN_UNEXPECTED( - (std::numeric_limits::max() - output_shape) > append_->shape().NumOfElements(), - "Concatenate: append parameter is too large to pend, got: " + std::to_string(append_->shape().NumOfElements())); - output_shape = output_shape + append_->shape().NumOfElements(); - } - - (void)outputs.emplace_back(std::vector{output_shape}); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h b/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h deleted file mode 100644 index 2d440ad1b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_CONCATENATE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_CONCATENATE_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class ConcatenateOp : public TensorOp { - public: - /// Constructor to ConcatenateOp. - /// @param int8_t axis - axis to concatenate tensors along. - /// @param std::shared_ptr prepend - prepend tensor. - /// @param std::shared_ptr append -append tensor. - ConcatenateOp(int8_t axis, std::shared_ptr prepend, std::shared_ptr append) - : axis_(axis), prepend_(std::move(prepend)), append_(std::move(append)) {} - - ~ConcatenateOp() override = default; - - /// Compute method allowing multiple tensors as inputs - /// @param TensorRow &input - input tensor rows - /// @param TensorRow *output - output tensor rows - Status Compute(const TensorRow &input, TensorRow *output) override; - - /// Compute tensor output shape - /// @param std::vector &inputs - vector of input tensor shapes - /// @param std::vector &inputs, std::vector &outputs) override; - - /// Number of inputs the tensor operation accepts - uint32_t NumInput() override { return 0; } - - std::string Name() const override { return kConcatenateOp; } - - private: - int8_t axis_; - std::shared_ptr prepend_; - std::shared_ptr append_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CONCATENATE_OP_H diff --git a/mindspore-lite/minddata/dataset/kernels/data/data_utils.cc b/mindspore-lite/minddata/dataset/kernels/data/data_utils.cc deleted file mode 100644 index 58860e36f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/data_utils.cc +++ /dev/null @@ -1,900 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/core/pybind_support.h" -#endif -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -template -Status OneHotEncodingImpl(const std::shared_ptr &input, std::shared_ptr *output, dsize_t num_classes, - int64_t index, double smoothing_rate) { - RETURN_UNEXPECTED_IF_NULL(output); - T class_idx; - if (input->Rank() == 0) { - RETURN_IF_NOT_OK(input->GetItemAt(&class_idx, {})); - } else { - RETURN_IF_NOT_OK(input->GetItemAt(&class_idx, {index})); - } - if (class_idx >= static_cast(num_classes)) { - RETURN_STATUS_UNEXPECTED("OneHot: index values should not bigger than num classes: " + std::to_string(num_classes) + - ", but got: " + std::to_string(class_idx)); - } - if ((*output)->type().IsInt()) { - RETURN_IF_NOT_OK((*output)->SetItemAt({index, static_cast(class_idx)}, 1)); - } else if ((*output)->type() == DataType::DE_FLOAT64) { - auto itr = (*output)->begin(); - auto end = (*output)->end(); - double val = smoothing_rate / static_cast(num_classes); - for (; itr != end; itr++) { - *itr = val; - } - double value = (1. - smoothing_rate) + val; - RETURN_IF_NOT_OK((*output)->SetItemAt({index, static_cast(class_idx)}, value)); - } else { - RETURN_STATUS_UNEXPECTED("OneHot: signed input case only supports signed int as input but got:" + - input->type().ToString()); - } - return Status::OK(); -} - -Status OneHotEncoding(const std::shared_ptr &input, std::shared_ptr *output, dsize_t num_classes, - double smoothing_rate) { - RETURN_UNEXPECTED_IF_NULL(output); - input->Squeeze(); - - CHECK_FAIL_RETURN_UNEXPECTED(input->Rank() <= 1, - "OneHot: Only support scalar or 1D input, got rank: " + std::to_string(input->Rank())); - CHECK_FAIL_RETURN_UNEXPECTED(input->type().IsInt(), - "OneHot: Only support input of int type, but got: " + input->type().ToString()); - try { - dsize_t num_elements = 1; - if (input->Rank() == 1) { - num_elements = input->shape()[0]; - } - TensorShape out_shape({num_elements, num_classes}); - std::shared_ptr out; - mindspore::dataset::DataType type = input->type(); - if (abs(smoothing_rate) > std::numeric_limits::epsilon()) { - type = DataType(DataType::DE_FLOAT64); - } - RETURN_IF_NOT_OK(Tensor::CreateEmpty(out_shape, type, &out)); - RETURN_IF_NOT_OK(out->Zero()); - for (dsize_t i = 0; i < num_elements; ++i) { - if (input->type() == DataType::DE_INT8) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_INT16) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_INT32) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_INT64) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_UINT8) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_UINT16) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_UINT32) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else if (input->type() == DataType::DE_UINT64) { - RETURN_IF_NOT_OK(OneHotEncodingImpl(input, &out, num_classes, i, smoothing_rate)); - } else { - RETURN_STATUS_UNEXPECTED("OneHot: OneHot only supports input of int type, but got:" + input->type().ToString()); - } - } - out->Squeeze(); - *output = out; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("OneHot: Unexpected errors occurred: " + std::string(e.what())); - } -} - -Status FillHelper(const std::shared_ptr &input, std::shared_ptr *out, - const std::shared_ptr &fill_output) { - RETURN_UNEXPECTED_IF_NULL(out); - const DataType &input_type = input->type(); - const TensorShape &input_shape = input->shape(); - switch (input_type.value()) { - case DataType::DE_BOOL: { - bool value = false; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_INT8: { - int8_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_UINT8: { - uint8_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_UINT16: { - uint16_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_INT16: { - int16_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_UINT32: { - uint32_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_INT32: { - int32_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_UINT64: { - uint64_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_INT64: { - int64_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_FLOAT16: { - int64_t value = 0; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_FLOAT32: { - float value = 0.; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_FLOAT64: { - double value = 0.; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&value, {})); - RETURN_IF_NOT_OK((*out)->Fill(value)); - break; - } - case DataType::DE_STRING: - case DataType::DE_BYTES: { - std::vector strings; - std::string_view fill_string_view; - RETURN_IF_NOT_OK(fill_output->GetItemAt(&fill_string_view, {})); - std::string fill_string = std::string(fill_string_view); - for (int i = 0; i < input_shape.NumOfElements(); i++) { - (void)strings.emplace_back(fill_string); - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(strings, input_shape, DataType(input_type.value()), out)); - break; - } - default: - RETURN_STATUS_UNEXPECTED("Fill: invalid data type, filling values into tensors of type " + input_type.ToString() + - " is not supported."); - break; - } - return Status::OK(); -} - -Status Fill(const std::shared_ptr &input, std::shared_ptr *output, - const std::shared_ptr &fill_value) { - RETURN_UNEXPECTED_IF_NULL(output); - const DataType &fill_type = fill_value->type(); - const DataType &input_type = input->type(); - const TensorShape &input_shape = input->shape(); - - if (fill_type.IsString() || input_type.IsString()) { - CHECK_FAIL_RETURN_UNEXPECTED( - fill_type == input_type, - "Fill: fill_value and the input tensor must be of the same data type when involving strings " - "or bytes, but got fill_value data type " + - fill_type.ToString() + " and input tensor data type " + input_type.ToString()); - } - - CHECK_FAIL_RETURN_UNEXPECTED( - fill_value->shape() == TensorShape({}), - "Fill: the shape of fill_value is not a scalar, got shape:" + fill_value->shape().ToString()); - - std::shared_ptr out, fill_output; - - if (input_type.IsNumeric() && fill_type.IsNumeric() && input_type != fill_type) { - RETURN_IF_NOT_OK(TypeCast(fill_value, &fill_output, input_type)); - } else { - fill_output = fill_value; - } - - if (input_type.IsNumeric()) { - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input_shape, input_type, &out)); - } - RETURN_IF_NOT_OK(FillHelper(input, &out, fill_output)); - *output = out; - return Status::OK(); -} - -template -void Cast(const std::shared_ptr &input, std::shared_ptr *output) { - auto in_itr = input->begin(); - auto out_itr = (*output)->begin(); - auto out_end = (*output)->end(); - - for (; out_itr != out_end; ++in_itr, ++out_itr) { - *out_itr = static_cast(*in_itr); - } -} - -template -void CastFrom(const std::shared_ptr &input, std::shared_ptr *output) { - switch ((*output)->type().value()) { - case DataType::DE_BOOL: - Cast(input, output); - break; - case DataType::DE_INT8: - Cast(input, output); - break; - case DataType::DE_UINT8: - Cast(input, output); - break; - case DataType::DE_INT16: - Cast(input, output); - break; - case DataType::DE_UINT16: - Cast(input, output); - break; - case DataType::DE_INT32: - Cast(input, output); - break; - case DataType::DE_UINT32: - Cast(input, output); - break; - case DataType::DE_INT64: - Cast(input, output); - break; - case DataType::DE_UINT64: - Cast(input, output); - break; - case DataType::DE_FLOAT16: - Cast(input, output); - break; - case DataType::DE_FLOAT32: - Cast(input, output); - break; - case DataType::DE_FLOAT64: - Cast(input, output); - break; - case DataType::DE_UNKNOWN: - default: - MS_LOG(ERROR) << "TypeCast: Casting to type " + (*output)->type().ToString() + " is valid, supported datatype: " + - "[bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float16, float32, float64]."; - break; - } -} - -// Type cast operator -Status TypeCast(const std::shared_ptr &input, std::shared_ptr *output, const DataType &data_type) { - RETURN_UNEXPECTED_IF_NULL(output); - switch (input->type().value()) { - case DataType::DE_BOOL: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_INT8: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_UINT8: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_INT16: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_UINT16: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_INT32: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_UINT32: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_INT64: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_UINT64: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_FLOAT16: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_FLOAT32: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_FLOAT64: - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), data_type, output)); - CastFrom(input, output); - break; - case DataType::DE_STRING: - if (data_type == DataType::DE_STRING) { - *output = input; - break; - } else { - RETURN_STATUS_UNEXPECTED("TypeCast: TypeCast does not support cast from string to " + data_type.ToString()); - } - case DataType::DE_BYTES: - if (data_type == DataType::DE_BYTES) { - *output = input; - break; - } else { - RETURN_STATUS_UNEXPECTED("TypeCast: TypeCast does not support cast from bytes to " + data_type.ToString()); - } - case DataType::DE_UNKNOWN: - default: - // sanity check, unreachable code. - RETURN_STATUS_UNEXPECTED( - "TypeCast: Typecast does not support input with type " + input->type().ToString() + ", supported datatype: " + - "[bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float16, float32, float64]."); - } - return Status::OK(); -} - -Status ToFloat16(const std::shared_ptr &input, std::shared_ptr *output) { - // initiate new tensor for type cast - DataType new_type = DataType("float16"); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), new_type, output)); - - auto in_itr = input->begin(); - auto in_end = input->end(); - auto out_itr = (*output)->begin(); - auto out_end = (*output)->end(); - - for (; (in_itr != in_end) && (out_itr != out_end); ++in_itr, ++out_itr) { - float element = *in_itr; - float float16_max = static_cast(std::numeric_limits::max()); - float float16_min = static_cast(std::numeric_limits::lowest()); - if (element > float16_max || element < float16_min) { - RETURN_STATUS_UNEXPECTED("ToFloat16: value " + std::to_string(element) + - "in input data is outside of valid float16 range [" + std::to_string(float16_max) + - ", " + std::to_string(float16_min) + "]."); - } - - *out_itr = float16(*in_itr); - } - - return Status::OK(); -} - -Status PadEnd(const std::shared_ptr &src, std::shared_ptr *dst, const std::vector &pad_shape, - const std::shared_ptr &pad_val) { - if (pad_val == nullptr) { - if (src->type().IsNumeric()) { - return PadEndNumeric(src, dst, pad_shape, 0.0); - } else { - return PadEndString(src, dst, pad_shape, ""); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(src->type().IsNumeric() == pad_val->type().IsNumeric(), - "PadEnd: can not pad numeric and string tensors together, but got: " + - pad_val->type().ToString() + " and " + src->type().ToString() + "."); - if (pad_val->type().IsNumeric()) { - std::shared_ptr float_pad_value; - RETURN_IF_NOT_OK(TypeCast(pad_val, &float_pad_value, DataType(DataType::DE_FLOAT32))); - float val = 0.; - RETURN_IF_NOT_OK(float_pad_value->GetItemAt(&val, {})); - return PadEndNumeric(src, dst, pad_shape, val); - } else { - CHECK_FAIL_RETURN_UNEXPECTED(src->type() == pad_val->type(), - "PadEnd: can not pad string and byte tensors together, but got: " + - pad_val->type().ToString() + " and " + src->type().ToString() + "."); - std::string_view val; - RETURN_IF_NOT_OK(pad_val->GetItemAt(&val, {})); - return PadEndString(src, dst, pad_shape, std::string(val)); - } -} - -Status PadEndNumeric(const std::shared_ptr &src, std::shared_ptr *dst, - const std::vector &pad_shape, float pad_val) { - CHECK_FAIL_RETURN_UNEXPECTED(src != nullptr && dst != nullptr, "PadEnd: input or output can't be nullptr"); - if (src->Rank() == 0 || src->shape().AsVector() == pad_shape) { - (*dst) = src; // if no padding, copy the pointer - } else { - CHECK_FAIL_RETURN_UNEXPECTED(src->Rank() == pad_shape.size(), - "PadEnd: invalid pad shape, as rank of input is: " + std::to_string(src->Rank()) + - ", and rank of pad value: " + std::to_string(pad_shape.size())); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape(pad_shape), src->type(), dst)); - auto tensor_type = src->type().value(); - if (std::fabs(pad_val) <= std::numeric_limits::epsilon()) { // if pad with zero, don't care what type it is - RETURN_IF_NOT_OK((*dst)->Zero()); - } else if (tensor_type == DataType::DE_INT8) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_BOOL) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_UINT8) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_INT16) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_FLOAT16) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_UINT16) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_INT32) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_UINT32) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_INT64) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_UINT64) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_FLOAT32) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else if (tensor_type == DataType::DE_FLOAT64) { - RETURN_IF_NOT_OK((*dst)->Fill(static_cast(pad_val))); - } else { - RETURN_STATUS_UNEXPECTED( - "PadEnd: Incorrect/Unknown datatype, supported datatype is: [bool, int8, uint8, int16, uint16, int32, uint32, " - "int64, uint64, float16, float32, float64]."); - } - std::vector cur_ind(src->Rank(), 0); - RETURN_IF_NOT_OK(PadEndNumericHelper(src, *dst, cur_ind, 0)); - } - return Status::OK(); -} - -Status PadEndNumericHelper(const std::shared_ptr &src, const std::shared_ptr &dst, - std::vector cur_ind, size_t cur_dim) { - if (cur_dim == src->Rank() - 1) { // if this is the last dimension, copy the data - RETURN_IF_NOT_OK(dst->CopyLastDimAt(src, cur_ind)); - } else { // not the last dimension, keep doing recursion - dsize_t min_ind = std::min(dst->shape()[cur_dim], src->shape()[cur_dim]); - for (dsize_t i = 0; i < min_ind; i++) { - cur_ind[cur_dim] = i; - RETURN_IF_NOT_OK(PadEndNumericHelper(src, dst, cur_ind, cur_dim + 1)); - } - } - return Status::OK(); -} - -Status PadEndString(const std::shared_ptr &src, std::shared_ptr *dst, - const std::vector &pad_shape, const std::string &pad_val) { - CHECK_FAIL_RETURN_UNEXPECTED(src != nullptr && dst != nullptr, "tensor can't be nullptr"); - if (src->Rank() == 0 || src->shape().AsVector() == pad_shape) { - (*dst) = src; // if no padding, copy the pointer - } else { - CHECK_FAIL_RETURN_UNEXPECTED(src->Rank() == pad_shape.size(), - "PadEnd: invalid pad shape, as rank of input is: " + std::to_string(src->Rank()) + - ", and rank of pad value: " + std::to_string(pad_shape.size())); - std::vector cur_ind(src->Rank(), 0); - std::vector strings; - RETURN_IF_NOT_OK(PadEndStringHelper(src, &strings, TensorShape(pad_shape), cur_ind, 0, pad_val)); - RETURN_IF_NOT_OK(Tensor::CreateFromVector(strings, TensorShape(pad_shape), src->type(), dst)); - } - return Status::OK(); -} - -Status PadEndStringHelper(const std::shared_ptr &src, std::vector *dst, - const TensorShape &dst_shape, std::vector cur_ind, size_t cur_dim, - const std::string &pad_value) { - if (cur_dim == src->Rank() - 1) { // if this is the last dimension, copy the data - dsize_t min_ind = std::min(dst_shape[cur_dim], src->shape()[cur_dim]); - for (dsize_t i = 0; i < min_ind; i++) { - cur_ind[cur_dim] = i; - std::string_view item; - RETURN_IF_NOT_OK(src->GetItemAt(&item, cur_ind)); - (void)dst->emplace_back(item); - } - for (dsize_t i = min_ind; i < dst_shape[cur_dim]; i++) { - (void)dst->emplace_back(pad_value); - } - - } else { // not the last dimension, keep doing recursion - dsize_t min_ind = std::min(dst_shape[cur_dim], src->shape()[cur_dim]); - for (dsize_t i = 0; i < min_ind; i++) { - cur_ind[cur_dim] = i; - RETURN_IF_NOT_OK(PadEndStringHelper(src, dst, dst_shape, cur_ind, cur_dim + 1, pad_value)); - } - dsize_t count = (dst_shape[cur_dim] - min_ind) * dst_shape.Strides()[cur_dim]; - for (dsize_t i = 0; i < count; i++) { - (void)dst->emplace_back(pad_value); - } - } - return Status::OK(); -} - -template -Status MaskHelper(const std::shared_ptr &input, const std::shared_ptr &output, - const std::shared_ptr &value_tensor, RelationalOp op) { - T value; - RETURN_IF_NOT_OK(value_tensor->GetItemAt(&value, {})); - auto in_itr = input->begin(); - auto out_itr = output->begin(); - for (; in_itr != input->end(); ++in_itr, ++out_itr) { - switch (op) { - case RelationalOp::kEqual: - *out_itr = (*in_itr == value); - break; - case RelationalOp::kNotEqual: - *out_itr = (*in_itr != value); - break; - case RelationalOp::kGreater: - *out_itr = (*in_itr > value); - break; - case RelationalOp::kGreaterEqual: - *out_itr = (*in_itr >= value); - break; - case RelationalOp::kLess: - *out_itr = (*in_itr < value); - break; - case RelationalOp::kLessEqual: - *out_itr = (*in_itr <= value); - break; - default: - RETURN_STATUS_UNEXPECTED( - "Mask: unknown relational operator, supported operator is: equal, notEqual, greater, less, lessEqual."); - } - } - return Status::OK(); -} - -Status Mask(const std::shared_ptr &input, std::shared_ptr *output, const std::shared_ptr &value, - RelationalOp op) { - CHECK_FAIL_RETURN_UNEXPECTED(input->type().IsNumeric() == value->type().IsNumeric(), - "Mask: input datatype does not match the value datatype, both should be numeric or " - "non-numerical in the same time."); - CHECK_FAIL_RETURN_UNEXPECTED(value->shape() == TensorShape::CreateScalar(), - "Mask: value shape should be a scalar, got shape:" + value->shape().ToString()); - - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), DataType(DataType::DE_BOOL), output)); - - std::unique_ptr value_cast_op = std::make_unique(input->type()); - std::shared_ptr casted_value; - if (input->type().IsNumeric()) { - RETURN_IF_NOT_OK(value_cast_op->Compute(value, &casted_value)); - } else { - casted_value = value; - } - - switch (input->type().value()) { - case DataType::DE_BOOL: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_INT8: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_UINT8: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_UINT16: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_INT16: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_UINT32: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_INT32: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_UINT64: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_INT64: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_FLOAT16: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_FLOAT32: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_FLOAT64: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_STRING: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_BYTES: - RETURN_IF_NOT_OK(MaskHelper(input, *output, casted_value, op)); - break; - case DataType::DE_UNKNOWN: - default: - RETURN_STATUS_UNEXPECTED( - "Mask: unsupported input datatype, support datatype is:[bool, int8, uint8, int16, uint16, int32, uint32, " - "int64, uint64, float16, float32, float64, string, bytes]."); - break; - } - return Status::OK(); -} - -Status Concatenate(const TensorRow &input, TensorRow *output, int8_t axis, const std::shared_ptr &prepend, - const std::shared_ptr &append) { - CHECK_FAIL_RETURN_UNEXPECTED(input.size() > 0, "Concatenate: input cannot be empty."); - axis = Tensor::HandleNeg(axis, input[0]->shape().Rank()); - CHECK_FAIL_RETURN_UNEXPECTED( - axis == 0, "Concatenate: only 1D input supported, input 'axis' should be 0, but got: " + std::to_string(axis)); - - TensorShape t = TensorShape::CreateScalar(); - - DataType first_dtype = input[0]->type(); - - TensorRow tensor_list; - - if (prepend != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED( - first_dtype == prepend->type(), - "Concatenate: input datatype does not match the prepend datatype, got input datatype: " + first_dtype.ToString() + - ", prepend datatype:" + prepend->type().ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - prepend->shape().Rank() == 1, - "Concatenate: only 1D input supported, got rank of prepend: " + std::to_string(prepend->shape().Rank())); - tensor_list.emplace_back(prepend); - } - - for (const auto &tensor : input) { - CHECK_FAIL_RETURN_UNEXPECTED(first_dtype == tensor->type(), "Concatenate: inconsistent datatype of input."); - CHECK_FAIL_RETURN_UNEXPECTED( - tensor->shape().Rank() == 1, - "Concatenate: only 1D input supported, got rank of input: " + std::to_string(tensor->shape().Rank())); - tensor_list.emplace_back(tensor); - } - - if (append != nullptr) { - CHECK_FAIL_RETURN_UNEXPECTED( - first_dtype == append->type(), - "Concatenate: input datatype does not match the append datatype, got input datatype: " + first_dtype.ToString() + - ", append datatype:" + append->type().ToString()); - CHECK_FAIL_RETURN_UNEXPECTED( - append->shape().Rank() == 1, - "Concatenate: only 1D append supported, got rank of append:" + std::to_string(append->shape().Rank())); - tensor_list.emplace_back(append); - } - - // create final shape - for (dsize_t i = 0; i < tensor_list[0]->shape().Rank(); i++) { - if (i != axis) { - t = t.AppendDim(tensor_list[0]->shape()[i]); - } else { - dsize_t new_shape = 0; - for (auto &tensor : tensor_list) { - new_shape = tensor->shape()[i] + new_shape; - } - t = t.AppendDim(new_shape); - } - } - - std::shared_ptr out; - - if (input[0]->type().IsNumeric()) { - RETURN_IF_NOT_OK(Tensor::CreateEmpty(t, tensor_list[0]->type(), &out)); - std::vector index(axis + 1, 0); - - auto n = index.size() - 1; - for (auto &tensor : tensor_list) { - RETURN_IF_NOT_OK(out->InsertTensor({index}, tensor, true)); - index[n] = index[n] + tensor->shape()[axis]; - } - } else { - std::vector strings; - - for (auto &i : tensor_list) { - auto itr = i->begin(); - for (; itr != i->end(); ++itr) { - (void)strings.emplace_back(*itr); - } - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(strings, t, input[0]->type(), &out)); - } - - output->push_back(out); - - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status BatchTensorToCVTensorVector(const std::shared_ptr &input, - std::vector> *output) { - RETURN_UNEXPECTED_IF_NULL(output); - std::vector tensor_shape = input->shape().AsVector(); - TensorShape remaining({-1}); - std::vector index(tensor_shape.size(), 0); - CHECK_FAIL_RETURN_UNEXPECTED( - tensor_shape.size() > 1, - "MixUpBatch: input must be at least 2-D in order to unpack, but got rank: " + std::to_string(tensor_shape.size())); - TensorShape element_shape(std::vector(tensor_shape.begin() + 1, tensor_shape.end())); - - for (; index[0] < tensor_shape[0]; index[0]++) { - uchar *start_addr_of_index = nullptr; - std::shared_ptr out; - - RETURN_IF_NOT_OK(input->StartAddrOfIndex(index, &start_addr_of_index, &remaining)); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(element_shape, input->type(), start_addr_of_index, &out)); - std::shared_ptr cv_out = CVTensor::AsCVTensor(std::move(out)); - if (!cv_out->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] MixUpBatch: allocate memory failed."); - } - output->push_back(cv_out); - } - return Status::OK(); -} -#endif - -Status BatchTensorToTensorVector(const std::shared_ptr &input, std::vector> *output) { - RETURN_UNEXPECTED_IF_NULL(output); - std::vector tensor_shape = input->shape().AsVector(); - TensorShape remaining({-1}); - std::vector index(tensor_shape.size(), 0); - CHECK_FAIL_RETURN_UNEXPECTED( - tensor_shape.size() > 1, - "CutMixBatch: input must be at least 2-D in order to unpack, but got rank:" + std::to_string(tensor_shape.size())); - TensorShape element_shape(std::vector(tensor_shape.begin() + 1, tensor_shape.end())); - - for (; index[0] < tensor_shape[0]; index[0]++) { - uchar *start_addr_of_index = nullptr; - std::shared_ptr out; - - RETURN_IF_NOT_OK(input->StartAddrOfIndex(index, &start_addr_of_index, &remaining)); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(element_shape, input->type(), start_addr_of_index, &out)); - output->push_back(out); - } - return Status::OK(); -} - -Status TensorVectorToBatchTensor(const std::vector> &input, std::shared_ptr *output) { - RETURN_UNEXPECTED_IF_NULL(output); - CHECK_FAIL_RETURN_UNEXPECTED(!input.empty(), "CutMixBatch: the input is empty."); - std::vector tensor_shape = input.front()->shape().AsVector(); - (void)tensor_shape.insert(tensor_shape.begin(), input.size()); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape(tensor_shape), input.at(0)->type(), output)); - for (int i = 0; i < input.size(); i++) { - RETURN_IF_NOT_OK((*output)->InsertTensor({i}, input[i])); - } - return Status::OK(); -} - -template -struct UniqueOpHashMap { - using map_type = std::unordered_map; -}; -#ifndef ENABLE_ANDROID -template <> -struct UniqueOpHashMap { - using map_type = std::unordered_map; -}; - -#else -struct gn_hash { - size_t operator()(const float16 &f) const { return static_cast(f); } -}; - -template <> -struct UniqueOpHashMap { - using map_type = std::unordered_map; -}; -#endif - -template <> -struct UniqueOpHashMap { - using map_type = std::unordered_map; -}; - -template <> -struct UniqueOpHashMap { - using map_type = std::unordered_map; -}; - -template -Status UniqueHelper(const std::shared_ptr &input, std::shared_ptr *output, - std::shared_ptr *output_idx, std::shared_ptr *output_cnt) { - const dsize_t N = input->Size(); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), DataType(DataType::DE_INT32), output_idx)); - - typename UniqueOpHashMap::map_type uniq; - uniq.reserve(2 * N); - auto in_iter = input->begin(); - auto out_idx_iter = (*output_idx)->begin(); - int32_t i = 0; - for (; in_iter != input->end(); ++in_iter, ++out_idx_iter) { - auto it = uniq.emplace(*in_iter, i); - *out_idx_iter = it.first->second; - if (it.second) { - ++i; - } - } - auto uniq_size = uniq.size(); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({static_cast(uniq_size)}), input->type(), output)); - auto out_iter = (*output)->begin(); - for (const auto &item : uniq) { - *(out_iter + static_cast(item.second)) = item.first; - } - RETURN_IF_NOT_OK( - Tensor::CreateEmpty(TensorShape({static_cast(uniq_size)}), DataType(DataType::DE_INT32), output_cnt)); - RETURN_IF_NOT_OK((*output_cnt)->Zero()); - - auto out_cnt_iter = (*output_cnt)->begin(); - out_idx_iter = (*output_idx)->begin(); - for (int32_t j = 0; j < N; ++j) { - auto idx = *(out_idx_iter + static_cast(j)); - ++*(out_cnt_iter + static_cast(idx)); - } - return Status::OK(); -} - -Status Unique(const std::shared_ptr &input, std::shared_ptr *output, - std::shared_ptr *output_idx, std::shared_ptr *output_cnt) { - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Rank() == 1, "Unique: only 1D input supported, but got rank: " + - std::to_string(input->shape().Rank())); - if (input->type() == DataType::DE_INT64) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_INT32) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_INT16) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_INT8) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_UINT64) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_UINT32) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_UINT16) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_UINT8) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_FLOAT16) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_FLOAT32) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else if (input->type() == DataType::DE_FLOAT64) { - RETURN_IF_NOT_OK(UniqueHelper(input, output, output_idx, output_cnt)); - } else { - RETURN_STATUS_UNEXPECTED("Unique: Unique op only supports numeric input."); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/data_utils.h b/mindspore-lite/minddata/dataset/kernels/data/data_utils.h deleted file mode 100644 index 955cf13b6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/data_utils.h +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DATA_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DATA_UTILS_H_ - -#include -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#endif -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" - -namespace mindspore { -namespace dataset { -// Returns Onehot encoding of the input tensor. -// Example: if input=2 and numClasses=3, the output is [0 0 1]. -// @param input: Tensor has type DE_UINT64, the non-one hot values are stored -// along the first dimensions or rows.. -// If the rank of input is not 1 or the type is not DE_UINT64, -// then it will fail. -// @param output: Tensor. The shape of the output tensor is -// and the type is same as input. -// @param num_classes: Number of classes in dataset. -// @param smoothing_rate: Adjustable hyperparameter for label smoothing level. -Status OneHotEncoding(const std::shared_ptr &input, std::shared_ptr *output, dsize_t num_classes, - double smoothing_rate = 0.); - -template -Status OneHotEncodingImpl(const std::shared_ptr &input, std::shared_ptr *output, dsize_t num_classes, - int64_t index, double smoothing_rate); - -// Returns a tensor of shape input filled with the passed fill_value -// @param input Tensor -// @param output Tensor. The shape and type of the output tensor is same as input -// @param fill_value Tensor. A scalar tensor used to fill the output tensor -Status Fill(const std::shared_ptr &input, std::shared_ptr *output, - const std::shared_ptr &fill_value); - -// Returns a type changed input tensor. -// Example: if input tensor is float64, the output will the specified dataType. See DataTypes.cpp -// @param input Tensor -// @param output Tensor. The shape of the output tensor is same as input with the type changed. -// @param data_type: type of data to cast data to -// @note: this operation will do a memcpy and if the value is truncated then precision will be lost -template -void CastFrom(const std::shared_ptr &input, std::shared_ptr *output); - -template -void Cast(const std::shared_ptr &input, std::shared_ptr *output); - -Status ToFloat16(const std::shared_ptr &input, std::shared_ptr *output); - -Status TypeCast(const std::shared_ptr &input, std::shared_ptr *output, const DataType &data_type); - -// Pad input tensor according pad_shape, need to have same rank. -// Based on the type of the input tensor, PadEndNumeric/String will be called. -// @param std::shared_ptr src - tensor to pad from -// @param std::shared_ptr *dst - return tensor padded -// @param std::vector pad_shape - shape to pad to -// @param std::shared_ptr pad_val - value to pad with in Tensor format, -// @return Status The status code returned -Status PadEnd(const std::shared_ptr &src, std::shared_ptr *dst, const std::vector &pad_shape, - const std::shared_ptr &pad_val); - -// Pad input numeric tensor according pad_shape, need to have same rank. -// @param std::shared_ptr src - tensor to pad from -// @param std::shared_ptr *dst - return tensor padded -// @param std::vector pad_shape - shape to pad to -// @param float pad_val - value to pad with -// @return Status The status code returned -Status PadEndNumeric(const std::shared_ptr &src, std::shared_ptr *dst, - const std::vector &pad_shape, float pad_val); - -// recursive helper function for padding numric tensors. This function could be very expensive if called on a -// multi-dimensional tensor it is only meant to be called by PadEndNumeric. -// @tparam T - type of tensor and fill value -// @param std::shared_ptr src - Tensor to pad from -// @param std::shared_ptr* dst - Tensor to pad to, return value -// @param std::vector cur_ind - recursion helper -// @param T pad_val - value to pad tensor with -// @param size_t cur_dim - recursion helper -// @return Status The status code returned -Status PadEndNumericHelper(const std::shared_ptr &src, const std::shared_ptr &dst, - std::vector cur_ind, size_t cur_dim = 0); - -// Pad input string tensor according pad_shape, need to have same rank. -// @param std::shared_ptr src - tensor to pad from -// @param std::shared_ptr *dst - return tensor padded -// @param std::vector pad_shape - shape to pad to -// @param std::string pad_val - value to pad with -// @return Status The status code returned -Status PadEndString(const std::shared_ptr &src, std::shared_ptr *dst, - const std::vector &pad_shape, const std::string &pad_val); - -// recursive helper function for padding string tensors. This function could be very expensive if called on a -// multi-dimensional tensor it is only meant to be called by PadEndString. -// @tparam T - type of tensor and fill value -// @param std::shared_ptr src - Tensor to pad from -// @param std::shared_ptr* dst - Tensor to pad to, return value -// @param std::vector cur_ind - recursion helperas text -// @param std::string pad_val - value to pad tensor with -// @param size_t cur_dim - recursion helper -// @return Status The status code returned -Status PadEndStringHelper(const std::shared_ptr &src, std::vector *dst, - const TensorShape &dst_shape, std::vector cur_ind, size_t cur_dim, - const std::string &pad_value); - -/// Helper method that masks the input tensor -/// @tparam T type of the tensor -/// @param input[in] input tensor -/// @param output[out] output tensor -/// @param value_tensor[in] scalar tensor value to compared with -/// @param op[in] RelationalOp enum -/// @return Status ok/error -template -Status MaskHelper(const std::shared_ptr &input, const std::shared_ptr &output, - const std::shared_ptr &value_tensor, RelationalOp op); - -/// Mask the input tensor -/// @param input[in] input tensor -/// @param output[out] output tensor -/// @param value[in] scalar tensor value to compared with -/// @param op[in] RelationalOp enum -/// @return Status ok/error -Status Mask(const std::shared_ptr &input, std::shared_ptr *output, const std::shared_ptr &value, - RelationalOp op); - -Status Concatenate(const TensorRow &input, TensorRow *output, int8_t axis, const std::shared_ptr &prepend, - const std::shared_ptr &append); - -// helper for concat, always append to the input, and pass that to the output -Status ConcatenateHelper(const std::shared_ptr &input, std::shared_ptr *output, int8_t axis, - std::shared_ptr append); - -/// Convert an n-dimensional Tensor to a vector of (n-1)-dimensional CVTensors -/// \param input[in] input tensor -/// \param output[out] output vector of CVTensors -/// \return Status ok/error -Status BatchTensorToCVTensorVector(const std::shared_ptr &input, - std::vector> *output); - -/// Convert an n-dimensional Tensor to a vector of (n-1)-dimensional Tensors -/// \param input[in] input tensor -/// \param output[out] output vector of tensors -/// \return Status ok/error -Status BatchTensorToTensorVector(const std::shared_ptr &input, std::vector> *output); - -/// Convert a vector of (n-1)-dimensional Tensors to an n-dimensional Tensor -/// \param input[in] input vector of tensors -/// \param output[out] output tensor -/// \return Status ok/error -Status TensorVectorToBatchTensor(const std::vector> &input, std::shared_ptr *output); - -/// Helper method that uniques the input tensor -/// @tparam T type of the tensor -/// \param input[in] input 1d tensor -/// \param output[out] output tensor -/// \param output[out] output tensor of item index -/// \param output[out] output tensor of item count -/// \return Status ok/error -template -Status UniqueHelper(const std::shared_ptr &input, std::shared_ptr *output, - std::shared_ptr *output_idx, std::shared_ptr *output_cnt); - -/// Unique the input tensor -/// @tparam T type of the tensor -/// \param input[in] input 1d tensor -/// \param output[out] output tensor -/// \param output[out] output tensor of item index -/// \param output[out] output tensor of item count -/// \return Status ok/error -Status Unique(const std::shared_ptr &input, std::shared_ptr *output, - std::shared_ptr *output_idx, std::shared_ptr *output_cnt); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DATA_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.cc b/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.cc deleted file mode 100644 index c4f40f21f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status DuplicateOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - CHECK_FAIL_RETURN_UNEXPECTED(input.size() == 1, - "Duplicate: only supports transform one column each time, got column num: " + - std::to_string(input.size()) + ", check 'input_columns' when call this operator."); - std::shared_ptr out; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input[0], &out)); - output->push_back(input[0]); - output->push_back(out); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h b/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h deleted file mode 100644 index 3fda07abf..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DUPLICATE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DUPLICATE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class DuplicateOp : public TensorOp { - public: - DuplicateOp() = default; - - ~DuplicateOp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - uint32_t NumOutput() override { return 2; } - - std::string Name() const override { return kDuplicateOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_DUPLICATE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/fill_op.cc b/mindspore-lite/minddata/dataset/kernels/data/fill_op.cc deleted file mode 100644 index 3abd8d9f3..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/fill_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/fill_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status FillOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - Status s = Fill(input, output, fill_value_); - return s; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/fill_op.h b/mindspore-lite/minddata/dataset/kernels/data/fill_op.h deleted file mode 100644 index a0d6eb08c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/fill_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_FILL_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_FILL_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class FillOp : public TensorOp { - public: - explicit FillOp(std::shared_ptr fill_value) : fill_value_(std::move(fill_value)) {} - - ~FillOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kFillOp; } - - private: - std::shared_ptr fill_value_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_FILL_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/mask_op.cc b/mindspore-lite/minddata/dataset/kernels/data/mask_op.cc deleted file mode 100644 index 2cd12f9cb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/mask_op.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/mask_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status MaskOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - std::shared_ptr temp_output; - CHECK_FAIL_RETURN_UNEXPECTED(type_.IsNumeric(), "Mask: only support numeric datatype of input, got string."); - - RETURN_IF_NOT_OK(Mask(input, &temp_output, value_, op_)); - - // cast the output to the the required type. Skip casting if type_ is bool. - if (type_ != DataType::DE_BOOL) { - RETURN_IF_NOT_OK(cast_->Compute(temp_output, output)); - } else { - *output = std::move(temp_output); - } - - return Status::OK(); -} - -Status MaskOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Mask: inputs cannot be empty."); - outputs[0] = type_; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/mask_op.h b/mindspore-lite/minddata/dataset/kernels/data/mask_op.h deleted file mode 100644 index 58990eae6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/mask_op.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_MASK_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_MASK_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class MaskOp : public TensorOp { - public: - MaskOp(RelationalOp op, std::shared_ptr value, DataType type = DataType(DataType::DE_BOOL)) - : op_(op), value_(std::move(value)), type_(type), cast_(new TypeCastOp(type)) {} - - ~MaskOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kMaskOp; } - - private: - RelationalOp op_; - std::shared_ptr value_; - DataType type_; - std::unique_ptr cast_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_MASK_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/no_op.h b/mindspore-lite/minddata/dataset/kernels/data/no_op.h deleted file mode 100644 index bb5bebfc1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/no_op.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_NO_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_NO_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class NoOp : public TensorOp { - public: - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override { - RETURN_UNEXPECTED_IF_NULL(output); - *output = input; - return Status::OK(); - } - - std::string Name() const override { return kNoOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_NO_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.cc b/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.cc deleted file mode 100644 index 29aca51f0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.cc +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status OneHotOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - Status s = OneHotEncoding(input, output, num_classes_, smoothing_rate_); - return s; -} - -Status OneHotOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "OneHot: inputs cannot be empty."); - outputs.clear(); - std::vector inputs_copy; - inputs_copy.push_back(inputs[0].Squeeze()); - CHECK_FAIL_RETURN_UNEXPECTED( - inputs[0].Rank() <= 1, "OneHot: Only support scalar or 1D input, got rank: " + std::to_string(inputs[0].Rank())); - if (inputs_copy[0].Rank() == 0) { - (void)outputs.emplace_back(std::vector{num_classes_}); - } else if (inputs_copy[0].Rank() == 1) { - (void)outputs.emplace_back(std::vector{inputs_copy[0][0], num_classes_}); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "OneHot: Invalid input shape."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h b/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h deleted file mode 100644 index 7b36dcd22..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_ONE_HOT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_ONE_HOT_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class OneHotOp : public TensorOp { - public: - OneHotOp(int num_classes, double smoothing_rate) : num_classes_(num_classes), smoothing_rate_(smoothing_rate) {} - - ~OneHotOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kOneHotOp; } - - private: - int num_classes_; - double smoothing_rate_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_ONE_HOT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.cc b/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.cc deleted file mode 100644 index 4cc66b676..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.cc +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status PadEndOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return PadEnd(input, output, output_shape_.AsVector(), pad_val_); -} - -Status PadEndOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - for (auto i = 0; i < inputs.size(); ++i) { - (void)outputs.emplace_back(TensorShape(output_shape_.AsVector())); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "PadEnd: invalid input shape."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h b/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h deleted file mode 100644 index ba570cb94..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PAD_END_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PAD_END_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class PadEndOp : public TensorOp { - public: - PadEndOp(const TensorShape &pad_shape, std::shared_ptr pad_value) - : output_shape_(pad_shape), pad_val_(std::move(pad_value)) {} - - ~PadEndOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kPadEndOp; } - - private: - TensorShape output_shape_; - std::shared_ptr pad_val_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PAD_END_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.cc b/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.cc deleted file mode 100644 index 959179c6d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.cc +++ /dev/null @@ -1,1462 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h" - -#include - -#include -#include - -#include "absl/base/casts.h" -#include "absl/container/inlined_vector.h" -#include "proto/example.pb.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore::dataset { -namespace protobuf = ::google::protobuf; - -constexpr bool kLittleEndian = __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__; -constexpr size_t kInlinedVectorSize = 4; - -template -using SmallVector = absl::InlinedVector; -using StringPiece = std::string_view; - -template -class LimitedArraySlice { - public: - using value_type = T; - - LimitedArraySlice(T *begin, size_t num_elements) : current_(begin), begin_(begin), end_(begin + num_elements) {} - - /// \brief Get the left space in the slice. - int64_t EndDistance() const { return end_ - current_; } - - /// \brief Push value to back of slice. If the slice is full, only change the - /// total number without modify the data. - void push_back(T &&value) { - if (EndDistance() > 0) { - *current_ = std::move(value); - } - ++current_; - } - - /// \brief Construct an element at the back of slice and return a mutable - /// reference to the new element. - T &construct_at_end() { - if (EndDistance() <= 0) { - MS_EXCEPTION(RuntimeError) << "LimitedArraySlice has no space left."; - } - return *(current_++); - } - - /// \brief Get the mutable reference to the last element in slice. - T &back() { return *(current_ - 1); } - - /// \brief Get the number of elements in slice. - size_t size() const { return std::min(current_ - begin_, end_ - begin_); } - - /// \brief Resize the slice to the given size by advancing the pointer to - /// the current element. - void resize(size_t size) { current_ = begin_ + size; } - - /// \brief Get the data buffer. - T *data() { return begin_; } - - private: - T *current_; - T *begin_; - T *end_; -}; - -uint8_t PeekTag(protobuf::io::CodedInputStream *stream) { - if (stream == nullptr) { - MS_EXCEPTION(RuntimeError) << "CodedInputStream is nullptr."; - } - const void *ptr; - int size; - if (!stream->GetDirectBufferPointer(&ptr, &size)) { - return 0; - } - if (ptr == nullptr) { - return 0; - } - return *static_cast(ptr); -} - -constexpr uint8_t kVarintTag(const uint32_t tag) { return (tag << 3) | 0; } -constexpr uint8_t kDelimitedTag(const uint32_t tag) { return (tag << 3) | 2; } -constexpr uint8_t kFixed32Tag(const uint32_t tag) { return (tag << 3) | 5; } - -namespace parsed { -class Feature { - public: - Feature() = default; - explicit Feature(const StringPiece &serialized) : serialized_(serialized) {} - - Status ParseDataType(DataType *dtype) { - RETURN_UNEXPECTED_IF_NULL(dtype); - if (serialized_.empty()) { - *dtype = DataType(DataType::DE_UNKNOWN); - return Status::OK(); - } - const auto oneof_tag = static_cast(*serialized_.data()); - serialized_.remove_prefix(1); - constexpr uint8_t kStringTag = 1; - constexpr uint8_t kFloat32Tag = 2; - constexpr uint8_t kInt64Tag = 3; - switch (oneof_tag) { - case kDelimitedTag(kStringTag): - *dtype = DataType(DataType::DE_STRING); - break; - case kDelimitedTag(kFloat32Tag): - *dtype = DataType(DataType::DE_FLOAT32); - break; - case kDelimitedTag(kInt64Tag): - *dtype = DataType(DataType::DE_INT64); - break; - default: - // Initialize variable to avoid compiler warning - *dtype = DataType(DataType::DE_UNKNOWN); - RETURN_STATUS_UNEXPECTED("Unsupported datatype."); - } - return Status::OK(); - } - - bool GetNumElementsInBytesList(int *num_elements) const { - if (num_elements == nullptr) { - return false; - } - protobuf::io::CodedInputStream stream(reinterpret_cast(serialized_.data()), - static_cast(serialized_.size())); - uint32_t length = 0; - if (!stream.ReadVarint32(&length)) { - return false; - } - const auto limit = stream.PushLimit(static_cast(length)); - *num_elements = 0; - while (!stream.ExpectAtEnd()) { - if (!stream.ExpectTag(kDelimitedTag(1))) { - return false; - } - uint32_t bytes_length = 0; - if (!stream.ReadVarint32(&bytes_length)) { - return false; - } - if (!stream.Skip(static_cast(bytes_length))) { - return false; - } - ++*num_elements; - } - stream.PopLimit(limit); - return true; - } - - static std::string *construct_at_end(LimitedArraySlice *bytes_list) { - if (bytes_list == nullptr) { - MS_EXCEPTION(RuntimeError) << "LimitedArraySlice is nullptr."; - } - if (bytes_list->EndDistance() <= 0) { - return nullptr; - } - return &bytes_list->construct_at_end(); - } - - static std::string *construct_at_end(std::vector *bytes_list) { - if (bytes_list == nullptr) { - MS_EXCEPTION(RuntimeError) << "String vector is nullptr."; - } - return &bytes_list->emplace_back(); - } - - template - bool ParseBytesList(Result *bytes_list) const { - if (bytes_list == nullptr) { - return false; - } - - protobuf::io::CodedInputStream stream(reinterpret_cast(serialized_.data()), - static_cast(serialized_.size())); - - uint32_t length; - if (!stream.ReadVarint32(&length)) { - return false; - } - const auto limit = stream.PushLimit(static_cast(length)); - - while (!stream.ExpectAtEnd()) { - if (!stream.ExpectTag(kDelimitedTag(1))) { - return false; - } - // parse string - uint32_t bytes_length; - if (!stream.ReadVarint32(&bytes_length)) { - return false; - } - std::string *bytes = construct_at_end(bytes_list); - if (bytes == nullptr) { - return false; - } - bytes->resize(bytes_length); - if (!stream.ReadRaw(bytes->data(), static_cast(bytes_length))) { - return false; - } - } - stream.PopLimit(limit); - return true; - } - - template - bool ParseFloatList(Result *float_list) const { - if (float_list == nullptr) { - return false; - } - protobuf::io::CodedInputStream stream(reinterpret_cast(serialized_.data()), - static_cast(serialized_.size())); - uint32_t length; - if (!stream.ReadVarint32(&length)) { - return false; - } - const auto limit = stream.PushLimit(static_cast(length)); - - if (!stream.ExpectAtEnd()) { - const uint8_t peek_tag = PeekTag(&stream); - if (peek_tag != kDelimitedTag(1) && peek_tag != kFixed32Tag(1)) { - return false; - } - - constexpr int32_t kNumFloatBytes = 4; - if (peek_tag == kDelimitedTag(1)) { // packed - if (!stream.ExpectTag(kDelimitedTag(1))) { // packed tag - return false; - } - uint32_t packed_length; - if (!stream.ReadVarint32(&packed_length)) { - return false; - } - const auto packed_limit = stream.PushLimit(static_cast(packed_length)); - - // Store the initial size to know the offset we have to start writing - // data from before resizing the output "vector". - const size_t initial_size = float_list->size(); - float_list->resize(initial_size + packed_length / kNumFloatBytes); - - // If the result data type is float and we are on a little endian - // machine then we can simply memcpy the data from the proto into the - // result vector. - if (kLittleEndian && sizeof(typename Result::value_type) == kNumFloatBytes) { - // Calculate the length of the buffer available what can be less than - // what we requested in resize in case of a LimitedArraySlice. - const uint32_t bytes_to_copy = - std::min(static_cast((float_list->size() - initial_size) * kNumFloatBytes), packed_length); - if (!stream.ReadRaw(float_list->data() + initial_size, bytes_to_copy)) { - return false; - } - } else { - int64_t index = initial_size; - while (!stream.ExpectAtEnd()) { - uint32_t buffer32; - if (!stream.ReadLittleEndian32(&buffer32)) { - return false; - } - if (index < float_list->size()) { - float_list->data()[index] = absl::bit_cast(buffer32); - ++index; - } - } - } - - stream.PopLimit(packed_limit); - } else { // non-packed - const size_t initial_size = float_list->size(); - // 1 byte for the tag (`1` encoded as Variant32) and kNumFloatBytes for - // the value. - const int64_t num_elements = stream.BytesUntilLimit() / (1 + kNumFloatBytes); - float_list->resize(initial_size + num_elements); - int64_t index = initial_size; - while (!stream.ExpectAtEnd()) { - if (!stream.ExpectTag(kFixed32Tag(1))) { - return false; - } - uint32_t buffer32; - if (!stream.ReadLittleEndian32(&buffer32)) { - return false; - } - float_list->data()[index] = absl::bit_cast(buffer32); - ++index; - } - } - } - - stream.PopLimit(limit); - return true; - } - - template - bool ParseInt64List(Result *int64_list) const { - if (int64_list == nullptr) { - return false; - } - protobuf::io::CodedInputStream stream(reinterpret_cast(serialized_.data()), - static_cast(serialized_.size())); - uint32_t length; - if (!stream.ReadVarint32(&length)) { - return false; - } - const auto limit = stream.PushLimit(static_cast(length)); - - if (!stream.ExpectAtEnd()) { - const uint8_t peek_tag = PeekTag(&stream); - if (peek_tag != kDelimitedTag(1) && peek_tag != kVarintTag(1)) { - return false; - } - if (peek_tag == kDelimitedTag(1)) { // packed - if (!stream.ExpectTag(kDelimitedTag(1))) { // packed tag - return false; - } - uint32_t packed_length; - if (!stream.ReadVarint32(&packed_length)) { - return false; - } - const auto packed_limit = stream.PushLimit(static_cast(packed_length)); - - while (!stream.ExpectAtEnd()) { - uint64_t n; // There is no API for int64 - if (!stream.ReadVarint64(&n)) { - return false; - } - int64_list->push_back(static_cast(n)); - } - - stream.PopLimit(packed_limit); - } else { // non-packed - while (!stream.ExpectAtEnd()) { - if (!stream.ExpectTag(kVarintTag(1))) { - return false; - } - uint64_t n; // There is no API for int64 - if (!stream.ReadVarint64(&n)) { - return false; - } - int64_list->push_back(static_cast(n)); - } - } - } - stream.PopLimit(limit); - return true; - } - - private: - StringPiece serialized_; -}; - -using FeatureMapEntry = std::pair; -using Example = std::vector; -} // namespace parsed - -inline bool SkipExtraneousTag(protobuf::io::CodedInputStream *stream) { - if (stream == nullptr) { - MS_EXCEPTION(RuntimeError) << "CodedInputStream is nullptr."; - } - uint32_t data; - uint64_t dummy; - constexpr uint32_t kVarint = 0; - constexpr uint32_t kFixed64 = 1; - constexpr uint32_t kLengthDelimited = 2; - constexpr uint32_t kGroupBegin = 3; - constexpr uint32_t kGroupEnd = 4; - constexpr uint32_t kFixed32 = 5; - switch (stream->ReadTag() & 0x7) { - case kVarint: // varint - return stream->ReadVarint32(&data); - case kFixed64: // fixed64 - return stream->ReadLittleEndian64(&dummy); - case kLengthDelimited: // length delimited - if (!stream->ReadVarint32(&data)) { - return false; - } - stream->Skip(static_cast(data)); - return true; - case kGroupBegin: // group begin - case kGroupEnd: // group end - return false; // groups not supported. - case kFixed32: // fixed32 - return stream->ReadLittleEndian32(&data); - default: - return false; - } - return false; // unrecognized tag type -} - -bool ParseString(protobuf::io::CodedInputStream *stream, StringPiece *result) { - if (stream == nullptr) { - return false; - } - if (result == nullptr) { - return false; - } - uint32_t length; - if (!stream->ReadVarint32(&length)) { - return false; - } - if (length == 0) { - *result = StringPiece(nullptr, 0); - return true; - } - const void *stream_alias; - int stream_size; - if (!stream->GetDirectBufferPointer(&stream_alias, &stream_size)) { - return false; - } - if (static_cast(stream_size) < length) { - return false; - } - *result = StringPiece(static_cast(stream_alias), length); - stream->Skip(static_cast(length)); - return true; -} - -bool ParseFeatureMapEntry(protobuf::io::CodedInputStream *stream, parsed::FeatureMapEntry *feature_map_entry) { - if (stream == nullptr) { - return false; - } - if (feature_map_entry == nullptr) { - return false; - } - uint32_t length; - if (!stream->ReadVarint32(&length)) { - return false; - } - const auto limit = stream->PushLimit(static_cast(length)); - - // Protobufs allow an arbitrary order for the key and value fields. - for (int n = 0; n <= 1; ++n) { - constexpr uint32_t kNameTag = 1; - constexpr uint32_t kFeatureTag = 2; - switch (stream->ReadTag()) { - case kDelimitedTag(kNameTag): - if (!ParseString(stream, &feature_map_entry->first)) { - return false; - } - break; - - case kDelimitedTag(kFeatureTag): { - StringPiece feature_string_piece; - if (!ParseString(stream, &feature_string_piece)) { - return false; - } - feature_map_entry->second = parsed::Feature(feature_string_piece); - break; - } - - default: - return false; - } - } - - if (!stream->ExpectAtEnd()) { - return false; - } - stream->PopLimit(limit); - return true; -} - -bool ParseFeatures(protobuf::io::CodedInputStream *stream, parsed::Example *example) { - if (stream == nullptr) { - return false; - } - if (example == nullptr) { - return false; - } - uint32_t length; - if (!stream->ReadVarint32(&length)) { - return false; - } - const auto limit = stream->PushLimit(static_cast(length)); - while (!stream->ExpectAtEnd()) { - parsed::FeatureMapEntry feature_map_entry; - if (!stream->ExpectTag(kDelimitedTag(1))) { - return false; - } - if (!ParseFeatureMapEntry(stream, &feature_map_entry)) { - return false; - } - example->push_back(std::move(feature_map_entry)); - } - stream->PopLimit(limit); - return true; -} - -bool ParseExample(protobuf::io::CodedInputStream *stream, parsed::Example *example) { - if (stream == nullptr) { - return false; - } - if (example == nullptr) { - return false; - } - // Loop over the input stream which may contain multiple serialized Example - // protos merged together as strings. This behavior is consistent with Proto's - // ParseFromString when string representations are concatenated. - while (!stream->ExpectAtEnd()) { - if (!stream->ExpectTag(kDelimitedTag(1))) { - if (!SkipExtraneousTag(stream)) { - return false; - } - } else { - if (!ParseFeatures(stream, example)) { - return false; - } - } - } - return true; -} - -bool ParseExample(const StringPiece &serialized, parsed::Example *example) { - if (example == nullptr) { - return false; - } - protobuf::io::CodedInputStream stream(reinterpret_cast(serialized.data()), - static_cast(serialized.size())); - return ParseExample(&stream, example); -} - -template -class TensorVector { - public: - using value_type = T; - - std::shared_ptr tensor() { - if (tensor_ == nullptr) { - resize(0); - } - return tensor_; - } - - int64_t size() const { return tensor_ != nullptr ? tensor_->Size() : 0; } - - void resize(int64_t new_size) { - if (tensor_ != nullptr) { - MS_EXCEPTION(RuntimeError) << "TensorVector has already initialized."; - } - Status s = Tensor::CreateEmpty(TensorShape({new_size}), DataType::FromCType(), &tensor_); - if (s.IsError()) { - MS_EXCEPTION(RuntimeError) << s.ToString(); - } - data_ = &*(tensor_->begin()); - } - - T *data() { return data_; } - - const T *data() const { return data_; } - - private: - std::shared_ptr tensor_ = nullptr; - T *data_ = nullptr; // the raw data inside the tensor -}; - -template -void CopyOrMoveBlock(const T *b, const T *e, T *t) { - std::copy(b, e, t); -} - -void LogFeatureRepeated(const StringPiece &feature_name) { - MS_LOG(WARNING) << "Feature name: " << feature_name << " is repeated in Example. Ignoring all but last one."; -} - -inline Status ReportUnexpectedParseFailure(const StringPiece &feature_name) { - RETURN_STATUS_UNEXPECTED("Failed to parse serialized Example of feature name: " + std::string(feature_name)); -} - -inline Status ReportUnexpectedDataType(const StringPiece &feature_name, const DataType &dtype) { - RETURN_STATUS_UNEXPECTED("Got unexpected data type: " + dtype.ToString() + - " of feature name: " + std::string(feature_name)); -} - -inline Status ReportUnexpectedDataShape(const StringPiece &feature_name) { - RETURN_STATUS_UNEXPECTED("Column shape of " + std::string(feature_name) + - " defined in schema does not match the shape actually load."); -} - -Status CreateUint8TensorFromString(const std::vector &bytes_list, std::shared_ptr *column_tensor, - const TensorShape &column_shape, const std::string &column_name) { - RETURN_UNEXPECTED_IF_NULL(column_tensor); - dsize_t total_size = - std::accumulate(bytes_list.begin(), bytes_list.end(), 0, - [](dsize_t size, const std::string &str) { return size + static_cast(str.size()); }); - TensorShape output_shape = column_shape; - if (!column_shape.known()) { - output_shape = TensorShape({total_size}); - } else { - CHECK_FAIL_RETURN_UNEXPECTED( - output_shape.NumOfElements() == total_size, - "Column shape of " + column_name + " defined in schema does not match the shape actually load."); - } - RETURN_IF_NOT_OK(Tensor::CreateEmpty(output_shape, DataType(DataType::DE_UINT8), column_tensor)); - ptrdiff_t offset = 0; - for (const auto &str : bytes_list) { - int ret_code = memcpy_s((*column_tensor)->GetMutableBuffer() + offset, (*column_tensor)->SizeInBytes() - offset, - common::SafeCStr(str), str.size()); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == EOK, "Failed to copy string into Tensor."); - offset += static_cast(str.size()); - } - return Status::OK(); -} - -Status ParseExampleOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - if (parallel_parse_) { - return ParallelParseExample(input, output); - } else { - return ParseSingleExample(input, output); - } -} - -Status ParseSingleKnownShapeColumn(const parsed::Feature &feature, std::shared_ptr *column_tensor, - const StringPiece &feature_name, const ColDescriptor &column_descriptor, - const DataType &example_dtype) { - RETURN_UNEXPECTED_IF_NULL(column_tensor); - const size_t num_elements = column_descriptor.Shape().NumOfElements(); - switch (example_dtype.value()) { - case DataType::DE_INT64: { - const auto data_buffer = reinterpret_cast((*column_tensor)->GetMutableBuffer()); - LimitedArraySlice slice(data_buffer, num_elements); - if (!feature.ParseInt64List(&slice)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (slice.EndDistance() != 0) { - return ReportUnexpectedDataShape(feature_name); - } - break; - } - case DataType::DE_FLOAT32: { - const auto data_buffer = reinterpret_cast((*column_tensor)->GetMutableBuffer()); - LimitedArraySlice slice(data_buffer, num_elements); - if (!feature.ParseFloatList(&slice)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (slice.EndDistance() != 0) { - return ReportUnexpectedDataShape(feature_name); - } - break; - } - case DataType::DE_STRING: { - std::vector bytes_list; - bytes_list.reserve(num_elements); - if (!feature.ParseBytesList(&bytes_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (column_descriptor.Type().value() == DataType::DE_STRING) { - if (bytes_list.size() != num_elements) { - return ReportUnexpectedDataShape(feature_name); - } - TensorShape string_tensor_shape = TensorShape::CreateUnknownRankShape(); - RETURN_IF_NOT_OK(column_descriptor.MaterializeTensorShape(num_elements, &string_tensor_shape)); - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(bytes_list, string_tensor_shape, DataType(DataType::DE_STRING), column_tensor)); - } else { - // load string or bytes as uint8 tensor - RETURN_IF_NOT_OK( - CreateUint8TensorFromString(bytes_list, column_tensor, column_descriptor.Shape(), std::string(feature_name))); - } - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - return Status::OK(); -} - -Status ParseSingleVarLenColumn(const parsed::Feature &feature, std::shared_ptr *column_tensor, - const StringPiece &feature_name, const ColDescriptor &column_descriptor, - const DataType &example_dtype) { - RETURN_UNEXPECTED_IF_NULL(column_tensor); - std::vector bytes_list; - TensorVector float_list; - SmallVector int64_list; - - size_t num_elements; - switch (example_dtype.value()) { - case DataType::DE_INT64: { - if (!feature.ParseInt64List(&int64_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = int64_list.size(); - break; - } - case DataType::DE_FLOAT32: { - if (!feature.ParseFloatList(&float_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = float_list.size(); - break; - } - case DataType::DE_STRING: { - int actual_num_elements = 0; - if (!feature.GetNumElementsInBytesList(&actual_num_elements)) { - return ReportUnexpectedParseFailure(feature_name); - } - bytes_list.reserve(actual_num_elements); - if (!feature.ParseBytesList(&bytes_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = bytes_list.size(); - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - - TensorShape column_shape = TensorShape::CreateUnknownRankShape(); - RETURN_IF_NOT_OK(column_descriptor.MaterializeTensorShape(num_elements, &column_shape)); - - switch (example_dtype.value()) { - case DataType::DE_INT64: { - RETURN_IF_NOT_OK(Tensor::CreateEmpty(column_shape, example_dtype, column_tensor)); - CopyOrMoveBlock(int64_list.begin(), int64_list.end(), - reinterpret_cast((*column_tensor)->GetMutableBuffer())); - break; - } - case DataType::DE_FLOAT32: { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(std::shared_ptr(float_list.tensor()), column_tensor)); - RETURN_IF_NOT_OK((*column_tensor)->Reshape(column_shape)); - break; - } - case DataType::DE_STRING: { - if (column_descriptor.Type().value() == DataType::DE_STRING) { - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(bytes_list, column_shape, DataType(DataType::DE_STRING), column_tensor)); - } else { - // load string or bytes as uint8 tensor - RETURN_IF_NOT_OK(CreateUint8TensorFromString(bytes_list, column_tensor, TensorShape::CreateUnknownRankShape(), - std::string(feature_name))); - } - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - return Status::OK(); -} - -Status ParseExampleOp::ParseSingleExample(const TensorRow &raw_bytes, TensorRow *parsed_row) { - RETURN_UNEXPECTED_IF_NULL(parsed_row); - const auto filename = raw_bytes.getPath().empty() ? "" : raw_bytes.getPath()[0]; - const auto tensor_iterator = raw_bytes[0]->begin(); - - const auto example_bytes = std::string(*tensor_iterator); - RETURN_IF_NOT_OK(ConstructColumnMap(example_bytes)); - - parsed::Example parsed_example; - CHECK_FAIL_RETURN_UNEXPECTED(ParseExample(example_bytes, &parsed_example), - "Failed to parse example bytes: " + example_bytes + " in tfrecord file: " + filename); - - parsed_row->reserve(data_schema_.NumColumns()); - - for (int32_t column_index = 0; column_index < data_schema_.NumColumns(); ++column_index) { - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - if (column_descriptor.HasKnownShape()) { - if (!column_descriptor.Type().IsString()) { - DataType type; - if (column_descriptor.Type().IsInt() || column_descriptor.Type().IsBool()) { - type = DataType(DataType::DE_INT64); - } else if (column_descriptor.Type().IsFloat()) { - type = DataType(DataType::DE_FLOAT32); - } - std::shared_ptr column_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(column_descriptor.Shape(), type, &column_tensor)); - parsed_row->emplace_back(std::move(column_tensor)); - } else { - parsed_row->emplace_back(std::make_shared(TensorShape({}), DataType(DataType::DE_UNKNOWN))); - } - } else { - MS_LOG(INFO) << "Shape of column name: " << column_descriptor.Name() << " is not defined."; - parsed_row->emplace_back(std::make_shared(TensorShape({}), DataType(DataType::DE_UNKNOWN))); - } - } - - std::vector feature_already_seen(data_schema_.NumColumns(), false); - std::vector file_paths; - - const size_t parsed_example_size = parsed_example.size(); - for (size_t i = 0; i < parsed_example_size; ++i) { - // This is a logic that standard protobuf parsing is implementing. - // I.e. last entry in the map overwrites all the previous ones. - parsed::FeatureMapEntry &name_and_feature = parsed_example[parsed_example_size - i - 1]; - - const StringPiece &feature_name = name_and_feature.first; - parsed::Feature &feature = name_and_feature.second; - - if (column_name_id_map_.find(std::string(feature_name)) == column_name_id_map_.end()) { - MS_LOG(INFO) << "Feature name: " << feature_name << " is not in schema, skip it."; - continue; - } - - const auto column_index = column_name_id_map_[std::string(feature_name)]; - - DataType example_dtype; - RETURN_IF_NOT_OK(feature.ParseDataType(&example_dtype)); - if (example_dtype == DataType::DE_UNKNOWN) { - continue; - } - - // If feature was already visited, skip. - if (feature_already_seen[column_index]) { - LogFeatureRepeated(feature_name); - continue; - } - feature_already_seen[column_index] = true; - - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - bool type_cast_flag = false; - if (example_dtype != column_descriptor.Type()) { - const std::string msg = - "The data type loaded from the example for feature name: " + column_descriptor.Name() + - " does not match the predefined type in schema, the actual type: " + example_dtype.ToString() + - ", but the predefined type: " + column_descriptor.Type().ToString(); - if (!example_dtype.IsString() && !column_descriptor.Type().IsString()) { - MS_LOG(INFO) << msg << ". This will cause a type cast."; - type_cast_flag = true; - } else if (column_descriptor.Type().value() != DataType::DE_UINT8) { - // allow to read data of type string or bytes into an uint8 tensor - RETURN_STATUS_UNEXPECTED(msg); - } - } - - if (column_descriptor.HasKnownShape()) { - RETURN_IF_NOT_OK(ParseSingleKnownShapeColumn(feature, &(*parsed_row)[column_index], feature_name, - column_descriptor, example_dtype)); - } else { // if variable length - RETURN_IF_NOT_OK( - ParseSingleVarLenColumn(feature, &(*parsed_row)[column_index], feature_name, column_descriptor, example_dtype)); - } - if (type_cast_flag) { - std::shared_ptr cast_out; - RETURN_IF_NOT_OK(TypeCast((*parsed_row)[column_index], &cast_out, column_descriptor.Type())); - (*parsed_row)[column_index] = cast_out; - } - file_paths.push_back(filename); - } - - for (int32_t column_index = 0; column_index < data_schema_.NumColumns(); ++column_index) { - CHECK_FAIL_RETURN_UNEXPECTED(feature_already_seen[column_index], - "Feature name: " + data_schema_.Column(column_index).Name() + - " is required in schema but could not be found in tfrecord file."); - } - - parsed_row->setPath(file_paths); - return Status::OK(); -} - -size_t CalculateNumMiniBatch(const std::shared_ptr &batch_tensor) { - // This parameter affects performance in a big and data-dependent way. - constexpr size_t kMiniBatchSizeBytes = 50000; - - const size_t batch_size = batch_tensor->shape()[0]; - - size_t result = 0; - size_t minibatch_bytes = 0; - for (size_t i = 0; i < batch_size; i++) { - if (minibatch_bytes == 0) { // start minibatch - result++; - } - std::string_view tensor_value; - batch_tensor->GetItemAt(&tensor_value, {static_cast(i)}); - minibatch_bytes += tensor_value.size() + 1; - if (minibatch_bytes > kMiniBatchSizeBytes) { - minibatch_bytes = 0; - } - } - // 'special logic' - const size_t min_minibatches = std::min(8, batch_size); - constexpr size_t max_minibatches = 64; - return std::max(min_minibatches, std::min(max_minibatches, result)); -} - -class BlockingCounter { - public: - explicit BlockingCounter(const uint32_t initial_count) : state_(initial_count << 1), notified_(false) { - if ((initial_count << 1) >> 1 != initial_count) { - MS_EXCEPTION(RuntimeError) << "Value of initial_count exceeds upper limit: " << initial_count; - } - } - - ~BlockingCounter() = default; - - inline void DecrementCount() { - constexpr uint32_t kStep = 2; - uint32_t new_state = state_.fetch_sub(kStep, std::memory_order_acq_rel) - kStep; - if (new_state != 1) { - if (((new_state + kStep) & ~1) == 0) { - MS_EXCEPTION(RuntimeError) << "The number of remaining worker threads is already 0."; - } - return; // either count has not dropped to 0, or waiter is not waiting - } - std::unique_lock lock(mutex_); - if (notified_) { - MS_EXCEPTION(RuntimeError) << "Try to awake a notified worker."; - } - notified_ = true; - cond_var_.notify_all(); - } - - inline void Wait() { - uint32_t new_state = state_.fetch_or(1, std::memory_order_acq_rel); - if ((new_state >> 1) == 0) { - return; - } - std::unique_lock lock(mutex_); - while (!notified_) { - cond_var_.wait(lock); - } - } - - // Wait for the specified time, return false iff the count has not dropped to - // zero before the timeout expired. - inline bool WaitFor(std::chrono::milliseconds millisecond) { - uint32_t new_state = state_.fetch_or(1, std::memory_order_acq_rel); - if ((new_state >> 1) == 0) { - return true; - } - std::unique_lock lock(mutex_); - while (!notified_) { - const std::cv_status status = cond_var_.wait_for(lock, millisecond); - if (status == std::cv_status::timeout) { - return false; - } - } - return true; - } - - private: - std::mutex mutex_; - std::condition_variable cond_var_; - std::atomic state_; // low bit is waiter flag - bool notified_; -}; - -void ParallelFor(const std::function &function, const size_t task_count, - const std::unique_ptr &thread_pool) { - if (task_count == 0) { - return; - } - if (thread_pool == nullptr) { - for (size_t i = 0; i < task_count; ++i) { - function(i); - } - } else { - BlockingCounter counter(task_count - 1); - for (size_t i = 1; i < task_count; ++i) { - thread_pool->Schedule([i, &function, &counter] { - function(i); - counter.DecrementCount(); - }); - } - function(0); - counter.Wait(); - } -} - -Status FillAndCopyVarLenTensor(const std::vector> &minibatch_row_buffer, - std::shared_ptr *column_tensor, const size_t column_index) { - RETURN_UNEXPECTED_IF_NULL(column_tensor); - ptrdiff_t buffer_offset = 0; - for (const auto &minibatch_row : minibatch_row_buffer) { - const auto &minibatch_tensor = minibatch_row[column_index].numeric_tensor; - for (const auto &varlen_tensor : minibatch_tensor) { - const auto tensor_buffer_size = varlen_tensor->SizeInBytes(); - const errno_t copy_status = - memcpy_s((*column_tensor)->GetMutableBuffer() + buffer_offset, (*column_tensor)->SizeInBytes() - buffer_offset, - varlen_tensor->GetBuffer(), tensor_buffer_size); - CHECK_FAIL_RETURN_UNEXPECTED(copy_status == EOK, - "Failed to copy tensor to batch, got error_t: " + std::to_string(copy_status)); - buffer_offset += tensor_buffer_size; - } - } - return Status::OK(); -} - -Status FillAndCopyVarLenString(const std::vector> &minibatch_row_buffer, - std::shared_ptr *column_tensor, const size_t column_index, - const ColDescriptor &column_descriptor, dsize_t batch_size) { - RETURN_UNEXPECTED_IF_NULL(column_tensor); - std::vector string_buffer; - dsize_t element_size = 0; - for (const auto &minibatch_row : minibatch_row_buffer) { - const auto string_length = minibatch_row[column_index].string_length; - if (element_size == 0) { - element_size = static_cast(string_length); - } else { - CHECK_FAIL_RETURN_UNEXPECTED(string_length == element_size, - "Could not batch string or bytes tensors with different shapes."); - } - const auto &minibatch_string = minibatch_row[column_index].string_tensor; - string_buffer.insert(string_buffer.end(), minibatch_string.begin(), minibatch_string.end()); - } - - std::vector shape; - if (element_size != 0) { - shape = {batch_size, element_size}; - } else { - shape = {batch_size}; - } - const auto column_shape = TensorShape(shape); - if (column_descriptor.Type().value() == DataType::DE_STRING) { - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(string_buffer, column_shape, DataType(DataType::DE_STRING), column_tensor)); - } else { - RETURN_IF_NOT_OK(CreateUint8TensorFromString(string_buffer, column_tensor, column_shape, column_descriptor.Name())); - } - return Status::OK(); -} - -Status MergeDenseVarLenMiniBatches(const std::vector> &varlen_dense_buffers, - TensorRow *parsed_row, int32_t column_index, const DataSchema &data_schema, - dsize_t batch_size) { - RETURN_UNEXPECTED_IF_NULL(parsed_row); - const ColDescriptor &column_descriptor = data_schema.Column(column_index); - if (column_descriptor.HasKnownShape()) { - return Status::OK(); - } - std::shared_ptr column_tensor; - if (!varlen_dense_buffers[0][column_index].numeric_tensor.empty()) { - const TensorShape column_shape = - varlen_dense_buffers[0][column_index].numeric_tensor[0]->shape().InsertDim(0, batch_size); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(column_shape, column_descriptor.Type(), &column_tensor)); - RETURN_IF_NOT_OK(FillAndCopyVarLenTensor(varlen_dense_buffers, &column_tensor, column_index)); - } else { - RETURN_IF_NOT_OK( - FillAndCopyVarLenString(varlen_dense_buffers, &column_tensor, column_index, column_descriptor, batch_size)); - } - (*parsed_row)[column_index] = column_tensor; - return Status::OK(); -} - -Status ParseExampleOp::ParallelParseExample(const TensorRow &raw_bytes, TensorRow *parsed_row) { - RETURN_UNEXPECTED_IF_NULL(parsed_row); - Tensor::TensorIterator tensor_iterator = raw_bytes[0]->begin(); - RETURN_IF_NOT_OK(ConstructColumnMap(std::string(*tensor_iterator))); - parsed_row->reserve(data_schema_.NumColumns()); - - auto batch_size = raw_bytes[0]->shape()[0]; - std::vector type_cast_flag(data_schema_.NumColumns(), false); - std::vector varlen_column(data_schema_.NumColumns(), false); - std::unordered_map> string_column_map; - for (int32_t column_index = 0; column_index < data_schema_.NumColumns(); ++column_index) { - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - if (column_descriptor.HasKnownShape()) { - if (!column_descriptor.Type().IsString()) { - auto column_shape = column_descriptor.Shape().InsertDim(0, batch_size); - DataType type; - if (column_descriptor.Type().IsInt() || column_descriptor.Type().IsBool()) { - if (column_descriptor.Type().value() != DataType::DE_INT64) { - type_cast_flag[column_index] = true; - } - type = DataType(DataType::DE_INT64); - } else if (column_descriptor.Type().IsFloat()) { - if (column_descriptor.Type().value() != DataType::DE_FLOAT32) { - type_cast_flag[column_index] = true; - } - type = DataType(DataType::DE_FLOAT32); - } - std::shared_ptr column_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(column_shape, type, &column_tensor)); - parsed_row->emplace_back(std::move(column_tensor)); - if (column_descriptor.Type().value() == DataType::DE_UINT8) { - string_column_map[column_index] = - std::vector(batch_size * column_descriptor.Shape().NumOfElements()); - } - } else { - parsed_row->emplace_back(std::make_shared(TensorShape({}), DataType(DataType::DE_UNKNOWN))); - string_column_map[column_index] = - std::vector(batch_size * column_descriptor.Shape().NumOfElements()); - } - } else { - MS_LOG(INFO) << "Shape of column name: " << column_descriptor.Name() << " is not defined."; - varlen_column[column_index] = true; - parsed_row->emplace_back(std::make_shared(TensorShape({}), DataType(DataType::DE_UNKNOWN))); - } - } - - // Calculate number of minibatches. - // In main regime make each minibatch around kMiniBatchSizeBytes bytes. - // Apply 'special logic' below for small and big regimes. - const size_t num_minibatches = CalculateNumMiniBatch(raw_bytes[0]); - - auto first_example_of_minibatch = [&](const size_t minibatch) -> size_t { - return (batch_size * minibatch) / num_minibatches; - }; - - std::vector> varlen_dense_buffers(num_minibatches); - std::vector status_of_minibatch(num_minibatches); - auto ProcessMiniBatch = [&](const size_t minibatch) { - varlen_dense_buffers[minibatch].resize(data_schema_.NumColumns()); - const auto start = first_example_of_minibatch(minibatch); - const auto end = first_example_of_minibatch(minibatch + 1); - for (auto tensor_index = start; tensor_index < end; ++tensor_index) { - status_of_minibatch[minibatch] = - ParseSerializedExample(static_cast(*tensor_iterator.operator+(static_cast(tensor_index))), - parsed_row, &string_column_map, &varlen_dense_buffers[minibatch], tensor_index); - if (!status_of_minibatch[minibatch].IsOk()) { - break; - } - } - }; - - CheckAndInitPool(); - - ParallelFor(ProcessMiniBatch, num_minibatches, pool_); - - for (Status &status : status_of_minibatch) { - RETURN_IF_NOT_OK(status); - } - - for (auto string_column = string_column_map.begin(); string_column != string_column_map.end(); ++string_column) { - auto column_index = string_column->first; - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - auto column_shape = column_descriptor.Shape().InsertDim(0, batch_size); - std::shared_ptr string_tensor; - if (column_descriptor.Type().value() == DataType::DE_STRING) { - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(string_column->second, column_shape, DataType(DataType::DE_STRING), &string_tensor)); - } else { - // load string or bytes as uint8 tensor - RETURN_IF_NOT_OK( - CreateUint8TensorFromString(string_column->second, &string_tensor, column_shape, column_descriptor.Name())); - type_cast_flag[column_index] = false; - } - (*parsed_row)[column_index] = string_tensor; - } - - for (int32_t column_index = 0; column_index < data_schema_.NumColumns(); ++column_index) { - if (type_cast_flag[column_index]) { - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - std::shared_ptr cast_out; - RETURN_IF_NOT_OK(TypeCast((*parsed_row)[column_index], &cast_out, column_descriptor.Type())); - (*parsed_row)[column_index] = cast_out; - } else if (varlen_column[column_index]) { - RETURN_IF_NOT_OK( - MergeDenseVarLenMiniBatches(varlen_dense_buffers, parsed_row, column_index, data_schema_, batch_size)); - } - } - return Status::OK(); -} - -Status ParseSerializedKnownShapeColumn(const parsed::Feature &feature, TensorRow *parsed_row, - std::unordered_map> *string_col_map, - const int32_t column_index, const size_t tensor_index, - const StringPiece &feature_name, const ColDescriptor &column_descriptor, - const DataType &example_dtype) { - RETURN_UNEXPECTED_IF_NULL(parsed_row); - RETURN_UNEXPECTED_IF_NULL(string_col_map); - std::shared_ptr &column_tensor = (*parsed_row)[column_index]; - if (example_dtype != column_descriptor.Type()) { - const std::string msg = - "The data type loaded from the example for feature name: " + column_descriptor.Name() + - " does not match the predefined type in schema, the actual type: " + example_dtype.ToString() + - ", but the predefined type: " + column_descriptor.Type().ToString(); - if (example_dtype == column_tensor->type()) { - // if the actual data type is the same as the pre-allocated tensor, - // we can first read it into the tensor, then cast to the type specified by the schema - MS_LOG(INFO) << msg << ". This will cause a type cast."; - } else if (!example_dtype.IsString() || column_descriptor.Type().value() != DataType::DE_UINT8) { - // allow to read data of type string or bytes into an uint8 tensor - RETURN_STATUS_UNEXPECTED(msg); - } - } - - const std::size_t num_elements = column_descriptor.Shape().NumOfElements(); - switch (example_dtype.value()) { - case DataType::DE_INT64: { - const auto data_buffer = - reinterpret_cast(column_tensor->GetMutableBuffer()) + tensor_index * num_elements; - LimitedArraySlice slice(data_buffer, num_elements); - if (!feature.ParseInt64List(&slice)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (slice.EndDistance() != 0) { - return ReportUnexpectedDataShape(feature_name); - } - break; - } - case DataType::DE_FLOAT32: { - const auto data_buffer = - reinterpret_cast(column_tensor->GetMutableBuffer()) + tensor_index * num_elements; - LimitedArraySlice slice(data_buffer, num_elements); - if (!feature.ParseFloatList(&slice)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (slice.EndDistance() != 0) { - return ReportUnexpectedDataShape(feature_name); - } - break; - } - case DataType::DE_STRING: { - const auto data_buffer = &(*string_col_map)[column_index][tensor_index * num_elements]; - LimitedArraySlice slice(data_buffer, num_elements); - if (!feature.ParseBytesList(&slice)) { - return ReportUnexpectedParseFailure(feature_name); - } - if (slice.EndDistance() != 0) { - return ReportUnexpectedDataShape(feature_name); - } - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - return Status::OK(); -} - -Status PushStringToBuffer(const std::vector &bytes_list, VarLenTensorBuffer *varlen_tensor_buffer, - const ColDescriptor &column_descriptor) { - RETURN_UNEXPECTED_IF_NULL(varlen_tensor_buffer); - if (column_descriptor.Type().value() == DataType::DE_STRING) { - // check that each sample contains the same number of strings - if (varlen_tensor_buffer->string_length != 0) { - CHECK_FAIL_RETURN_UNEXPECTED(varlen_tensor_buffer->string_length == bytes_list.size(), - "Could not batch string Tensors with different shapes."); - } else { - if (column_descriptor.Rank() != 0) { - varlen_tensor_buffer->string_length = bytes_list.size(); - } else { - varlen_tensor_buffer->string_length = 0; - } - } - for (auto &bytes : bytes_list) { - varlen_tensor_buffer->string_tensor.emplace_back(bytes); - } - } else if (column_descriptor.Type().value() == DataType::DE_UINT8) { - size_t total_size = 0; - for (auto &bytes : bytes_list) { - total_size += bytes.size(); - varlen_tensor_buffer->string_tensor.emplace_back(bytes); - } - if (varlen_tensor_buffer->string_length != 0) { - CHECK_FAIL_RETURN_UNEXPECTED(varlen_tensor_buffer->string_length == total_size, - "Could not batch bytes Tensors with different shapes."); - } else { - varlen_tensor_buffer->string_length = total_size; - } - } - return Status::OK(); -} - -Status ParseSerializedVarLenColumn(const parsed::Feature &feature, VarLenTensorBuffer *varlen_tensor_buffer, - const StringPiece &feature_name, const ColDescriptor &column_descriptor, - const DataType &example_dtype) { - RETURN_UNEXPECTED_IF_NULL(varlen_tensor_buffer); - bool type_cast_flag = false; - if (example_dtype != column_descriptor.Type()) { - const std::string msg = - "The data type loaded from the example for feature name: " + column_descriptor.Name() + - " does not match the predefined type in schema, the actual type: " + example_dtype.ToString() + - ", but the predefined type: " + column_descriptor.Type().ToString(); - if (!example_dtype.IsString() && !column_descriptor.Type().IsString()) { - MS_LOG(INFO) << msg << ". This will cause a type cast."; - type_cast_flag = true; - } else if (column_descriptor.Type().value() != DataType::DE_UINT8) { - // allow to read data of type string or bytes into an uint8 tensor - RETURN_STATUS_UNEXPECTED(msg); - } - } - - size_t num_elements; - SmallVector int64_list; - TensorVector float_list; - std::vector bytes_list; - switch (example_dtype.value()) { - case DataType::DE_INT64: { - if (!feature.ParseInt64List(&int64_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = int64_list.size(); - break; - } - case DataType::DE_FLOAT32: { - if (!feature.ParseFloatList(&float_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = float_list.size(); - break; - } - case DataType::DE_STRING: { - int actual_num_elements = 0; - if (!feature.GetNumElementsInBytesList(&actual_num_elements)) { - return ReportUnexpectedParseFailure(feature_name); - } - bytes_list.reserve(actual_num_elements); - if (!feature.ParseBytesList(&bytes_list)) { - return ReportUnexpectedParseFailure(feature_name); - } - num_elements = bytes_list.size(); - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - - TensorShape varlen_tensor_shape = TensorShape::CreateUnknownRankShape(); - RETURN_IF_NOT_OK(column_descriptor.MaterializeTensorShape(num_elements, &varlen_tensor_shape)); - std::shared_ptr varlen_tensor; - switch (example_dtype.value()) { - case DataType::DE_INT64: { - RETURN_IF_NOT_OK(Tensor::CreateEmpty(varlen_tensor_shape, example_dtype, &varlen_tensor)); - CopyOrMoveBlock(int64_list.begin(), int64_list.end(), - reinterpret_cast(varlen_tensor->GetMutableBuffer())); - if (type_cast_flag) { - std::shared_ptr casted_varlen_tensor; - RETURN_IF_NOT_OK(TypeCast(varlen_tensor, &casted_varlen_tensor, column_descriptor.Type())); - varlen_tensor_buffer->numeric_tensor.emplace_back(casted_varlen_tensor); - } else { - varlen_tensor_buffer->numeric_tensor.emplace_back(varlen_tensor); - } - break; - } - case DataType::DE_FLOAT32: { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(std::shared_ptr(float_list.tensor()), &varlen_tensor)); - RETURN_IF_NOT_OK(varlen_tensor->Reshape(varlen_tensor_shape)); - if (type_cast_flag) { - std::shared_ptr casted_varlen_tensor; - RETURN_IF_NOT_OK(TypeCast(varlen_tensor, &casted_varlen_tensor, column_descriptor.Type())); - varlen_tensor_buffer->numeric_tensor.emplace_back(casted_varlen_tensor); - } else { - varlen_tensor_buffer->numeric_tensor.emplace_back(varlen_tensor); - } - break; - } - case DataType::DE_STRING: { - RETURN_IF_NOT_OK(PushStringToBuffer(bytes_list, varlen_tensor_buffer, column_descriptor)); - break; - } - default: - return ReportUnexpectedDataType(feature_name, example_dtype); - } - return Status::OK(); -} - -Status ParseExampleOp::ParseSerializedExample(const std::string &example_bytes, TensorRow *parsed_row, - std::unordered_map> *string_column_map, - std::vector *varlen_tensor_vector, - const size_t tensor_index) { - RETURN_UNEXPECTED_IF_NULL(varlen_tensor_vector); - parsed::Example parsed_example; - CHECK_FAIL_RETURN_UNEXPECTED(ParseExample(example_bytes, &parsed_example), - "Failed to parse example bytes: " + example_bytes); - - const size_t parsed_example_size = parsed_example.size(); - std::vector feature_already_seen(data_schema_.NumColumns(), false); - for (size_t i = 0; i < parsed_example_size; ++i) { - // This is a logic that standard protobuf parsing is implementing. - // I.e. last entry in the map overwrites all the previous ones. - parsed::FeatureMapEntry &name_and_feature = parsed_example[parsed_example_size - i - 1]; - const StringPiece &feature_name = name_and_feature.first; - parsed::Feature &feature = name_and_feature.second; - - if (column_name_id_map_.find(std::string(feature_name)) == column_name_id_map_.end()) { - MS_LOG(INFO) << "Feature name: " << feature_name << " is not in schema, skip it."; - continue; - } - - DataType example_dtype; - RETURN_IF_NOT_OK(feature.ParseDataType(&example_dtype)); - if (example_dtype == DataType::DE_UNKNOWN) { - continue; - } - - const auto column_index = column_name_id_map_[std::string(feature_name)]; - // If feature was already visited, skip. - if (feature_already_seen[column_index]) { - LogFeatureRepeated(feature_name); - continue; - } - feature_already_seen[column_index] = true; - - const ColDescriptor &column_descriptor = data_schema_.Column(column_index); - if (column_descriptor.HasKnownShape()) { - RETURN_IF_NOT_OK(ParseSerializedKnownShapeColumn(feature, parsed_row, string_column_map, column_index, - tensor_index, feature_name, column_descriptor, example_dtype)); - } else { // if variable length - RETURN_IF_NOT_OK(ParseSerializedVarLenColumn(feature, &(*varlen_tensor_vector)[column_index], feature_name, - column_descriptor, example_dtype)); - } - } - - for (int32_t column_index = 0; column_index < data_schema_.NumColumns(); ++column_index) { - if (!feature_already_seen[column_index]) { - RETURN_STATUS_UNEXPECTED("Feature name: " + data_schema_.Column(column_index).Name() + - " is required in schema but could not be found in tfrecord file."); - } - } - return Status::OK(); -} - -Status ParseExampleOp::ConstructColumnMap(const std::string &example_bytes) { - if (column_name_id_map_.empty()) { - if (data_schema_.Empty()) { - dataengine::Example example; - if (!example.ParseFromString(example_bytes)) { - RETURN_STATUS_UNEXPECTED("Failed to parse example bytes: " + std::string(example_bytes)); - } - - const dataengine::Features &example_features = example.features(); - const google::protobuf::Map &feature_map = example_features.feature(); - if (column_list_.empty()) { - (void)std::transform(feature_map.begin(), feature_map.end(), std::back_inserter(column_list_), - [](const auto &it) -> std::string { return it.first; }); - std::sort(column_list_.begin(), column_list_.end()); - } - - for (const auto &column_name : column_list_) { - auto it = feature_map.find(column_name); - if (it == feature_map.end()) { - RETURN_STATUS_UNEXPECTED("Invalid column list, failed to find column name: " + column_name + " in example."); - } - - std::string column_type; - const dataengine::Feature &feature = it->second; - switch (feature.kind_case()) { - case dataengine::Feature::KindCase::kBytesList: - column_type = "uint8"; - break; - case dataengine::Feature::KindCase::kFloatList: - column_type = "float32"; - break; - case dataengine::Feature::KindCase::kInt64List: - column_type = "int64"; - break; - default: - RETURN_STATUS_UNEXPECTED("Unsupported column type, the column type of " + column_name + - " should be int64, float32 or string."); - } - RETURN_IF_NOT_OK( - data_schema_.AddColumn(ColDescriptor(column_name, DataType(column_type), TensorImpl::kFlexible, 1))); - } - } - RETURN_IF_NOT_OK(data_schema_.GetColumnNameMap(&column_name_id_map_)); - CHECK_FAIL_RETURN_UNEXPECTED(!column_name_id_map_.empty(), "Can not get column name map, it is empty."); - } - return Status::OK(); -} - -void ParseExampleOp::CheckAndInitPool() { - if (parallel_parse_ && pool_ == nullptr) { - pool_ = std::make_unique(kThreadPoolSize); - } -} -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h b/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h deleted file mode 100644 index 2d31d2632..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PARSE_EXAMPLE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PARSE_EXAMPLE_OP_H_ - -#include - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -constexpr int kThreadPoolSize = 32; - -struct VarLenTensorBuffer { - std::vector> numeric_tensor; // store the minibatch of numeric tensors - std::vector string_tensor; // store the minibatch of strings - size_t string_length; // store the lengtn of string in minibatch -}; - -class ParseExampleOp : public TensorOp { - public: - ParseExampleOp(DataSchema data_schema, std::vector column_list, bool parallel_parse) - : data_schema_(std::move(data_schema)), - column_list_(std::move(column_list)), - parallel_parse_(parallel_parse), - pool_(nullptr) {} - - ~ParseExampleOp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kParseExampleOp; } - - private: - Status ParseSingleExample(const TensorRow &raw_bytes, TensorRow *parsed_row); - - Status ParallelParseExample(const TensorRow &raw_bytes, TensorRow *parsed_row); - - Status ParseSerializedExample(const std::string &example_bytes, TensorRow *parsed_row, - std::unordered_map> *string_column_map, - std::vector *varlen_tensor_vector, size_t tensor_index); - - Status ConstructColumnMap(const std::string &example_bytes); - - void CheckAndInitPool(); - - DataSchema data_schema_; - std::vector column_list_; - bool parallel_parse_; - std::unique_ptr pool_; - std::unordered_map column_name_id_map_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_PARSE_EXAMPLE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.cc b/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.cc deleted file mode 100644 index f1e55382b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.cc +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomApplyOp::RandomApplyOp(const std::vector> &ops, double prob) - : prob_(prob), rand_double_(0.0, 1.0) { - compose_ = std::make_unique(ops); -} - -uint32_t RandomApplyOp::NumOutput() { - if (compose_->NumOutput() != NumInput()) { - MS_LOG(WARNING) << "NumOutput!=NumInput (randomApply would randomly affect number of outputs)."; - return 0; - } - return compose_->NumOutput(); -} - -Status RandomApplyOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(compose_->OutputShape(inputs, outputs)); - // randomApply either runs all ops or do nothing. If the two methods don't give the same result. return unknown shape. - if (inputs != outputs) { // when RandomApply is not applied, input should be the same as output - outputs.clear(); - outputs.resize(NumOutput(), TensorShape::CreateUnknownRankShape()); - } - return Status::OK(); -} - -Status RandomApplyOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(compose_->OutputType(inputs, outputs)); - if (inputs != outputs) { // when RandomApply is not applied, input should be the same as output - outputs.clear(); - outputs.resize(NumOutput(), DataType(DataType::DE_UNKNOWN)); - } - return Status::OK(); -} - -Status RandomApplyOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - if (rand_double_(random_generator_) <= prob_) { - RETURN_IF_NOT_OK(compose_->Compute(input, output)); - } else { - *output = input; // copy over the tensors - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h b/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h deleted file mode 100644 index b9bc0f5a0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_APPLY_OP_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_APPLY_OP_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -class RandomApplyOp : public RandomTensorOp { - public: - /// constructor - /// \param[in] ops the list of TensorOps to apply with prob likelihood - /// \param[in] prob probability whether the list of TensorOps will be applied - RandomApplyOp(const std::vector> &ops, double prob); - - /// default destructor - ~RandomApplyOp() override = default; - - /// return the number of inputs the first tensorOp in compose takes - /// \return number of input tensors - uint32_t NumInput() override { return compose_->NumInput(); } - - /// return the number of outputs - /// \return number of output tensors - uint32_t NumOutput() override; - - /// return output shape if randomApply won't affect the output shape, otherwise return unknown shape - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - /// return output type if randomApply won't affect the output type, otherwise return unknown type - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - /// \param[in] input - /// \param[out] output - /// \return Status code - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomApplyOp; } - - private: - double prob_; - std::shared_ptr compose_; - std::uniform_real_distribution rand_double_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_APPLY_OP_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.cc b/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.cc deleted file mode 100644 index eccb9f9f5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.cc +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomChoiceOp::RandomChoiceOp(const std::vector> &ops) - : ops_(ops), rand_int_(0, ops.size() - 1) {} - -uint32_t RandomChoiceOp::NumInput() { - uint32_t num_input = ops_.front()->NumInput(); - for (auto &op : ops_) { - uint32_t cur_num = op->NumInput(); - if (num_input != cur_num && cur_num > 0) { - MS_LOG(WARNING) << "Unable to determine Num of Input, ops in RandomChoice don't take the same number of input."; - return 0; - } - } - return num_input; -} - -uint32_t RandomChoiceOp::NumOutput() { - uint32_t num_output = ops_.front()->NumOutput(); - for (auto &op : ops_) { - uint32_t cur_num = op->NumOutput(); - if (num_output != cur_num) { - MS_LOG(WARNING) << "Unable to determine NumOutput, ops in RandomChoice don't have the same number of output."; - return 0; - } - } - return num_output; -} - -Status RandomChoiceOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(ops_.front()->OutputShape(inputs, outputs)); - for (auto &op : ops_) { - std::vector out_shapes; - RETURN_IF_NOT_OK(op->OutputShape(inputs, out_shapes)); - if (outputs != out_shapes) { - MS_LOG(WARNING) << "TensorOp in RandomChoice don't return the same tensorShape."; - outputs.clear(); - outputs.resize(NumOutput(), TensorShape::CreateUnknownRankShape()); - return Status::OK(); - } - } - return Status::OK(); -} - -Status RandomChoiceOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(ops_.front()->OutputType(inputs, outputs)); - for (auto &op : ops_) { - std::vector out_types; - RETURN_IF_NOT_OK(op->OutputType(inputs, out_types)); - if (outputs != out_types) { - MS_LOG(WARNING) << "TensorOp in RandomChoice don't return the same tensorType."; - outputs.clear(); - outputs.resize(NumOutput(), DataType(DataType::DE_UNKNOWN)); - return Status::OK(); - } - } - return Status::OK(); -} - -Status RandomChoiceOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - size_t rand_num = rand_int_(random_generator_); - CHECK_FAIL_RETURN_UNEXPECTED(rand_num < ops_.size(), "invalid rand_num:" + std::to_string(rand_num)); - RETURN_IF_NOT_OK(ops_[rand_num]->Compute(input, output)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h b/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h deleted file mode 100644 index 67c55ef25..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_CHOICE_OP_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_CHOICE_OP_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -class RandomChoiceOp : public RandomTensorOp { - public: - /// constructor - /// \param[in] ops list of TensorOps to randomly choose 1 from - explicit RandomChoiceOp(const std::vector> &ops); - - /// default destructor - ~RandomChoiceOp() override = default; - - /// return the number of inputs. All op in ops_ should have the same number of inputs - /// \return number of input tensors - uint32_t NumInput() override; - - /// return the number of outputs. All op in ops_ should have the same number of outputs - /// \return number of input tensors - uint32_t NumOutput() override; - - /// return output shape if all ops in ops_ return the same shape, otherwise return unknown shape - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - /// return output type if all ops in ops_ return the same type, otherwise return unknown type - /// \param[in] inputs - /// \param[out] outputs - /// \return Status code - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - /// \param[in] input - /// \param[out] output - /// \return Status code - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomChoiceOp; } - - private: - std::vector> ops_; - std::uniform_int_distribution rand_int_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_RANDOM_CHOICE_OP_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/slice_op.cc b/mindspore-lite/minddata/dataset/kernels/data/slice_op.cc deleted file mode 100644 index 96db1563d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/slice_op.cc +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/slice_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status SliceOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return input->Slice(output, slice_options_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/slice_op.h b/mindspore-lite/minddata/dataset/kernels/data/slice_op.h deleted file mode 100644 index 70097ddeb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/slice_op.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_SLICE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_SLICE_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_helpers.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class SliceOp : public TensorOp { - public: - explicit SliceOp(std::vector slice_input) : slice_options_(std::move(slice_input)) {} - - explicit SliceOp(const SliceOption &slice_option) { slice_options_.push_back(slice_option); } - - // short hand notation for slicing along fist dimension - explicit SliceOp(Slice slice) { (void)slice_options_.emplace_back(slice); } - - explicit SliceOp(bool all) { (void)slice_options_.emplace_back(all); } - - explicit SliceOp(const std::vector &indices) { (void)slice_options_.emplace_back(indices); } - - ~SliceOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kSliceOp; } - - private: - std::vector slice_options_ = {}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_SLICE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.cc b/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.cc deleted file mode 100644 index 6543e7571..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/to_float16_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status ToFloat16Op::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return ToFloat16(input, output); -} - -Status ToFloat16Op::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "ToFloat16: inputs cannot be empty."); - outputs[0] = DataType(DataType::DE_FLOAT16); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.h b/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.h deleted file mode 100644 index dd107d3f7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/to_float16_op.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TO_FLOAT16_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TO_FLOAT16_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class ToFloat16Op : public TensorOp { - public: - ToFloat16Op() = default; - - ~ToFloat16Op() override = default; - - // Overrides the base class compute function - // Calls the ToFloat16 function in ImageUtils, this function takes an input tensor - // and transforms its data to float16, the output memory is manipulated to contain the result - // @return Status The status code returned - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kToFloat16Op; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TO_FLOAT16_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.cc b/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.cc deleted file mode 100644 index 97bb58605..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.cc +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -TypeCastOp::TypeCastOp(const DataType &new_type) : type_(new_type) {} - -TypeCastOp::TypeCastOp(const std::string &data_type) { type_ = DataType(data_type); } - -Status TypeCastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return TypeCast(input, output, type_); -} - -Status TypeCastOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "TypeCast: inputs cannot be empty."); - outputs[0] = type_; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h b/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h deleted file mode 100644 index 060923fbe..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TYPE_CAST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TYPE_CAST_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class TypeCastOp : public TensorOp { - public: - // Constructor for TypecastOp - // @param data_type datatype to cast to - explicit TypeCastOp(const DataType &data_type); - - // Constructor for TypecastOp - // @param data_type datatype to cast to - explicit TypeCastOp(const std::string &data_type); - - ~TypeCastOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kTypeCastOp; } - - private: - DataType type_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_TYPE_CAST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/data/unique_op.cc b/mindspore-lite/minddata/dataset/kernels/data/unique_op.cc deleted file mode 100644 index 3278b7881..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/unique_op.cc +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/data/unique_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -Status UniqueOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - CHECK_FAIL_RETURN_UNEXPECTED(input.size() == 1, - "Unique: only support 1D input, got rank: " + std::to_string(input.size())); - - auto in_tensor = input[0]; - auto in_tensor_shape = in_tensor->shape(); - auto in_tensor_type = in_tensor->type(); - - CHECK_FAIL_RETURN_UNEXPECTED(in_tensor_type.IsNumeric(), - "Unique: only support numeric datatype of input, got string."); - CHECK_FAIL_RETURN_UNEXPECTED( - in_tensor_shape.Rank() >= 2, - "Unique: input must be at least 2-D in order to do unique op, got rank:" + std::to_string(in_tensor_shape.Rank())); - CHECK_FAIL_RETURN_UNEXPECTED(in_tensor->Size() <= std::numeric_limits::max(), - "Unique: Unique does not support size of input tensor large than: " + - std::to_string(std::numeric_limits::max()) + - ", got:" + std::to_string(in_tensor->Size())); - - RETURN_IF_NOT_OK(in_tensor->Reshape(TensorShape({in_tensor->Size()}))); - - std::shared_ptr out; - std::shared_ptr out_idx; - std::shared_ptr out_cnt; - - RETURN_IF_NOT_OK(Unique(in_tensor, &out, &out_idx, &out_cnt)); - output->push_back(out); - output->push_back(out_idx); - output->push_back(out_cnt); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/data/unique_op.h b/mindspore-lite/minddata/dataset/kernels/data/unique_op.h deleted file mode 100644 index 85e968eba..000000000 --- a/mindspore-lite/minddata/dataset/kernels/data/unique_op.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_UNIQUE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_UNIQUE_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" - -namespace mindspore { -namespace dataset { -class UniqueOp : public TensorOp { - public: - UniqueOp() = default; - - ~UniqueOp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - uint32_t NumOutput() override { return 0; } - - std::string Name() const override { return kUniqueOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_DATA_UNIQUE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/CMakeLists.txt b/mindspore-lite/minddata/dataset/kernels/image/CMakeLists.txt deleted file mode 100644 index f9b4bda69..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/CMakeLists.txt +++ /dev/null @@ -1,90 +0,0 @@ -file(GLOB_RECURSE _CURRENT_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") -set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_MD) -add_subdirectory(lite_cv) -add_subdirectory(dvpp) - -if(ENABLE_FFMPEG) - set(FFMPEG_DEPEND_FILES - decode_video_op.cc - video_utils.cc) -endif() - -add_library(kernels-image OBJECT - adjust_brightness_op.cc - adjust_contrast_op.cc - adjust_gamma_op.cc - adjust_hue_op.cc - adjust_saturation_op.cc - affine_op.cc - auto_augment_op.cc - auto_contrast_op.cc - bounding_box.cc - center_crop_op.cc - convert_color_op.cc - crop_op.cc - cut_out_op.cc - cutmix_batch_op.cc - decode_op.cc - equalize_op.cc - erase_op.cc - gaussian_blur_op.cc - horizontal_flip_op.cc - hwc_to_chw_op.cc - image_utils.cc - invert_op.cc - math_utils.cc - mixup_batch_op.cc - normalize_op.cc - normalize_pad_op.cc - pad_op.cc - pad_to_size_op.cc - perspective_op.cc - posterize_op.cc - rand_augment_op.cc - random_adjust_sharpness_op.cc - random_affine_op.cc - random_auto_contrast_op.cc - random_color_adjust_op.cc - random_crop_decode_resize_op.cc - random_crop_and_resize_with_bbox_op.cc - random_crop_and_resize_op.cc - random_crop_op.cc - random_crop_with_bbox_op.cc - random_equalize_op.cc - random_horizontal_flip_op.cc - random_horizontal_flip_with_bbox_op.cc - random_invert_op.cc - random_lighting_op.cc - bounding_box_augment_op.cc - random_posterize_op.cc - random_resize_op.cc - random_rotation_op.cc - random_select_subpolicy_op.cc - random_solarize_op.cc - random_vertical_flip_op.cc - random_vertical_flip_with_bbox_op.cc - random_sharpness_op.cc - rescale_op.cc - resize_op.cc - resize_preserve_ar_op.cc - resized_crop_op.cc - rgb_to_bgr_op.cc - rgb_to_gray_op.cc - rgba_to_bgr_op.cc - rgba_to_rgb_op.cc - sharpness_op.cc - slice_patches_op.cc - solarize_op.cc - swap_red_blue_op.cc - uniform_aug_op.cc - resize_with_bbox_op.cc - random_resize_with_bbox_op.cc - random_color_op.cc - rotate_op.cc - resize_cubic_op.cc - trivial_augment_wide_op.cc - vertical_flip_op.cc - to_tensor_op.cc - ${FFMPEG_DEPEND_FILES}) - -add_dependencies(kernels-image kernels-dvpp-image) diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.cc b/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.cc deleted file mode 100644 index f3a8528fd..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status AdjustBrightnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - return AdjustBrightness(input, output, brightness_factor_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h b/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h deleted file mode 100644 index e02ebf3d7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_BRIGHTNESS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_BRIGHTNESS_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AdjustBrightnessOp : public TensorOp { - public: - explicit AdjustBrightnessOp(float brightness_factor) : brightness_factor_(brightness_factor) {} - - ~AdjustBrightnessOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kAdjustBrightnessOp; } - - private: - float brightness_factor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_BRIGHTNESS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.cc b/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.cc deleted file mode 100644 index 19712d281..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status AdjustContrastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - return AdjustContrast(input, output, contrast_factor_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h b/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h deleted file mode 100644 index 8ca54b6ae..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_CONTRAST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_CONTRAST_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AdjustContrastOp : public TensorOp { - public: - explicit AdjustContrastOp(float contrast_factor) : contrast_factor_(contrast_factor) {} - - ~AdjustContrastOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kAdjustContrastOp; } - - private: - float contrast_factor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CONTRAST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.cc b/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.cc deleted file mode 100644 index 6a0f597bd..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.cc +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -constexpr float AdjustGammaOp::kGain = 1.0; - -Status AdjustGammaOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - // typecast - CHECK_FAIL_RETURN_UNEXPECTED( - !input->type().IsString(), - "AdjustGamma: input tensor type should be int, float or double, but got: " + input->type().ToString()); - - if (input->type().IsFloat()) { - std::shared_ptr input_tensor; - RETURN_IF_NOT_OK(TypeCast(input, &input_tensor, DataType(DataType::DE_FLOAT32))); - return AdjustGamma(input_tensor, output, gamma_, gain_); - } else { - return AdjustGamma(input, output, gamma_, gain_); - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h b/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h deleted file mode 100644 index 9c33d946f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AdjustGammaOp : public TensorOp { - public: - /// Default gain to be used - static const float kGain; - AdjustGammaOp(float gamma, float gain) : gamma_(gamma), gain_(gain) {} - - ~AdjustGammaOp() override = default; - - /// Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const AdjustGammaOp &so) { - so.Print(out); - return out; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kAdjustGammaOp; } - - private: - float gamma_; - float gain_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.cc b/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.cc deleted file mode 100644 index 8f3484b21..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status AdjustHueOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - return AdjustHue(input, output, hue_factor_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h b/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h deleted file mode 100644 index 1148011a8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_HUE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_HUE_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AdjustHueOp : public TensorOp { - public: - explicit AdjustHueOp(float hue_factor) : hue_factor_(hue_factor) {} - - ~AdjustHueOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kAdjustHueOp; } - - private: - float hue_factor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_HUE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.cc b/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.cc deleted file mode 100644 index 20e9eb39e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status AdjustSaturationOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - return AdjustSaturation(input, output, saturation_factor_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h b/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h deleted file mode 100644 index 26a1e79dc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_SATURATION_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_SATURATION_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AdjustSaturationOp : public TensorOp { - public: - explicit AdjustSaturationOp(float saturation_factor) : saturation_factor_(saturation_factor) {} - - ~AdjustSaturationOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kAdjustSaturationOp; } - - private: - float saturation_factor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_SATURATION_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/affine_op.cc b/mindspore-lite/minddata/dataset/kernels/image/affine_op.cc deleted file mode 100644 index d2b4704b7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/affine_op.cc +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -const InterpolationMode AffineOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; -const float_t AffineOp::kDegrees = 0.0; -const std::vector AffineOp::kTranslation = {0.0, 0.0}; -const float_t AffineOp::kScale = 1.0; -const std::vector AffineOp::kShear = {0.0, 0.0}; -const std::vector AffineOp::kFillValue = {0, 0, 0}; - -AffineOp::AffineOp(float_t degrees, const std::vector &translation, float_t scale, - const std::vector &shear, InterpolationMode interpolation, - const std::vector &fill_value) - : degrees_(degrees), - translation_(translation), - scale_(scale), - shear_(shear), - interpolation_(interpolation), - fill_value_(fill_value) {} - -Status AffineOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - CHECK_FAIL_RETURN_UNEXPECTED( - input->shape().Size() == kMinImageRank || input->shape().Size() == kDefaultImageRank, - "Affine: input tensor is not in shape of or , but got rank: " + std::to_string(input->shape().Size())); - dsize_t height = input->shape()[0]; - dsize_t width = input->shape()[1]; - float_t translation_x = translation_[0] * static_cast(width); - float_t translation_y = translation_[1] * static_cast(height); - std::vector new_translation{translation_x, translation_y}; - if (fill_value_.size() == 1) { - fill_value_ = {fill_value_[0], fill_value_[0], fill_value_[0]}; - } - return Affine(input, output, degrees_, new_translation, scale_, shear_, interpolation_, fill_value_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/affine_op.h b/mindspore-lite/minddata/dataset/kernels/image/affine_op.h deleted file mode 100644 index bf4235d03..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/affine_op.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AFFINE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AFFINE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AffineOp : public TensorOp { - public: - /// Default values - static const float_t kDegrees; - static const std::vector kTranslation; - static const float_t kScale; - static const std::vector kShear; - static const InterpolationMode kDefInterpolation; - static const std::vector kFillValue; - - /// Constructor - explicit AffineOp(float_t degrees, const std::vector &translation = kTranslation, float_t scale = kScale, - const std::vector &shear = kShear, InterpolationMode interpolation = kDefInterpolation, - const std::vector &fill_value = kFillValue); - - ~AffineOp() override = default; - - std::string Name() const override { return kAffineOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - protected: - float_t degrees_; - std::vector translation_; // translation_x and translation_y - float_t scale_; - std::vector shear_; // shear_x and shear_y - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AFFINE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.cc b/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.cc deleted file mode 100644 index fdbb89fb2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.cc +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -AutoAugmentOp::AutoAugmentOp(AutoAugmentPolicy policy, InterpolationMode interpolation, - const std::vector &fill_value) - : policy_(policy), interpolation_(interpolation), fill_value_(fill_value) { - transforms_ = GetTransforms(policy); -} - -Transforms AutoAugmentOp::GetTransforms(AutoAugmentPolicy policy) { - if (policy == AutoAugmentPolicy::kImageNet) { - return {{{"Posterize", 0.4, 8}, {"Rotate", 0.6, 9}}, {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, - {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}}, {{"Posterize", 0.6, 7}, {"Posterize", 0.6, 6}}, - {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, {{"Equalize", 0.4, -1}, {"Rotate", 0.8, 8}}, - {{"Solarize", 0.6, 3}, {"Equalize", 0.6, -1}}, {{"Posterize", 0.8, 5}, {"Equalize", 1.0, -1}}, - {{"Rotate", 0.2, 3}, {"Solarize", 0.6, 8}}, {{"Equalize", 0.6, -1}, {"Posterize", 0.4, 6}}, - {{"Rotate", 0.8, 8}, {"Color", 0.4, 0}}, {{"Rotate", 0.4, 9}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.0, -1}, {"Equalize", 0.8, -1}}, {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, - {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, {{"Rotate", 0.8, 8}, {"Color", 1.0, 2}}, - {{"Color", 0.8, 8}, {"Solarize", 0.8, 7}}, {{"Sharpness", 0.4, 7}, {"Invert", 0.6, -1}}, - {{"ShearX", 0.6, 5}, {"Equalize", 1.0, -1}}, {{"Color", 0.4, 0}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, - {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, - {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}}}; - } else if (policy == AutoAugmentPolicy::kCifar10) { - return {{{"Invert", 0.1, -1}, {"Contrast", 0.2, 6}}, {{"Rotate", 0.7, 2}, {"TranslateX", 0.3, 9}}, - {{"Sharpness", 0.8, 1}, {"Sharpness", 0.9, 3}}, {{"ShearY", 0.5, 8}, {"TranslateY", 0.7, 9}}, - {{"AutoContrast", 0.5, -1}, {"Equalize", 0.9, -1}}, {{"ShearY", 0.2, 7}, {"Posterize", 0.3, 7}}, - {{"Color", 0.4, 3}, {"Brightness", 0.6, 7}}, {{"Sharpness", 0.3, 9}, {"Brightness", 0.7, 9}}, - {{"Equalize", 0.6, -1}, {"Equalize", 0.5, -1}}, {{"Contrast", 0.6, 7}, {"Sharpness", 0.6, 5}}, - {{"Color", 0.7, 7}, {"TranslateX", 0.5, 8}}, {{"Equalize", 0.3, -1}, {"AutoContrast", 0.4, -1}}, - {{"TranslateY", 0.4, 3}, {"Sharpness", 0.2, 6}}, {{"Brightness", 0.9, 6}, {"Color", 0.2, 8}}, - {{"Solarize", 0.5, 2}, {"Invert", 0.0, -1}}, {{"Equalize", 0.2, -1}, {"AutoContrast", 0.6, -1}}, - {{"Equalize", 0.2, -1}, {"Equalize", 0.6, -1}}, {{"Color", 0.9, 9}, {"Equalize", 0.6, -1}}, - {{"AutoContrast", 0.8, -1}, {"Solarize", 0.2, 8}}, {{"Brightness", 0.1, 3}, {"Color", 0.7, 0}}, - {{"Solarize", 0.4, 5}, {"AutoContrast", 0.9, -1}}, {{"TranslateY", 0.9, 9}, {"TranslateY", 0.7, 9}}, - {{"AutoContrast", 0.9, -1}, {"Solarize", 0.8, 3}}, {{"Equalize", 0.8, -1}, {"Invert", 0.1, -1}}, - {{"TranslateY", 0.7, 9}, {"AutoContrast", 0.9, -1}}}; - } else { - return {{{"ShearX", 0.9, 4}, {"Invert", 0.2, -1}}, {{"ShearY", 0.9, 8}, {"Invert", 0.7, -1}}, - {{"Equalize", 0.6, -1}, {"Solarize", 0.6, 6}}, {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, {{"ShearX", 0.9, 4}, {"AutoContrast", 0.8, -1}}, - {{"ShearY", 0.9, 8}, {"Invert", 0.4, -1}}, {{"ShearY", 0.9, 5}, {"Solarize", 0.2, 6}}, - {{"Invert", 0.9, -1}, {"AutoContrast", 0.8, -1}}, {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, - {{"ShearX", 0.9, 4}, {"Solarize", 0.3, 3}}, {{"ShearY", 0.8, 8}, {"Invert", 0.7, -1}}, - {{"Equalize", 0.9, -1}, {"TranslateY", 0.6, 6}}, {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, - {{"Contrast", 0.3, 3}, {"Rotate", 0.8, 4}}, {{"Invert", 0.8, -1}, {"TranslateY", 0.0, 2}}, - {{"ShearY", 0.7, 6}, {"Solarize", 0.4, 8}}, {{"Invert", 0.6, -1}, {"Rotate", 0.8, 4}}, - {{"ShearY", 0.3, 7}, {"TranslateX", 0.9, 3}}, {{"ShearX", 0.1, 6}, {"Invert", 0.6, -1}}, - {{"Solarize", 0.7, 2}, {"TranslateY", 0.6, 7}}, {{"ShearY", 0.8, 4}, {"Invert", 0.8, -1}}, - {{"ShearX", 0.7, 9}, {"TranslateY", 0.8, 3}}, {{"ShearY", 0.8, 5}, {"AutoContrast", 0.7, -1}}, - {{"ShearX", 0.7, 2}, {"Invert", 0.1, -1}}}; - } -} - -Status AutoAugmentOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("AutoAugment: input tensor is not in shape of , but got rank: " + - std::to_string(input->Rank())); - } - int num_channels = input->shape()[2]; - if (num_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("AutoAugment: channel of input image should be 3, but got: " + - std::to_string(num_channels)); - } - - int transform_id; - auto probs = std::make_shared>(2); - auto signs = std::make_shared>(2); - GetParams(transforms_.size(), &transform_id, probs, signs); - - std::vector image_size = {input->shape()[0], input->shape()[1]}; - std::shared_ptr img = input; - - const int num_augments = 2; - for (auto i = 0; i < num_augments; i++) { - std::string op_name = std::get<0>(transforms_[transform_id][i]); - float p = std::get<1>(transforms_[transform_id][i]); - int32_t magnitude_id = std::get<2>(transforms_[transform_id][i]); - if ((*probs)[i] <= p) { - Space space = GetSpace(10, image_size); - std::vector magnitudes = std::get<0>(space[op_name]); - bool sign = std::get<1>(space[op_name]); - float magnitude = 0.0; - if (magnitudes.size() != 1 && magnitude_id != -1) { - magnitude = magnitudes[magnitude_id]; - } - if (sign && (*signs)[i] == 0) { - magnitude *= -1.0; - } - RETURN_IF_NOT_OK(ApplyAugment(img, &img, op_name, magnitude, interpolation_, fill_value_)); - } - } - *output = img; - return Status::OK(); -} - -void AutoAugmentOp::GetParams(int transform_num, int *transform_id, const std::shared_ptr> &probs, - const std::shared_ptr> &signs) { - std::uniform_int_distribution id_dist(0, transform_num - 1); - *transform_id = id_dist(random_generator_); - std::uniform_real_distribution prob_dist(0.0, 1.0); - - (*probs)[0] = prob_dist(random_generator_); - (*probs)[1] = prob_dist(random_generator_); - - std::uniform_int_distribution sign_dist(0, 1); - - (*signs)[0] = sign_dist(random_generator_); - (*signs)[1] = sign_dist(random_generator_); -} - -Space AutoAugmentOp::GetSpace(int32_t num_bins, const std::vector &image_size) { - Space space = {{"ShearX", {Linspace(0.0, 0.3, num_bins), true}}, - {"ShearY", {Linspace(0.0, 0.3, num_bins), true}}, - {"TranslateX", {Linspace(0.0, 150.0F / 331.F * static_cast(image_size[1]), num_bins), true}}, - {"TranslateY", {Linspace(0.0, 150.0F / 331.F * static_cast(image_size[0]), num_bins), true}}, - {"Rotate", {Linspace(0.0, 30., num_bins), true}}, - {"Brightness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Color", {Linspace(0.0, 0.9, num_bins), true}}, - {"Contrast", {Linspace(0.0, 0.9, num_bins), true}}, - {"Sharpness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Posterize", - {Linspace(0.0, static_cast(num_bins) - 1.F, num_bins, - -4.0F / (static_cast(num_bins) - 1.F), 8., true), - false}}, - {"Solarize", {Linspace(256.0, 0.0, num_bins), false}}, - {"AutoContrast", {{0.}, false}}, - {"Equalize", {{0.}, false}}, - {"Invert", {{0.}, false}}}; - return space; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h b/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h deleted file mode 100644 index 132909545..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_AUGMENT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_AUGMENT_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -typedef std::vector>> Transforms; -typedef std::map, bool>> Space; - -namespace mindspore { -namespace dataset { -class AutoAugmentOp : public RandomTensorOp { - public: - AutoAugmentOp(AutoAugmentPolicy policy, InterpolationMode interpolation, const std::vector &fill_value); - - ~AutoAugmentOp() override = default; - - std::string Name() const override { return kAutoAugmentOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - private: - void GetParams(int transform_num, int *transform_id, const std::shared_ptr> &probs, - const std::shared_ptr> &signs); - - Transforms GetTransforms(AutoAugmentPolicy policy); - - Space GetSpace(int32_t num_bins, const std::vector &image_size); - - AutoAugmentPolicy policy_; - InterpolationMode interpolation_; - std::vector fill_value_; - Transforms transforms_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_AUGMENT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.cc b/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.cc deleted file mode 100644 index 240cd2717..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -const float AutoContrastOp::kCutOff = 0.0; -const std::vector AutoContrastOp::kIgnore = {}; - -Status AutoContrastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return AutoContrast(input, output, cutoff_, ignore_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h b/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h deleted file mode 100644 index 632566585..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_CONTRAST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_CONTRAST_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class AutoContrastOp : public TensorOp { - public: - /// Default cutoff to be used - static const float kCutOff; - /// Default ignore to be used - static const std::vector kIgnore; - - AutoContrastOp(const float &cutoff, const std::vector &ignore) : cutoff_(cutoff), ignore_(ignore) {} - - ~AutoContrastOp() override = default; - - /// Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const AutoContrastOp &so) { - so.Print(out); - return out; - } - - std::string Name() const override { return kAutoContrastOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - private: - float cutoff_; - std::vector ignore_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_AUTO_CONTRAST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/bounding_box.cc b/mindspore-lite/minddata/dataset/kernels/image/bounding_box.cc deleted file mode 100644 index 133df5316..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/bounding_box.cc +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -const uint8_t kNumOfCols = 4; -const float kEpsilon = 1e-4; - -BoundingBox::BoundingBox(bbox_float x, bbox_float y, bbox_float width, bbox_float height) - : x_(x), y_(y), width_(width), height_(height) {} - -Status BoundingBox::ReadFromTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox, - std::shared_ptr *bbox_out) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); - bbox_float x; - bbox_float y; - bbox_float width; - bbox_float height; - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&x, {index_of_bbox, 0})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&y, {index_of_bbox, 1})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&width, {index_of_bbox, 2})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&height, {index_of_bbox, 3})); - *bbox_out = std::make_shared(x, y, width, height); - return Status::OK(); -} - -Status BoundingBox::ValidateBoundingBoxes(const TensorRow &image_and_bbox) { - const int64_t kValidInputSize = 2; - if (image_and_bbox.size() != kValidInputSize) { - RETURN_STATUS_ERROR(StatusCode::kMDBoundingBoxInvalidShape, - "BoundingBox: invalid input, size of input data should be 2 " - "(including image and bounding box), but got: " + - std::to_string(image_and_bbox.size())); - } - if (image_and_bbox[1]->shape().Size() < 2) { - RETURN_STATUS_ERROR(StatusCode::kMDBoundingBoxInvalidShape, - "BoundingBox: bounding boxes should have to be two-dimensional matrix at least, " - "but got " + - std::to_string(image_and_bbox[1]->shape().Size()) + " dimension."); - } - if (image_and_bbox[0]->shape().Size() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED("Invalid data, input image hasn't been decoded, you may need to perform Decode first."); - } - - int64_t num_of_features = image_and_bbox[1]->shape()[1]; - if (num_of_features < kNumOfCols) { - RETURN_STATUS_ERROR( - StatusCode::kMDBoundingBoxInvalidShape, - "BoundingBox: bounding boxes should be have at least 4 features, but got: " + std::to_string(num_of_features)); - } - std::vector> bbox_list; - RETURN_IF_NOT_OK(GetListOfBoundingBoxes(image_and_bbox[1], &bbox_list)); - int64_t img_h = image_and_bbox[0]->shape()[0]; - int64_t img_w = image_and_bbox[0]->shape()[1]; - for (auto &bbox : bbox_list) { - // fix: 100.000008 > 100 or 30.000002 > 30 - if (bbox->x() + bbox->width() - img_w > kEpsilon || bbox->y() + bbox->height() - img_h > kEpsilon) { - RETURN_STATUS_ERROR( - StatusCode::kMDBoundingBoxOutOfBounds, - "BoundingBox: bounding boxes is out of bounds of the image, as image width: " + std::to_string(img_w) + - ", bbox width coordinate: " + std::to_string(bbox->x() + bbox->width()) + ", and image height: " + - std::to_string(img_h) + ", bbox height coordinate: " + std::to_string(bbox->y() + bbox->height())); - } - // fix: -0.000001 < 0.0 - if (bbox->x() < -kEpsilon || bbox->y() < -kEpsilon) { - RETURN_STATUS_ERROR(StatusCode::kMDBoundingBoxOutOfBounds, - "BoundingBox: the coordinates of the bounding boxes has negative value, got: (" + - std::to_string(bbox->x()) + "," + std::to_string(bbox->y()) + ")."); - } - } - return Status::OK(); -} - -Status BoundingBox::WriteToTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox) const { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 0}, x_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 1}, y_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 2}, width_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 3}, height_)); - return Status::OK(); -} - -Status BoundingBox::GetListOfBoundingBoxes(const TensorPtr &bbox_tensor, - std::vector> *bbox_out) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); - dsize_t num_of_boxes = bbox_tensor->shape()[0]; - for (dsize_t i = 0; i < num_of_boxes; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(ReadFromTensor(bbox_tensor, i, &bbox)); - bbox_out->push_back(bbox); - } - return Status::OK(); -} - -Status BoundingBox::CreateTensorFromBoundingBoxList(const std::vector> &bboxes, - TensorPtr *tensor_out) { - dsize_t num_of_boxes = bboxes.size(); - std::vector bboxes_for_tensor; - for (dsize_t i = 0; i < num_of_boxes; i++) { - bbox_float b_data[kNumOfCols] = {bboxes[i]->x(), bboxes[i]->y(), bboxes[i]->width(), bboxes[i]->height()}; - (void)bboxes_for_tensor.insert(bboxes_for_tensor.end(), b_data, b_data + kNumOfCols); - } - RETURN_IF_NOT_OK(Tensor::CreateFromVector(bboxes_for_tensor, TensorShape{num_of_boxes, kNumOfCols}, tensor_out)); - return Status::OK(); -} - -Status BoundingBox::PadBBoxes(const TensorPtr *bbox_list, size_t bbox_count, int32_t pad_top, int32_t pad_left) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); - for (dsize_t i = 0; i < bbox_count; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(ReadFromTensor(*bbox_list, i, &bbox)); - bbox->SetX(bbox->x() + static_cast(pad_left)); - bbox->SetY(bbox->y() + static_cast(pad_top)); - RETURN_IF_NOT_OK(bbox->WriteToTensor(*bbox_list, i)); - } - return Status::OK(); -} - -Status BoundingBox::UpdateBBoxesForCrop(TensorPtr *bbox_list, size_t *bbox_count, int32_t CB_Xmin, int32_t CB_Ymin, - int32_t CB_Xmax, int32_t CB_Ymax) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); - // PASS LIST, COUNT OF BOUNDING BOXES - // Also PAss X/Y Min/Max of image cropped region - normally obtained from 'GetCropBox' functions - std::vector correct_ind; - std::vector copyVals; - dsize_t bboxDim = (*bbox_list)->shape()[1]; - for (dsize_t i = 0; i < *bbox_count; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(ReadFromTensor(*bbox_list, i, &bbox)); - bbox_float bb_Xmax = bbox->x() + bbox->width(); - bbox_float bb_Ymax = bbox->y() + bbox->height(); - // check for image / BB overlap - if (bbox->x() > static_cast(CB_Xmax) || bbox->y() > static_cast(CB_Ymax) || - bb_Xmax < static_cast(CB_Xmin) || bb_Ymax < static_cast(CB_Ymin)) { - continue; // no overlap found - } - // Update this bbox and select it to move to the final output tensor - correct_ind.push_back(i); - // adjust BBox corners by bringing into new CropBox if beyond - // Also resetting/adjusting for boxes to lie within CropBox instead of Image - subtract CropBox Xmin/YMin - - bbox_float bb_Xmin = - bbox->x() - std::min(0.F, bbox->x() - static_cast(CB_Xmin)) - static_cast(CB_Xmin); - bbox_float bb_Ymin = - bbox->y() - std::min(0.F, bbox->y() - static_cast(CB_Ymin)) - static_cast(CB_Ymin); - bb_Xmax = bb_Xmax - std::max(0.F, bb_Xmax - static_cast(CB_Xmax)) - static_cast(CB_Xmin); - bb_Ymax = bb_Ymax - std::max(0.F, bb_Ymax - static_cast(CB_Ymax)) - static_cast(CB_Ymin); - - // bound check for float values - bb_Xmin = std::max(bb_Xmin, static_cast(0)); - bb_Ymin = std::max(bb_Ymin, static_cast(0)); - bb_Xmax = std::min(bb_Xmax, static_cast(CB_Xmax - CB_Xmin)); // find max value relative to new image - bb_Ymax = std::min(bb_Ymax, static_cast(CB_Ymax - CB_Ymin)); - - // reset min values and calculate width/height from Box corners - bbox->SetX(bb_Xmin); - bbox->SetY(bb_Ymin); - bbox->SetWidth(bb_Xmax - bb_Xmin); - bbox->SetHeight(bb_Ymax - bb_Ymin); - RETURN_IF_NOT_OK(bbox->WriteToTensor(*bbox_list, i)); - } - // create new tensor and copy over bboxes still valid to the image - // bboxes outside of new cropped region are ignored - empty tensor returned in case of none - *bbox_count = correct_ind.size(); - bbox_float temp = 0.0; - for (auto slice : correct_ind) { // for every index in the loop - for (dsize_t ix = 0; ix < bboxDim; ix++) { - RETURN_IF_NOT_OK((*bbox_list)->GetItemAt(&temp, {slice, ix})); - copyVals.push_back(temp); - } - } - std::shared_ptr retV; - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(copyVals, TensorShape({static_cast(*bbox_count), bboxDim}), &retV)); - (*bbox_list) = retV; // reset pointer - return Status::OK(); -} - -Status BoundingBox::UpdateBBoxesForResize(const TensorPtr &bbox_list, size_t bbox_count, int32_t target_width, - int32_t target_height, int32_t orig_width, int32_t orig_height) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); - CHECK_FAIL_RETURN_UNEXPECTED(orig_width != 0, "BoundingBox: orig_width is zero."); - CHECK_FAIL_RETURN_UNEXPECTED(orig_height != 0, "BoundingBox: orig_height is zero."); - - // cast to float to preserve fractional - bbox_float W_aspRatio = static_cast(target_width) / static_cast(orig_width); - bbox_float H_aspRatio = static_cast(target_height) / static_cast(orig_height); - for (dsize_t i = 0; i < bbox_count; i++) { - // for each bounding box - std::shared_ptr bbox; - RETURN_IF_NOT_OK(ReadFromTensor(bbox_list, i, &bbox)); - - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->x()) > W_aspRatio, - "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->y()) > H_aspRatio, - "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->width()) > W_aspRatio, - "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->height()) > H_aspRatio, - "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); - - // update positions and widths - bbox->SetX(bbox->x() * W_aspRatio); - bbox->SetY(bbox->y() * H_aspRatio); - bbox->SetWidth(bbox->width() * W_aspRatio); - bbox->SetHeight(bbox->height() * H_aspRatio); - // reset bounding box values - RETURN_IF_NOT_OK(bbox->WriteToTensor(bbox_list, i)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/bounding_box.h b/mindspore-lite/minddata/dataset/kernels/image/bounding_box.h deleted file mode 100644 index 06677f680..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/bounding_box.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class BoundingBox { - public: - typedef float_t bbox_float; - - /// \brief Constructor for BoundingBox - /// \param[in] x horizontal axis coordinate of bounding box - /// \param[in] y vertical axis coordinate of bounding box - /// \param[in] width width of bounding box on horizontal axis - /// \param[in] height height of bounding box on vertical axis - BoundingBox(bbox_float x, bbox_float y, bbox_float width, bbox_float height); - - ~BoundingBox() = default; - - /// \brief Provide stream operator for displaying a bounding box. - friend std::ostream &operator<<(std::ostream &out, const BoundingBox &bbox) { - out << "Bounding Box with (X,Y,W,H): (" << bbox.x_ << "," << bbox.y_ << "," << bbox.width_ << "," << bbox.height_ - << ")"; - return out; - } - - /// Getters - bbox_float x() const { return x_; } - bbox_float y() const { return y_; } - bbox_float width() const { return width_; } - bbox_float height() const { return height_; } - - /// Setters - void SetX(bbox_float x) { x_ = x; } - void SetY(bbox_float y) { y_ = y; } - void SetWidth(bbox_float w) { width_ = w; } - void SetHeight(bbox_float h) { height_ = h; } - - /// \brief Set the bounding box data to bbox at a certain index in the tensor. - /// \param[in] bbox_tensor tensor containing a list of bounding boxes of shape (m, n) where n >= 4 - /// and first 4 items of each row are x,y,w,h of the bounding box - /// \param[in] index_of_bbox index of bounding box to set to tensor - /// \returns Status status of bounding box set - Status WriteToTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox = 0) const; - - /// \brief Create a bounding box object from an item at a certain index in a tensor. - /// \param[in] bbox_tensor tensor containing a list of bounding boxes of shape (m, n) where n >= 4 - /// and first 4 items of each row are x,y,w,h of the bounding box - /// \param[in] index_of_bbox index of bounding box to fetch from the tensor - /// \param[out] bbox_out output bounding box - /// \returns Status status of bounding box fetch - static Status ReadFromTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox, - std::shared_ptr *bbox_out); - - /// \brief Validate a list of bounding boxes with respect to an image. - /// \param[in] image_and_bbox tensor containing a list of bounding boxes of shape (m, n) where n >= 4 - /// and first 4 items of each row are x,y,w,h of the bounding box and an image of shape (H, W, C) or (H, W) - /// \returns Status status of bounding box fetch - static Status ValidateBoundingBoxes(const TensorRow &image_and_bbox); - - /// \brief Get a list of bounding boxes from a tensor. - /// \param[in] bbox_tensor tensor containing a list of bounding boxes of shape (m, n) where n >= 4 - /// and first 4 items of each row are x,y,w,h of the bounding box - /// \param[out] bbox_out output vector of bounding boxes - /// \returns Status status of bounding box list fetch - static Status GetListOfBoundingBoxes(const TensorPtr &bbox_tensor, - std::vector> *bbox_out); - - /// \brief Creates a tensor from a list of bounding boxes. - /// \param[in] bboxes list of bounding boxes - /// \param[out] tensor_out output tensor - /// \returns Status status of tensor creation - static Status CreateTensorFromBoundingBoxList(const std::vector> &bboxes, - TensorPtr *tensor_out); - - /// \brief Updates bounding boxes with required Top and Left padding - /// \note Top and Left padding amounts required to adjust bboxs min X,Y values according to padding 'push' - /// Top/Left since images 0,0 coordinate is taken from top left - /// \param bboxList: A tensor containing bounding box tensors - /// \param bboxCount: total Number of bounding boxes - required within caller function to run update loop - /// \param pad_top: Total amount of padding applied to image top - /// \param pad_left: Total amount of padding applied to image left side - static Status PadBBoxes(const TensorPtr *bbox_list, size_t bbox_count, int32_t pad_top, int32_t pad_left); - - /// \brief Updates and checks bounding boxes for new cropped region of image - /// \param bbox_list: A tensor containing bounding box tensors - /// \param bbox_count: total Number of bounding boxes - required within caller function to run update loop - /// \param CB_Xmin: Image's CropBox Xmin coordinate - /// \param CB_Xmin: Image's CropBox Ymin coordinate - /// \param CB_Xmax: Image's CropBox Xmax coordinate - (Xmin + width) - /// \param CB_Xmax: Image's CropBox Ymax coordinate - (Ymin + height) - static Status UpdateBBoxesForCrop(TensorPtr *bbox_list, size_t *bbox_count, int32_t CB_Xmin, int32_t CB_Ymin, - int32_t CB_Xmax, int32_t CB_Ymax); - - /// \brief Updates bounding boxes for an Image Resize Operation - Takes in set of valid BBoxes - /// For e.g those that remain after a crop - /// \param bbox_list: A tensor containing bounding box tensors - /// \param bbox_count: total Number of bounding boxes - required within caller function to run update loop - /// \param target_width: required width of image post resize - /// \param target_height: required height of image post resize - /// \param orig_width: current width of image pre resize - /// \param orig_height: current height of image pre resize - static Status UpdateBBoxesForResize(const TensorPtr &bbox_list, size_t bbox_count, int32_t target_width, - int32_t target_height, int32_t orig_width, int32_t orig_height); - - private: - bbox_float x_; - bbox_float y_; - bbox_float width_; - bbox_float height_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.cc b/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.cc deleted file mode 100644 index 9ff49b668..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" - -namespace mindspore { -namespace dataset { -const float BoundingBoxAugmentOp::kDefRatio = 0.3; - -BoundingBoxAugmentOp::BoundingBoxAugmentOp(std::shared_ptr transform, float ratio) - : ratio_(ratio), uniform_(0.0, 1.0), transform_(std::move(transform)) {} - -Status BoundingBoxAugmentOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - uint32_t num_of_boxes = input[1]->shape()[0]; - std::shared_ptr crop_out; - std::shared_ptr res_out; - std::shared_ptr input_image; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input[0], &input_image)); - std::shared_ptr input_restore = CVTensor::AsCVTensor(input_image); - for (uint32_t i = 0; i < num_of_boxes; i++) { - // using a uniform distribution to ensure op happens with probability ratio_ - if (uniform_(random_generator_) < ratio_) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); - RETURN_IF_NOT_OK(Crop(input_restore, &crop_out, static_cast(bbox->x()), static_cast(bbox->y()), - static_cast(bbox->width()), static_cast(bbox->height()))); - // transform the cropped bbox region - TensorRow crop_out_row; - TensorRow res_out_row; - crop_out_row.push_back(crop_out); - res_out_row.push_back(res_out); - RETURN_IF_NOT_OK(transform_->Compute(crop_out_row, &res_out_row)); - // place the transformed region back in the restored input - std::shared_ptr res_img = CVTensor::AsCVTensor(res_out_row[0]); - // check if transformed crop is out of bounds of the box - if (res_img->mat().cols > static_cast(bbox->width()) || - res_img->mat().rows > static_cast(bbox->height()) || - res_img->mat().cols < static_cast(bbox->width()) || - res_img->mat().rows < static_cast(bbox->height())) { - // if so, resize to fit in the box - std::shared_ptr resize_op = - std::make_shared(static_cast(bbox->height()), static_cast(bbox->width())); - RETURN_IF_NOT_OK(resize_op->Compute(std::static_pointer_cast(res_img), &res_out_row[0])); - res_img = CVTensor::AsCVTensor(res_out_row[0]); - } - res_img->mat().copyTo(input_restore->mat()( - cv::Rect(static_cast(bbox->x()), static_cast(bbox->y()), res_img->mat().cols, res_img->mat().rows))); - } - } - (*output).push_back(std::static_pointer_cast(input_restore)); - (*output).push_back(input[1]); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h b/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h deleted file mode 100644 index cecb3e920..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_AUGMENT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_AUGMENT_OP_H_ - -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class BoundingBoxAugmentOp : public RandomTensorOp { - public: - // Default values, also used by python_bindings.cc - static const float kDefRatio; - - // Constructor for BoundingBoxAugmentOp - // @param std::shared_ptr transform transform: C++ operation to apply on select bounding boxes - // @param float ratio: ratio of bounding boxes to have the transform applied on - BoundingBoxAugmentOp(std::shared_ptr transform, float ratio); - - ~BoundingBoxAugmentOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const BoundingBoxAugmentOp &so) { - so.Print(out); - return out; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kBoundingBoxAugmentOp; } - - private: - float ratio_; - std::uniform_real_distribution uniform_; - std::shared_ptr transform_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_BOUNDING_BOX_AUGMENT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.cc b/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.cc deleted file mode 100644 index 9fe708713..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.cc +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -const int32_t CenterCropOp::kDefWidth = 0; - -Status CenterCropOp::CenterCropImg(const std::shared_ptr &input, std::shared_ptr *output) const { - RETURN_UNEXPECTED_IF_NULL(output); - int32_t top = crop_het_ - input->shape()[0]; // number of pixels to pad (top and bottom) - int32_t left = crop_wid_ - input->shape()[1]; - std::shared_ptr pad_image; - - const int64_t kMaxPadScale = 3; - CHECK_FAIL_RETURN_UNEXPECTED( - top < input->shape()[0] * kMaxPadScale && left < input->shape()[1] * kMaxPadScale, - "CenterCrop: Padding size cannot be more than 3 times of the original image size, got top padding: " + - std::to_string(top) + ", left padding: " + std::to_string(left) + ", while the original image size: " + - std::to_string(input->shape()[0]) + ", " + std::to_string(input->shape()[1])); - - const int32_t kDivisorOfHalf = 2; - if (top > 0 && left > 0) { - return Pad(input, output, (top + 1) / kDivisorOfHalf, top / kDivisorOfHalf, (left + 1) / kDivisorOfHalf, - left / kDivisorOfHalf, BorderType::kConstant); - } else if (top > 0) { - RETURN_IF_NOT_OK( - Pad(input, &pad_image, (top + 1) / kDivisorOfHalf, top / kDivisorOfHalf, 0, 0, BorderType::kConstant)); - return Crop(pad_image, output, (static_cast(pad_image->shape()[1]) - crop_wid_) / kDivisorOfHalf, - (static_cast(pad_image->shape()[0]) - crop_het_) / kDivisorOfHalf, crop_wid_, crop_het_); - } else if (left > 0) { - RETURN_IF_NOT_OK( - Pad(input, &pad_image, 0, 0, (left + 1) / kDivisorOfHalf, left / kDivisorOfHalf, BorderType::kConstant)); - return Crop(pad_image, output, (static_cast(pad_image->shape()[1]) - crop_wid_) / kDivisorOfHalf, - (static_cast(pad_image->shape()[0]) - crop_het_) / kDivisorOfHalf, crop_wid_, crop_het_); - } else { - return Crop(input, output, (static_cast(input->shape()[1]) - crop_wid_) / kDivisorOfHalf, - (static_cast(input->shape()[0]) - crop_het_) / kDivisorOfHalf, crop_wid_, crop_het_); - } -} - -Status CenterCropOp::ConstructShape(const TensorShape &in_shape, std::shared_ptr *out_shape) const { - RETURN_UNEXPECTED_IF_NULL(out_shape); - auto in_shape_vec = in_shape.AsVector(); - const int h_index = -3; - const int w_index = -2; - in_shape_vec[in_shape_vec.size() + h_index] = crop_het_; - in_shape_vec[in_shape_vec.size() + w_index] = crop_wid_; - - *out_shape = std::make_shared(in_shape_vec); - - return Status::OK(); -} - -Status CenterCropOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - dsize_t rank = input->shape().Rank(); - CHECK_FAIL_RETURN_UNEXPECTED( - rank >= kMinImageRank, - "CenterCrop: Input tensor should have at least 2 dimensions, but got: " + std::to_string(rank)); - CHECK_FAIL_RETURN_UNEXPECTED(crop_het_ > 0 && crop_wid_ > 0, - "CenterCrop: Crop size should be positive, but got crop height: " + - std::to_string(crop_het_) + ", crop width: " + std::to_string(crop_wid_)); - - if (rank <= kDefaultImageRank) { // images - RETURN_IF_NOT_OK(CenterCropImg(input, output)); - } else { // deal with videos - // reshape input to nhwc - auto input_shape = input->shape(); - dsize_t num_batch = input->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and center crop N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (auto &image : input_vector_hwc) { - std::shared_ptr center_crop; - RETURN_IF_NOT_OK(CenterCropImg(image, ¢er_crop)); - output_vector_hwc.push_back(center_crop); - } - - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, output)); - // reshape output before return, only height and width are changed - std::shared_ptr output_shape; - RETURN_IF_NOT_OK(ConstructShape(input_shape, &output_shape)); - RETURN_IF_NOT_OK((*output)->Reshape(*output_shape)); - } - - return Status::OK(); -} - -void CenterCropOp::Print(std::ostream &out) const { - out << "CenterCropOp: " - << "cropWidth: " << crop_wid_ << "cropHeight: " << crop_het_ << "\n"; -} - -Status CenterCropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out = TensorShape{crop_het_, crop_wid_}; - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "CenterCrop: inputs cannot be empty."); - if (inputs[0].Rank() == kMinImageRank) { - (void)outputs.emplace_back(out); - } else if (inputs[0].Rank() == kDefaultImageRank) { - (void)outputs.emplace_back(out.AppendDim(inputs[0][kChannelIndexHWC])); - } else if (inputs[0].Rank() > kDefaultImageRank) { - std::shared_ptr output_shape; - RETURN_IF_NOT_OK(ConstructShape(inputs[0], &output_shape)); - (void)outputs.emplace_back(*output_shape); - } - CHECK_FAIL_RETURN_UNEXPECTED( - !outputs.empty(), - "CenterCrop: input tensor should have at least 2 dimensions, but got: " + std::to_string(inputs[0].Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h b/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h deleted file mode 100644 index 6cf77a2a7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CENTER_CROP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CENTER_CROP_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class CenterCropOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const int32_t kDefWidth; - - explicit CenterCropOp(int32_t het, int32_t wid = kDefWidth) : crop_het_(het), crop_wid_(wid == 0 ? het : wid) {} - - ~CenterCropOp() override = default; - - void Print(std::ostream &out) const override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kCenterCropOp; } - - private: - Status CenterCropImg(const std::shared_ptr &input, std::shared_ptr *output) const; - - Status ConstructShape(const TensorShape &in_shape, std::shared_ptr *out_shape) const; - - int32_t crop_het_; - int32_t crop_wid_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CENTER_CROP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.cc b/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.cc deleted file mode 100644 index 3a1c9963d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.cc +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h" - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -ConvertColorOp::ConvertColorOp(ConvertMode convert_mode) : convert_mode_(convert_mode) {} - -Status ConvertColorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return ConvertColor(input, output, convert_mode_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h b/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h deleted file mode 100644 index 0ecd7c2b9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CONVERT_COLOR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CONVERT_COLOR_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ConvertColorOp : public TensorOp { - public: - explicit ConvertColorOp(ConvertMode convert_mode); - - ~ConvertColorOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kConvertColorOp; } - - private: - ConvertMode convert_mode_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CONVERT_COLOR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/crop_op.cc b/mindspore-lite/minddata/dataset/kernels/image/crop_op.cc deleted file mode 100644 index 13a47efab..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/crop_op.cc +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/crop_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status CropOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageRank("Crop", input->shape().Size())); - auto input_h = static_cast(input->shape()[0]); - auto input_w = static_cast(input->shape()[1]); - CHECK_FAIL_RETURN_UNEXPECTED(y_ + height_ <= input_h, "Crop: Crop height dimension: " + std::to_string(y_ + height_) + - " exceeds image height: " + std::to_string(input_h)); - CHECK_FAIL_RETURN_UNEXPECTED(x_ + width_ <= input_w, "Crop: Crop width dimension: " + std::to_string(x_ + width_) + - " exceeds image width: " + std::to_string(input_w)); - return Crop(input, output, x_, y_, width_, height_); -} - -Status CropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out = TensorShape{height_, width_}; - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Crop: inputs cannot be empty."); - if (inputs[0].Rank() == kMinImageRank) { - (void)outputs.emplace_back(out); - } - if (inputs[0].Rank() == kDefaultImageRank) { - (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), - "Crop: invalid input shape, expected 2D or 3D input, but got input dimension is:" + - std::to_string(inputs[0].Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/crop_op.h b/mindspore-lite/minddata/dataset/kernels/image/crop_op.h deleted file mode 100644 index e49077c8f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/crop_op.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CROP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CROP_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class CropOp : public TensorOp { - public: - /// \brief Constructor to Crop Op - /// \param[in] y - the vertical starting coordinate - /// \param[in] x - the horizontal starting coordinate - /// \param[in] height - the height of the crop box - /// \param[in] width - the width of the crop box - explicit CropOp(int32_t y, int32_t x, int32_t height, int32_t width) : y_(y), x_(x), height_(height), width_(width) {} - - CropOp(const CropOp &rhs) = default; - - CropOp(CropOp &&rhs) = default; - - ~CropOp() override = default; - - void Print(std::ostream &out) const override { - out << "CropOp y: " << y_ << " x: " << x_ << " h: " << height_ << " w: " << width_; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kCropOp; } - - protected: - int32_t y_; - int32_t x_; - int32_t height_; - int32_t width_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CROP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.cc b/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.cc deleted file mode 100644 index 59d84bc7a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.cc +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - - * 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. -*/ -#include "mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const bool CutOutOp::kDefRandomColor = false; - -// constructor -CutOutOp::CutOutOp(int32_t box_height, int32_t box_width, int32_t num_patches, bool random_color, - std::vector fill_colors, bool is_hwc) - : box_height_(box_height), - box_width_(box_width), - num_patches_(num_patches), - random_color_(random_color), - fill_colors_(std::move(fill_colors)), - is_hwc_(is_hwc) {} - -// main function call for cut out -Status CutOutOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - std::shared_ptr inputCV = CVTensor::AsCVTensor(input); - // cut out will clip the erasing area if the box is near the edge of the image and the boxes are black - RETURN_IF_NOT_OK(CutOut(inputCV, output, box_height_, box_width_, num_patches_, false, random_color_, - &random_generator_, fill_colors_, is_hwc_)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h b/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h deleted file mode 100644 index 6abf802d9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUT_OUT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUT_OUT_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class CutOutOp : public RandomTensorOp { - public: - // Default values, also used by python_bindings.cc - static const bool kDefRandomColor; - - // Constructor for CutOutOp - // @param box_height box height - // @param box_width box_width - // @param num_patches how many patches to erase from image - // @param random_color boolean value to indicate fill patch with random color - // @param fill_colors value for the color to fill patch with - // @param is_hwc Check if input is HWC/CHW format - // @note maybe using unsigned long int isn't the best here according to our coding rules - CutOutOp(int32_t box_height, int32_t box_width, int32_t num_patches, bool random_color = kDefRandomColor, - std::vector fill_colors = {}, bool is_hwc = true); - - ~CutOutOp() override = default; - - void Print(std::ostream &out) const override { - out << "CutOut:: box_height: " << box_height_ << " box_width: " << box_width_ << " num_patches: " << num_patches_; - } - - // Overrides the base class compute function - // Calls the erase function in ImageUtils, this function takes an input tensor - // and overwrites some of its data using openCV, the output memory is manipulated to contain the result - // @return Status The status code returned - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kCutOutOp; } - - private: - int32_t box_height_; - int32_t box_width_; - int32_t num_patches_; - bool random_color_; - std::vector fill_colors_; - bool is_hwc_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUT_OUT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.cc b/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.cc deleted file mode 100644 index 6853f97e6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.cc +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright 2020-2025 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -constexpr size_t kInputColumnSize = 2; -constexpr size_t kMinLabelShapeSize = 2; -constexpr size_t kMaxLabelShapeSize = 3; -constexpr size_t kImageShapeSize = 4; -constexpr size_t kDimensionOne = 1; -constexpr size_t kDimensionTwo = 2; -constexpr size_t kDimensionThree = 3; - -CutMixBatchOp::CutMixBatchOp(ImageBatchFormat image_batch_format, float alpha, float prob) - : image_batch_format_(image_batch_format), alpha_(alpha), prob_(prob) {} - -void CutMixBatchOp::GetCropBox(int height, int width, float lam, int *x, int *y, int *crop_width, int *crop_height) { - const float cut_ratio = std::sqrt(1.F - lam); - auto cut_w = static_cast(static_cast(width) * cut_ratio); - auto cut_h = static_cast(static_cast(height) * cut_ratio); - std::uniform_int_distribution width_uniform_distribution(0, width); - std::uniform_int_distribution height_uniform_distribution(0, height); - int cx = width_uniform_distribution(random_generator_); - int x2, y2; - int cy = height_uniform_distribution(random_generator_); - constexpr int cut_half = 2; - *x = std::clamp(cx - cut_w / cut_half, 0, width - 1); // horizontal coordinate of left side of crop box - *y = std::clamp(cy - cut_h / cut_half, 0, height - 1); // vertical coordinate of the top side of crop box - x2 = std::clamp(cx + cut_w / cut_half, 0, width - 1); // horizontal coordinate of right side of crop box - y2 = std::clamp(cy + cut_h / cut_half, 0, height - 1); // vertical coordinate of the bottom side of crop box - *crop_width = std::clamp(x2 - *x, 1, std::max(width - 1, 1)); - *crop_height = std::clamp(y2 - *y, 1, std::max(height - 1, 1)); -} - -Status CutMixBatchOp::ValidateCutMixBatch(const TensorRow &input) { - // check column size - if (input.size() < kInputColumnSize) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: invalid input size, input should have 2 columns (image and label), but got: " + - std::to_string(input.size())); - } - - std::shared_ptr image = input.at(0); - std::shared_ptr label = input.at(1); - TensorShape image_shape = image->shape(); - TensorShape label_shape = label->shape(); - - // check image shape - if (image_shape.Size() != kImageShapeSize) { - std::string err_msg = "CutMixBatch: input image is not in shape of or , but got dimension: " + - std::to_string(image_shape.Size()); - if (image_shape.Size() == kDefaultImageRank) { - err_msg += ". You may need to perform Batch first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // check image channel - dsize_t channel_index; - if (image_batch_format_ == ImageBatchFormat::kNCHW) { - channel_index = kChannelIndexCHW + 1; - } else { // ImageBatchFormat::kNHWC - channel_index = kChannelIndexHWC + 1; - } - if (image_shape[channel_index] != kMinImageChannel && image_shape[channel_index] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("CutMixBatch: input image is not in channel of 1 or 3, but got channel: " + - std::to_string(image_shape[channel_index])); - } - - // check label dtype - CHECK_FAIL_RETURN_UNEXPECTED(label->type().IsNumeric(), - "CutMixBatch: invalid label type, label must be in a numeric type, but got: " + - label->type().ToString() + ". You may need to perform OneHot first."); - - // check label size - if (label_shape.Size() != kMinLabelShapeSize && label_shape.Size() != kMaxLabelShapeSize) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: input label is not in shape of or , but got dimension: " + - std::to_string(label_shape.Size()) + ". You may need to perform OneHot and Batch first."); - } - - // check batch size equal - if (image_shape[0] != label_shape[0]) { - RETURN_STATUS_UNEXPECTED("CutMixBatch: batch sizes of image and label must be the same, but got image size: " + - std::to_string(image_shape[0]) + " and label size: " + std::to_string(label_shape[0])); - } - - return Status::OK(); -} - -Status CutMixBatchOp::ComputeImage(const std::shared_ptr &image, int64_t rand_indx_i, float lam, - float *label_lam, std::shared_ptr *image_i) { - TensorShape image_shape = image->shape(); - int x, y, crop_width, crop_height; - // Get a random image - TensorShape remaining({-1}); - uchar *start_addr_of_index = nullptr; - std::shared_ptr rand_image; - - RETURN_IF_NOT_OK(image->StartAddrOfIndex({rand_indx_i, 0, 0, 0}, &start_addr_of_index, &remaining)); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory( - TensorShape({image_shape[kDimensionOne], image_shape[kDimensionTwo], image_shape[kDimensionThree]}), image->type(), - start_addr_of_index, &rand_image)); - - // Compute image - if (image_batch_format_ == ImageBatchFormat::kNHWC) { - // NHWC Format - GetCropBox(static_cast(image_shape[kDimensionOne]), static_cast(image_shape[kDimensionTwo]), lam, - &x, &y, &crop_width, &crop_height); - std::shared_ptr crop_from = CVTensor::AsCVTensor(rand_image); - std::shared_ptr mix_to = CVTensor::AsCVTensor(*image_i); - cv::Rect roi(x, y, crop_width, crop_height); - (crop_from->mat())(roi).copyTo((mix_to->mat())(roi)); - *image_i = std::static_pointer_cast(mix_to); - *label_lam = 1.F - (static_cast(crop_width * crop_height) / - static_cast(image_shape[kDimensionOne] * image_shape[kDimensionTwo])); - } else { - // NCHW Format - GetCropBox(static_cast(image_shape[kDimensionTwo]), static_cast(image_shape[kDimensionThree]), - lam, &x, &y, &crop_width, &crop_height); - // Divide a multi-channel array into several single-channel arrays - std::vector> rand_image_channels; - std::vector> image_i_channels; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(rand_image, &rand_image_channels)); - RETURN_IF_NOT_OK(BatchTensorToTensorVector(*image_i, &image_i_channels)); - std::vector> mix_channels; - for (auto i = 0; i < rand_image_channels.size() && i < image_i_channels.size(); ++i) { - std::shared_ptr crop_from = CVTensor::AsCVTensor(rand_image_channels[i]); - std::shared_ptr mix_to = CVTensor::AsCVTensor(image_i_channels[i]); - cv::Rect roi(x, y, crop_width, crop_height); - (crop_from->mat())(roi).copyTo((mix_to->mat())(roi)); - mix_channels.push_back(std::static_pointer_cast(mix_to)); - } - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(mix_channels, image_i)); - *label_lam = 1.F - (static_cast(crop_width * crop_height) / - static_cast(image_shape[kDimensionTwo] * image_shape[kDimensionThree])); - } - - return Status::OK(); -} - -Status CutMixBatchOp::ComputeLabel(const std::shared_ptr &label, int64_t rand_indx_i, int64_t index_i, - int64_t row_labels, int64_t num_classes, std::size_t label_shape_size, - float label_lam, std::shared_ptr *out_labels) { - // Compute labels - std::shared_ptr float_label; - RETURN_IF_NOT_OK(TypeCast(label, &float_label, DataType(DataType::DE_FLOAT32))); - for (int64_t j = 0; j < row_labels; j++) { - for (int64_t k = 0; k < num_classes; k++) { - std::vector first_index = - label_shape_size == kMaxLabelShapeSize ? std::vector{index_i, j, k} : std::vector{index_i, k}; - std::vector second_index = - label_shape_size == kMaxLabelShapeSize ? std::vector{rand_indx_i, j, k} : std::vector{rand_indx_i, k}; - float first_value; - float second_value; - RETURN_IF_NOT_OK(float_label->GetItemAt(&first_value, first_index)); - RETURN_IF_NOT_OK(float_label->GetItemAt(&second_value, second_index)); - RETURN_IF_NOT_OK((*out_labels)->SetItemAt(first_index, label_lam * first_value + (1 - label_lam) * second_value)); - } - } - - return Status::OK(); -} - -Status CutMixBatchOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(ValidateCutMixBatch(input)); - TensorShape image_shape = input.at(0)->shape(); - TensorShape label_shape = input.at(1)->shape(); - - // Move images into a vector of Tensors - std::vector> images; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input.at(0), &images)); - - // Calculate random labels - std::vector rand_indx; - CHECK_FAIL_RETURN_UNEXPECTED( - images.size() <= static_cast(std::numeric_limits::max()), - "The size of \"images\" must not be more than \"INT64_MAX\", but got: " + std::to_string(images.size())); - for (auto idx = 0; idx < images.size(); idx++) { - rand_indx.push_back(idx); - } - std::shuffle(rand_indx.begin(), rand_indx.end(), random_generator_); - std::gamma_distribution gamma_alpha(alpha_, 1.0); - std::gamma_distribution gamma_beta(alpha_, 1.0); - std::uniform_real_distribution uniform_distribution(0.0, 1.0); - - // Tensor holding the output labels - std::shared_ptr out_labels; - RETURN_IF_NOT_OK(TypeCast(input.at(1), &out_labels, DataType(DataType::DE_FLOAT32))); - int64_t row_labels = label_shape.Size() == kMaxLabelShapeSize ? label_shape[kDimensionOne] : 1; - int64_t num_classes = label_shape[-1]; - - // Compute labels and images - for (auto i = 0; i < image_shape[0]; i++) { - // Calculating lambda - // If x1 is a random variable from Gamma(a1, 1) and x2 is a random variable from Gamma(a2, 1) - // then x = x1 / (x1+x2) is a random variable from Beta(a1, a2) - float x1 = gamma_alpha(random_generator_); - float x2 = gamma_beta(random_generator_); - while (std::numeric_limits::max() - x1 < x2) { - x1 = gamma_alpha(random_generator_); - x2 = gamma_beta(random_generator_); - } - float lam = x1 / (x1 + x2); - double random_number = uniform_distribution(random_generator_); - if (random_number < prob_) { - float label_lam; // lambda used for labels - // Compute image - RETURN_IF_NOT_OK(ComputeImage(input.at(0), rand_indx[i], lam, &label_lam, &images[i])); - // Compute labels - RETURN_IF_NOT_OK(ComputeLabel(input.at(1), rand_indx[i], static_cast(i), row_labels, num_classes, - label_shape.Size(), label_lam, &out_labels)); - } - } - - std::shared_ptr out_images; - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(images, &out_images)); - - // Move the output into a TensorRow - output->push_back(out_images); - output->push_back(out_labels); - - return Status::OK(); -} - -void CutMixBatchOp::Print(std::ostream &out) const { - out << "CutMixBatchOp: " - << "\n"; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h b/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h deleted file mode 100644 index 94a8d1b83..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUTMIXBATCH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUTMIXBATCH_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class CutMixBatchOp : public RandomTensorOp { - public: - explicit CutMixBatchOp(ImageBatchFormat image_batch_format, float alpha, float prob); - - ~CutMixBatchOp() override = default; - - void Print(std::ostream &out) const override; - - void GetCropBox(int width, int height, float lam, int *x, int *y, int *crop_width, int *crop_height); - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kCutMixBatchOp; } - - private: - /// \brief Helper function used in Compute to validate the input TensorRow. - /// \param[in] input Input TensorRow of CutMixBatchOp - /// \returns Status - Status ValidateCutMixBatch(const TensorRow &input); - - /// \brief Helper function used in Compute to compute each image. - /// \param[in] input Input TensorRow of CutMixBatchOp. - /// \param[in] rand_indx_i The i-th generated random index as the start address of the input image. - /// \param[in] lam A random variable follow Beta distribution, used in GetCropBox. - /// \param[in] label_lam Lambda used for labels, will be updated after computing each image. - /// \param[in] image_i The result of the i-th computed image. - /// \returns Status - Status ComputeImage(const std::shared_ptr &image, int64_t rand_indx_i, float lam, float *label_lam, - std::shared_ptr *image_i); - - /// \brief Helper function used in Compute to compute each label corresponding to each image. - /// \param[in] input Input TensorRow of CutMixBatchOp. - /// \param[in] rand_indx_i The i-th generated random index as the start address of the input image. - /// \param[in] index_i The i-th label to be generated, corresponding to the i-th computed image. - /// \param[in] row_labels Number of rows of the label. - /// \param[in] num_classes Number of class of the label. - /// \param[in] label_shape_size The size of the label shape from input TensorRow. - /// \param[in] label_lam Lambda used for setting the location. - /// \param[in] out_labels The output of the i-th label, corresponding to the i-th computed image. - /// \returns Status - Status ComputeLabel(const std::shared_ptr &label, int64_t rand_indx_i, int64_t index_i, int64_t row_labels, - int64_t num_classes, std::size_t label_shape_size, float label_lam, - std::shared_ptr *out_labels); - - float alpha_; - float prob_; - ImageBatchFormat image_batch_format_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_CUTMIXBATCH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/decode_op.cc b/mindspore-lite/minddata/dataset/kernels/image/decode_op.cc deleted file mode 100644 index cecad712f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/decode_op.cc +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/decode_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const bool DecodeOp::kDefRgbFormat = true; - -DecodeOp::DecodeOp(bool rgb) : is_rgb_format_(rgb) { - if (is_rgb_format_) { // RGB color mode - MS_LOG(DEBUG) << "Decode color mode is RGB."; - } else { - MS_LOG(DEBUG) << "Decode color mode is BGR."; - } -} - -Status DecodeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // check the input tensor shape - if (input->Rank() != 1) { - RETURN_STATUS_UNEXPECTED("Decode: invalid input shape, only support 1D input, got rank: " + - std::to_string(input->Rank())); - } - if (is_rgb_format_) { // RGB color mode - return Decode(input, output); - } else { // BGR color mode - RETURN_STATUS_UNEXPECTED( - "Decode: only support Decoded into RGB image, check input parameter 'rgb' first, its value should be 'True'."); - } -} - -Status DecodeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, -1, 3}); // we don't know what is output image size, but we know it should be 3 channels - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED( - !outputs.empty(), - "Decode: invalid input shape, expected 1D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); - return Status::OK(); -} - -Status DecodeOp::OutputType(const std::vector &inputs, std::vector &outputs) { - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Decode: inputs cannot be empty."); - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - outputs[0] = DataType(DataType::DE_UINT8); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/decode_op.h b/mindspore-lite/minddata/dataset/kernels/image/decode_op.h deleted file mode 100644 index 3c9f85716..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/decode_op.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DecodeOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const bool kDefRgbFormat; - - explicit DecodeOp(bool rgb = true); - - ~DecodeOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDecodeOp; } - - private: - bool is_rgb_format_ = true; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.cc b/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.cc deleted file mode 100644 index 7181c1c2c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.cc +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/video_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -DecodeVideoOp::DecodeVideoOp() {} - -Status DecodeVideoOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - // check the input tensor shape - if (input[0]->Rank() != 1) { - RETURN_STATUS_UNEXPECTED("DecodeVideo: invalid input shape, only support 1D input, got rank: " + - std::to_string(input[0]->Rank())); - } - return DecodeVideo(input, output); -} - -Status DecodeVideoOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - // kDefaultImageRank is 3 - TensorShape visual_shape({-1, -1, -1, kDefaultImageRank}); - TensorShape audio_shape({-1, -1}); - if (inputs[0].Rank() == 1) { - outputs.emplace_back(visual_shape); - outputs.emplace_back(audio_shape); - } else { - RETURN_STATUS_UNEXPECTED("DecodeVideo: invalid input shape, expected 1D input, but got input dimension: " + - std::to_string(inputs[0].Rank())); - } - return Status::OK(); -} - -Status DecodeVideoOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - outputs[0] = DataType(DataType::DE_UINT8); - outputs[1] = DataType(DataType::DE_UNKNOWN); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h b/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h deleted file mode 100644 index d49944791..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_VIDEO_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_VIDEO_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DecodeVideoOp : public TensorOp { - public: - DecodeVideoOp(); - - ~DecodeVideoOp() = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDecodeVideoOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DECODE_VIDEO_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/CMakeLists.txt b/mindspore-lite/minddata/dataset/kernels/image/dvpp/CMakeLists.txt deleted file mode 100644 index edcac64f4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -file(GLOB_RECURSE _CURRENT_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") -set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_MD) -add_definitions(-DENABLE_DVPP_INTERFACE) -set(DVPP_IMAGE_SOURCE - # Ascend310 - ascend310/dvpp_crop_jpeg_op.cc - ascend310/dvpp_decode_resize_crop_jpeg_op.cc - ascend310/dvpp_decode_resize_jpeg_op.cc - ascend310/dvpp_decode_jpeg_op.cc - ascend310/dvpp_decode_png_op.cc - ascend310/dvpp_decode_video_op.cc - ascend310/dvpp_normalize_op.cc - ascend310/dvpp_resize_jpeg_op.cc - # adaptor - acl_adapter.cc - ) - - -add_library(kernels-dvpp-image OBJECT ${DVPP_IMAGE_SOURCE}) -if(ENABLE_ACL OR MSLITE_ENABLE_ACL) - add_subdirectory(utils) -endif() diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.cc deleted file mode 100644 index c9c3d5262..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.cc +++ /dev/null @@ -1,760 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#endif - -#include - -#include "utils/ms_context.h" - -namespace mindspore { -namespace dataset { -namespace { -#if defined(BUILD_LITE) -constexpr auto kAclPluginRelatedPath = "./libdvpp_utils.so"; -#else -constexpr auto kAclPluginRelatedPath = "./lib/plugin/ascend/libdvpp_utils.so"; -#endif -} // namespace -AclAdapter &AclAdapter::GetInstance() { - static AclAdapter instance{}; - static std::once_flag flag; - std::call_once(flag, []() { instance.InitPlugin(); }); - return instance; -} - -void AclAdapter::InitPlugin() { - if (plugin_handle_ != nullptr) { - return; - } -#if !defined(ENABLE_ACL) || defined(ENABLE_D) - // 310.tar.gz skip this check - if (MsContext::GetInstance() != nullptr && !MsContext::GetInstance()->IsAscendPluginLoaded()) { - return; - } -#endif -#if !defined(_WIN32) && !defined(_WIN64) - Dl_info dl_info; - if (dladdr(reinterpret_cast(AclAdapter::GetInstance), &dl_info) == 0) { - MS_LOG(INFO) << "Get dladdr error"; - return; - } - std::string cur_so_path = dl_info.dli_fname; - std::string acl_plugin_path = std::string(dirname(cur_so_path.data())) + "/" + kAclPluginRelatedPath; - - plugin_handle_ = dlopen(acl_plugin_path.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (plugin_handle_ == nullptr) { - MS_LOG(INFO) << "Cannot dlopen " << acl_plugin_path << ", result = " << GetDlErrorMsg() - << ", it can be ignored if not running on ascend."; - return; - } - - create_dvpp_video_fun_obj_ = DlsymFuncObj(CreateDvppVideo, plugin_handle_); - init_dvpp_video_fun_obj_ = DlsymFuncObj(InitDvppVideo, plugin_handle_); - close_dvpp_video_fun_obj_ = DlsymFuncObj(CloseDvppVideo, plugin_handle_); - dvpp_video_dump_frame_fun_obj_ = DlsymFuncObj(DvppVideoDumpFrame, plugin_handle_); - init_resource_fun_obj_ = DlsymFuncObj(InitResource, plugin_handle_); - get_context_fun_obj_ = DlsymFuncObj(GetContext, plugin_handle_); - release_fun_obj_ = DlsymFuncObj(Release, plugin_handle_); - create_acl_process_with_resize_fun_obj_ = DlsymFuncObj(CreateAclProcessWithResize, plugin_handle_); - create_acl_process_with_para_fun_obj_ = DlsymFuncObj(CreateAclProcessWithPara, plugin_handle_); - create_acl_process_fun_obj_ = DlsymFuncObj(CreateAclProcess, plugin_handle_); - destroy_acl_process_fun_obj_ = DlsymFuncObj(DestroyAclProcess, plugin_handle_); - release_acl_process_fun_obj_ = DlsymFuncObj(ReleaseAclProcess, plugin_handle_); - init_acl_process_fun_obj_ = DlsymFuncObj(InitAclProcess, plugin_handle_); - get_context_from_acl_process_fun_obj_ = DlsymFuncObj(GetContextFromAclProcess, plugin_handle_); - get_stream_from_acl_process_fun_obj_ = DlsymFuncObj(GetStreamFromAclProcess, plugin_handle_); - jpeg_drc_with_data_fun_obj_ = DlsymFuncObj(JPEG_DRC_WITH_DATA, plugin_handle_); - jpeg_dr_with_data_fun_obj_ = DlsymFuncObj(JPEG_DR_WITH_DATA, plugin_handle_); - jpeg_d_with_data_fun_obj_ = DlsymFuncObj(JPEG_D_WITH_DATA, plugin_handle_); - jpeg_r_with_data_fun_obj_ = DlsymFuncObj(JPEG_R_WITH_DATA, plugin_handle_); - jpeg_c_with_data_fun_obj_ = DlsymFuncObj(JPEG_C_WITH_DATA, plugin_handle_); - png_d_with_data_fun_obj_ = DlsymFuncObj(PNG_D_WITH_DATA, plugin_handle_); - jpeg_drc_fun_obj_ = DlsymFuncObj(JPEG_DRC, plugin_handle_); - jpeg_dr_fun_obj_ = DlsymFuncObj(JPEG_DR, plugin_handle_); - jpeg_d_fun_obj_ = DlsymFuncObj(JPEG_D, plugin_handle_); - jpeg_r_fun_obj_ = DlsymFuncObj(JPEG_R, plugin_handle_); - jpeg_c_fun_obj_ = DlsymFuncObj(JPEG_C, plugin_handle_); - png_d_fun_obj_ = DlsymFuncObj(PNG_D, plugin_handle_); - get_memory_data_fun_obj_ = DlsymFuncObj(GetMemoryData, plugin_handle_); - get_croped_device_data_fun_obj_ = DlsymFuncObj(GetCropedDeviceData, plugin_handle_); - get_resized_device_data_fun_obj_ = DlsymFuncObj(GetResizedDeviceData, plugin_handle_); - get_decode_device_data_fun_obj_ = DlsymFuncObj(GetDecodeDeviceData, plugin_handle_); - h_2_d_sink_fun_obj_ = DlsymFuncObj(H2D_Sink, plugin_handle_); - d_2_h_pop_fun_obj_ = DlsymFuncObj(D2H_Pop, plugin_handle_); - device_memory_release_fun_obj_ = DlsymFuncObj(DeviceMemoryRelease, plugin_handle_); - set_resize_paras_fun_obj_ = DlsymFuncObj(SetResizeParas, plugin_handle_); - set_crop_paras_fun_obj_ = DlsymFuncObj(SetCropParas, plugin_handle_); - aclrt_malloc_host_fun_obj_ = DlsymFuncObj(aclrtMallocHost, plugin_handle_); - aclrt_free_host_fun_obj_ = DlsymFuncObj(aclrtFreeHost, plugin_handle_); - aclrt_memcpy_fun_obj_ = DlsymFuncObj(aclrtMemcpy, plugin_handle_); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Ascend910B - dvpp_affine_fun_obj_ = DlsymFuncObj(DvppAffine, plugin_handle_); - dvpp_auto_contrast_fun_obj_ = DlsymFuncObj(DvppAutoContrast, plugin_handle_); - dvpp_brightness_fun_obj_ = DlsymFuncObj(DvppAdjustBrightness, plugin_handle_); - dvpp_contrast_fun_obj_ = DlsymFuncObj(DvppAdjustContrast, plugin_handle_); - dvpp_convert_color_fun_obj_ = DlsymFuncObj(DvppConvertColor, plugin_handle_); - dvpp_crop_fun_obj_ = DlsymFuncObj(DvppCrop, plugin_handle_); - dvpp_decode_fun_obj_ = DlsymFuncObj(DvppDecode, plugin_handle_); - dvpp_equalize_fun_obj_ = DlsymFuncObj(DvppEqualize, plugin_handle_); - dvpp_erase_fun_obj_ = DlsymFuncObj(DvppErase, plugin_handle_); - dvpp_gaussian_blur_fun_obj_ = DlsymFuncObj(DvppGaussianBlur, plugin_handle_); - dvpp_horizontal_flip_fun_obj_ = DlsymFuncObj(DvppHorizontalFlip, plugin_handle_); - dvpp_invert_fun_obj_ = DlsymFuncObj(DvppInvert, plugin_handle_); - dvpp_hue_fun_obj_ = DlsymFuncObj(DvppAdjustHue, plugin_handle_); - dvpp_normalize_fun_obj_ = DlsymFuncObj(DvppNormalize, plugin_handle_); - dvpp_pad_fun_obj_ = DlsymFuncObj(DvppPad, plugin_handle_); - dvpp_perspective_fun_obj_ = DlsymFuncObj(DvppPerspective, plugin_handle_); - dvpp_posterize_fun_obj_ = DlsymFuncObj(DvppPosterize, plugin_handle_); - dvpp_resize_fun_obj_ = DlsymFuncObj(DvppResize, plugin_handle_); - dvpp_resized_crop_fun_obj_ = DlsymFuncObj(DvppResizedCrop, plugin_handle_); - dvpp_rotate_fun_obj_ = DlsymFuncObj(DvppRotate, plugin_handle_); - dvpp_saturation_fun_obj_ = DlsymFuncObj(DvppAdjustSaturation, plugin_handle_); - dvpp_sharpness_fun_obj_ = DlsymFuncObj(DvppAdjustSharpness, plugin_handle_); - dvpp_solarize_fun_obj_ = DlsymFuncObj(DvppSolarize, plugin_handle_); - dvpp_vertical_flip_fun_obj_ = DlsymFuncObj(DvppVerticalFlip, plugin_handle_); - - // acl - get_soc_name_fun_obj_ = DlsymFuncObj(GetSocName, plugin_handle_); - create_acl_tensor_fun_obj_ = DlsymFuncObj(CreateAclTensor, plugin_handle_); - destroy_tensor_fun_obj_ = DlsymFuncObj(DestroyTensor, plugin_handle_); - destroy_float_array_fun_obj_ = DlsymFuncObj(DestroyFloatArray, plugin_handle_); - destroy_int_array_fun_obj_ = DlsymFuncObj(DestroyIntArray, plugin_handle_); -#endif -#endif -} - -void AclAdapter::FinalizePlugin() { - if (plugin_handle_ == nullptr) { - return; - } - - create_dvpp_video_fun_obj_ = nullptr; - init_dvpp_video_fun_obj_ = nullptr; - close_dvpp_video_fun_obj_ = nullptr; - dvpp_video_dump_frame_fun_obj_ = nullptr; - init_resource_fun_obj_ = nullptr; - get_context_fun_obj_ = nullptr; - release_fun_obj_ = nullptr; - create_acl_process_with_resize_fun_obj_ = nullptr; - create_acl_process_with_para_fun_obj_ = nullptr; - create_acl_process_fun_obj_ = nullptr; - destroy_acl_process_fun_obj_ = nullptr; - release_acl_process_fun_obj_ = nullptr; - init_acl_process_fun_obj_ = nullptr; - get_context_from_acl_process_fun_obj_ = nullptr; - get_stream_from_acl_process_fun_obj_ = nullptr; - jpeg_drc_with_data_fun_obj_ = nullptr; - jpeg_dr_with_data_fun_obj_ = nullptr; - jpeg_d_with_data_fun_obj_ = nullptr; - jpeg_r_with_data_fun_obj_ = nullptr; - jpeg_c_with_data_fun_obj_ = nullptr; - png_d_with_data_fun_obj_ = nullptr; - jpeg_drc_fun_obj_ = nullptr; - jpeg_dr_fun_obj_ = nullptr; - jpeg_d_fun_obj_ = nullptr; - jpeg_r_fun_obj_ = nullptr; - jpeg_c_fun_obj_ = nullptr; - png_d_fun_obj_ = nullptr; - get_memory_data_fun_obj_ = nullptr; - get_croped_device_data_fun_obj_ = nullptr; - get_resized_device_data_fun_obj_ = nullptr; - get_decode_device_data_fun_obj_ = nullptr; - h_2_d_sink_fun_obj_ = nullptr; - d_2_h_pop_fun_obj_ = nullptr; - device_memory_release_fun_obj_ = nullptr; - set_resize_paras_fun_obj_ = nullptr; - set_crop_paras_fun_obj_ = nullptr; - aclrt_malloc_host_fun_obj_ = nullptr; - aclrt_free_host_fun_obj_ = nullptr; - aclrt_memcpy_fun_obj_ = nullptr; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Ascend910B - dvpp_affine_fun_obj_ = nullptr; - dvpp_auto_contrast_fun_obj_ = nullptr; - dvpp_brightness_fun_obj_ = nullptr; - dvpp_contrast_fun_obj_ = nullptr; - dvpp_convert_color_fun_obj_ = nullptr; - dvpp_crop_fun_obj_ = nullptr; - dvpp_decode_fun_obj_ = nullptr; - dvpp_equalize_fun_obj_ = nullptr; - dvpp_erase_fun_obj_ = nullptr; - dvpp_gaussian_blur_fun_obj_ = nullptr; - dvpp_horizontal_flip_fun_obj_ = nullptr; - dvpp_invert_fun_obj_ = nullptr; - dvpp_hue_fun_obj_ = nullptr; - dvpp_normalize_fun_obj_ = nullptr; - dvpp_pad_fun_obj_ = nullptr; - dvpp_perspective_fun_obj_ = nullptr; - dvpp_posterize_fun_obj_ = nullptr; - dvpp_resize_fun_obj_ = nullptr; - dvpp_resized_crop_fun_obj_ = nullptr; - dvpp_rotate_fun_obj_ = nullptr; - dvpp_saturation_fun_obj_ = nullptr; - dvpp_sharpness_fun_obj_ = nullptr; - dvpp_solarize_fun_obj_ = nullptr; - dvpp_vertical_flip_fun_obj_ = nullptr; - - // acl - get_soc_name_fun_obj_ = nullptr; - create_acl_tensor_fun_obj_ = nullptr; - destroy_tensor_fun_obj_ = nullptr; - destroy_float_array_fun_obj_ = nullptr; - destroy_int_array_fun_obj_ = nullptr; -#endif -#if !defined(_WIN32) && !defined(_WIN64) - (void)dlclose(plugin_handle_); -#endif - plugin_handle_ = nullptr; -} - -void *AclAdapter::CreateDvppVideo(void *context, uint8_t *data, uint32_t size, uint32_t width, uint32_t height, - uint32_t type, uint32_t out_format, const std::string &output) const { - if (!HasAclPlugin() || create_dvpp_video_fun_obj_ == nullptr) { - return nullptr; - } - return create_dvpp_video_fun_obj_(context, data, size, width, height, type, out_format, output); -} - -AclLiteError AclAdapter::InitDvppVideo(void *dvpp_video) const { - if (!HasAclPlugin() || init_dvpp_video_fun_obj_ == nullptr) { - return ACLLITE_ERROR; - } - return init_dvpp_video_fun_obj_(dvpp_video); -} - -AclLiteError AclAdapter::CloseDvppVideo(void *dvpp_video) const { - if (!HasAclPlugin() || close_dvpp_video_fun_obj_ == nullptr) { - return ACLLITE_ERROR; - } - return close_dvpp_video_fun_obj_(dvpp_video); -} - -AclLiteError AclAdapter::DvppVideoDumpFrame(void *dvpp_video) const { - if (!HasAclPlugin() || dvpp_video_dump_frame_fun_obj_ == nullptr) { - return ACLLITE_ERROR; - } - return dvpp_video_dump_frame_fun_obj_(dvpp_video); -} - -APP_ERROR AclAdapter::InitResource(ResourceInfo *resource_info) const { - if (!HasAclPlugin() || init_resource_fun_obj_ == nullptr || resource_info == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return init_resource_fun_obj_(*resource_info); -} - -void *AclAdapter::GetContext(int device_id) const { - if (!HasAclPlugin() || get_context_fun_obj_ == nullptr) { - return nullptr; - } - return get_context_fun_obj_(device_id); -} - -void AclAdapter::Release() const { - if (!HasAclPlugin() || release_fun_obj_ == nullptr) { - return; - } - release_fun_obj_(); -} - -void *AclAdapter::CreateAclProcessWithResize(uint32_t resize_width, uint32_t resize_height, uint32_t crop_width, - uint32_t crop_height, void *context, bool is_crop, void *stream, - const std::shared_ptr &dvpp_common) const { - if (!HasAclPlugin() || create_acl_process_with_resize_fun_obj_ == nullptr) { - return nullptr; - } - return create_acl_process_with_resize_fun_obj_(resize_width, resize_height, crop_width, crop_height, context, is_crop, - stream, dvpp_common); -} - -void *AclAdapter::CreateAclProcessWithPara(uint32_t para_width, uint32_t para_height, void *context, bool is_crop, - void *stream, const std::shared_ptr &dvpp_common) const { - if (!HasAclPlugin() || create_acl_process_with_para_fun_obj_ == nullptr) { - return nullptr; - } - return create_acl_process_with_para_fun_obj_(para_width, para_height, context, is_crop, stream, dvpp_common); -} - -void *AclAdapter::CreateAclProcess(void *context, bool is_crop, void *stream, - const std::shared_ptr &dvpp_common) const { - if (!HasAclPlugin() || create_acl_process_fun_obj_ == nullptr) { - return nullptr; - } - return create_acl_process_fun_obj_(context, is_crop, stream, dvpp_common); -} - -void AclAdapter::DestroyAclProcess(void *acl_process) const { - if (!HasAclPlugin() || destroy_acl_process_fun_obj_ == nullptr) { - return; - } - destroy_acl_process_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::ReleaseAclProcess(void *acl_process) const { - if (!HasAclPlugin() || release_acl_process_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return release_acl_process_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::InitAclProcess(void *acl_process) const { - if (!HasAclPlugin() || init_acl_process_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return init_acl_process_fun_obj_(acl_process); -} - -void *AclAdapter::GetContextFromAclProcess(void *acl_process) const { - if (!HasAclPlugin() || get_context_from_acl_process_fun_obj_ == nullptr) { - return nullptr; - } - return get_context_from_acl_process_fun_obj_(acl_process); -} - -void *AclAdapter::GetStreamFromAclProcess(void *acl_process) const { - if (!HasAclPlugin() || get_stream_from_acl_process_fun_obj_ == nullptr) { - return nullptr; - } - return get_stream_from_acl_process_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::JPEG_DRC_WITH_DATA(void *acl_process, const RawData &data) const { - if (!HasAclPlugin() || jpeg_drc_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_drc_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::JPEG_DR_WITH_DATA(void *acl_process, const RawData &data) const { - if (!HasAclPlugin() || jpeg_dr_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_dr_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::JPEG_D_WITH_DATA(void *acl_process, const RawData &data) const { - if (!HasAclPlugin() || jpeg_d_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_d_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::JPEG_R_WITH_DATA(void *acl_process, const DvppDataInfo &data) const { - if (!HasAclPlugin() || jpeg_r_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_r_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::JPEG_C_WITH_DATA(void *acl_process, const DvppDataInfo &data) const { - if (!HasAclPlugin() || jpeg_c_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_c_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::PNG_D_WITH_DATA(void *acl_process, const RawData &data) const { - if (!HasAclPlugin() || png_d_with_data_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return png_d_with_data_fun_obj_(acl_process, data); -} - -APP_ERROR AclAdapter::JPEG_DRC(void *acl_process) const { - if (!HasAclPlugin() || jpeg_drc_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_drc_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::JPEG_DR(void *acl_process) const { - if (!HasAclPlugin() || jpeg_dr_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_dr_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::JPEG_D(void *acl_process) const { - if (!HasAclPlugin() || jpeg_d_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_d_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::JPEG_R(void *acl_process, const std::string &last_step) const { - if (!HasAclPlugin() || jpeg_r_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_r_fun_obj_(acl_process, last_step); -} - -APP_ERROR AclAdapter::JPEG_C(void *acl_process, const std::string &last_step) const { - if (!HasAclPlugin() || jpeg_c_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return jpeg_c_fun_obj_(acl_process, last_step); -} - -APP_ERROR AclAdapter::PNG_D(void *acl_process) const { - if (!HasAclPlugin() || png_d_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return png_d_fun_obj_(acl_process); -} - -void *AclAdapter::GetMemoryData(void *acl_process) const { - if (!HasAclPlugin() || get_memory_data_fun_obj_ == nullptr) { - return nullptr; - } - return get_memory_data_fun_obj_(acl_process); -} - -DvppDataInfo *AclAdapter::GetCropedDeviceData(void *acl_process) const { - if (!HasAclPlugin() || get_croped_device_data_fun_obj_ == nullptr) { - return nullptr; - } - return get_croped_device_data_fun_obj_(acl_process); -} - -DvppDataInfo *AclAdapter::GetResizedDeviceData(void *acl_process) const { - if (!HasAclPlugin() || get_resized_device_data_fun_obj_ == nullptr) { - return nullptr; - } - return get_resized_device_data_fun_obj_(acl_process); -} - -DvppDataInfo *AclAdapter::GetDecodeDeviceData(void *acl_process) const { - if (!HasAclPlugin() || get_decode_device_data_fun_obj_ == nullptr) { - return nullptr; - } - return get_decode_device_data_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::H2D_Sink(void *acl_process, const std::shared_ptr &input, - std::shared_ptr *device_input) const { - if (!HasAclPlugin() || h_2_d_sink_fun_obj_ == nullptr || device_input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return h_2_d_sink_fun_obj_(acl_process, input, *device_input); -} - -APP_ERROR AclAdapter::D2H_Pop(void *acl_process, const std::shared_ptr &device_output, - std::shared_ptr *output) const { - if (!HasAclPlugin() || d_2_h_pop_fun_obj_ == nullptr || output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return d_2_h_pop_fun_obj_(acl_process, device_output, *output); -} - -APP_ERROR AclAdapter::DeviceMemoryRelease(void *acl_process) const { - if (!HasAclPlugin() || device_memory_release_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return device_memory_release_fun_obj_(acl_process); -} - -APP_ERROR AclAdapter::SetResizeParas(void *acl_process, uint32_t width, uint32_t height) const { - if (!HasAclPlugin() || set_resize_paras_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return set_resize_paras_fun_obj_(acl_process, width, height); -} - -APP_ERROR AclAdapter::SetCropParas(void *acl_process, uint32_t width, uint32_t height) const { - if (!HasAclPlugin() || set_crop_paras_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return set_crop_paras_fun_obj_(acl_process, width, height); -} - -int AclAdapter::Memcpy(void *dst, size_t dest_max, const void *src, size_t count, int kind) const { - if (!HasAclPlugin() || aclrt_memcpy_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return aclrt_memcpy_fun_obj_(dst, dest_max, src, count, kind); -} - -int AclAdapter::MallocHost(void **host_ptr, size_t size) const { - if (!HasAclPlugin() || aclrt_malloc_host_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return aclrt_malloc_host_fun_obj_(host_ptr, size); -} - -int AclAdapter::FreeHost(void *host_ptr) const { - if (!HasAclPlugin() || aclrt_free_host_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return aclrt_free_host_fun_obj_(host_ptr); -} - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -// Ascend910B -APP_ERROR AclAdapter::DvppAdjustBrightness(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (!HasAclPlugin() || dvpp_brightness_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_brightness_fun_obj_(input, output, factor); -} - -APP_ERROR AclAdapter::DvppAdjustContrast(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (!HasAclPlugin() || dvpp_contrast_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_contrast_fun_obj_(input, output, factor); -} - -APP_ERROR AclAdapter::DvppAdjustHue(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (!HasAclPlugin() || dvpp_hue_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_hue_fun_obj_(input, output, factor); -} - -APP_ERROR AclAdapter::DvppAdjustSaturation(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (!HasAclPlugin() || dvpp_saturation_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_saturation_fun_obj_(input, output, factor); -} - -APP_ERROR AclAdapter::DvppAdjustSharpness(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (!HasAclPlugin() || dvpp_sharpness_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_sharpness_fun_obj_(input, output, factor); -} - -APP_ERROR AclAdapter::DvppAffine(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &matrix, - uint32_t interpolation_mode, uint32_t padding_mode, const std::vector &fill) { - if (!HasAclPlugin() || dvpp_affine_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_affine_fun_obj_(input, output, matrix, interpolation_mode, padding_mode, fill); -} - -APP_ERROR AclAdapter::DvppAutoContrast(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &cutoff, const std::vector &ignore) { - if (!HasAclPlugin() || dvpp_auto_contrast_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_auto_contrast_fun_obj_(input, output, cutoff, ignore); -} - -APP_ERROR AclAdapter::DvppConvertColor(const std::shared_ptr &input, - std::shared_ptr *output, ConvertMode convertMode) { - if (!HasAclPlugin() || dvpp_convert_color_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_convert_color_fun_obj_(input, output, convertMode); -} - -APP_ERROR AclAdapter::DvppCrop(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, - uint32_t height, uint32_t width) { - if (!HasAclPlugin() || dvpp_crop_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_crop_fun_obj_(input, output, top, left, height, width); -} - -APP_ERROR AclAdapter::DvppDecode(const std::shared_ptr &input, - std::shared_ptr *output) { - if (!HasAclPlugin() || dvpp_decode_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_decode_fun_obj_(input, output); -} - -APP_ERROR AclAdapter::DvppEqualize(const std::shared_ptr &input, - std::shared_ptr *output) { - if (!HasAclPlugin() || dvpp_equalize_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_equalize_fun_obj_(input, output); -} - -APP_ERROR AclAdapter::DvppErase(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, - uint32_t height, uint32_t width, const std::vector &value) { - if (!HasAclPlugin() || dvpp_erase_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_erase_fun_obj_(input, output, top, left, height, width, value); -} - -APP_ERROR AclAdapter::DvppGaussianBlur(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &kernel_size, const std::vector &sigma, - uint32_t padding_mode) { - if (!HasAclPlugin() || dvpp_gaussian_blur_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_gaussian_blur_fun_obj_(input, output, kernel_size, sigma, padding_mode); -} - -APP_ERROR AclAdapter::DvppHorizontalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - if (!HasAclPlugin() || dvpp_horizontal_flip_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_horizontal_flip_fun_obj_(input, output); -} - -APP_ERROR AclAdapter::DvppInvert(const std::shared_ptr &input, - std::shared_ptr *output) { - if (!HasAclPlugin() || dvpp_invert_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_invert_fun_obj_(input, output); -} - -APP_ERROR AclAdapter::DvppNormalize(const std::shared_ptr &input, - std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc) { - if (!HasAclPlugin() || dvpp_normalize_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_normalize_fun_obj_(input, output, mean, std, is_hwc); -} - -APP_ERROR AclAdapter::DvppPad(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &padding, - uint32_t padding_mode, const std::vector &fill) { - if (!HasAclPlugin() || dvpp_pad_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_pad_fun_obj_(input, output, padding, padding_mode, fill); -} - -APP_ERROR AclAdapter::DvppPerspective(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, - InterpolationMode interpolation) { - if (!HasAclPlugin() || dvpp_perspective_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_perspective_fun_obj_(input, output, start_points, end_points, interpolation); -} - -APP_ERROR AclAdapter::DvppPosterize(const std::shared_ptr &input, - std::shared_ptr *output, uint8_t bits) { - if (!HasAclPlugin() || dvpp_posterize_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_posterize_fun_obj_(input, output, bits); -} - -APP_ERROR AclAdapter::DvppResize(const std::shared_ptr &input, - std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { - if (!HasAclPlugin() || dvpp_resize_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_resize_fun_obj_(input, output, output_height, output_width, fx, fy, mode); -} - -APP_ERROR AclAdapter::DvppResizedCrop(const std::shared_ptr &input, - std::shared_ptr *output, int32_t top, int32_t left, - int32_t height, int32_t width, int32_t output_height, int32_t output_width, - InterpolationMode interpolation) { - if (!HasAclPlugin() || dvpp_resized_crop_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_resized_crop_fun_obj_(input, output, top, left, height, width, output_height, output_width, - interpolation); -} - -APP_ERROR AclAdapter::DvppRotate(const std::shared_ptr &input, - std::shared_ptr *output, float degrees, InterpolationMode mode, - bool expand, const std::vector ¢er, const std::vector &fill) { - if (!HasAclPlugin() || dvpp_rotate_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_rotate_fun_obj_(input, output, degrees, mode, expand, center, fill); -} - -APP_ERROR AclAdapter::DvppVerticalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - if (!HasAclPlugin() || dvpp_vertical_flip_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_vertical_flip_fun_obj_(input, output); -} - -APP_ERROR AclAdapter::DvppSolarize(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &threshold) { - if (!HasAclPlugin() || dvpp_solarize_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return dvpp_solarize_fun_obj_(input, output, threshold); -} - -// acl -APP_ERROR AclAdapter::GetSocName(std::string *soc_name) { - if (!HasAclPlugin() || get_soc_name_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return get_soc_name_fun_obj_(soc_name); -} - -APP_ERROR AclAdapter::CreateAclTensor(const int64_t *view_dims, uint64_t view_dims_num, mindspore::TypeId data_type, - const int64_t *stride, int64_t offset, const int64_t *storage_dims, - uint64_t storage_dims_num, void *tensor_data, bool is_hwc, void **acl_tensor) { - if (!HasAclPlugin() || create_acl_tensor_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return create_acl_tensor_fun_obj_(view_dims, view_dims_num, data_type, stride, offset, storage_dims, storage_dims_num, - tensor_data, is_hwc, acl_tensor); -} - -APP_ERROR AclAdapter::DestroyTensor(void *tensor) { - if (!HasAclPlugin() || destroy_tensor_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return destroy_tensor_fun_obj_(tensor); -} - -APP_ERROR AclAdapter::DestroyFloatArray(void *float_array) { - if (!HasAclPlugin() || destroy_float_array_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return destroy_float_array_fun_obj_(float_array); -} - -APP_ERROR AclAdapter::DestroyIntArray(void *int_array) { - if (!HasAclPlugin() || destroy_int_array_fun_obj_ == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return destroy_int_array_fun_obj_(int_array); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h deleted file mode 100644 index f8a51dfec..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h +++ /dev/null @@ -1,267 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_ACL_ADAPTER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_ACL_ADAPTER_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" - -namespace mindspore::dataset { -class AclAdapter { - public: - static AclAdapter &GetInstance(); - - bool HasAclPlugin() const { return plugin_handle_ != nullptr; } - - void *CreateDvppVideo(void *context, uint8_t *data, uint32_t size, uint32_t width, uint32_t height, uint32_t type, - uint32_t out_format, const std::string &output) const; - AclLiteError InitDvppVideo(void *dvpp_video) const; - AclLiteError CloseDvppVideo(void *dvpp_video) const; - AclLiteError DvppVideoDumpFrame(void *dvpp_video) const; - APP_ERROR InitResource(ResourceInfo *resource_info) const; - void *GetContext(int device_id) const; - void Release() const; - void *CreateAclProcessWithResize(uint32_t resize_width, uint32_t resize_height, uint32_t crop_width, - uint32_t crop_height, void *context, bool is_crop, void *stream, - const std::shared_ptr &dvpp_common) const; - void *CreateAclProcessWithPara(uint32_t para_width, uint32_t para_height, void *context, bool is_crop, void *stream, - const std::shared_ptr &dvpp_common) const; - void *CreateAclProcess(void *context, bool is_crop, void *stream, - const std::shared_ptr &dvpp_common) const; - void DestroyAclProcess(void *acl_process) const; - APP_ERROR ReleaseAclProcess(void *acl_process) const; - APP_ERROR InitAclProcess(void *acl_process) const; - void *GetContextFromAclProcess(void *acl_process) const; - void *GetStreamFromAclProcess(void *acl_process) const; - APP_ERROR JPEG_DRC_WITH_DATA(void *acl_process, const RawData &data) const; - APP_ERROR JPEG_DR_WITH_DATA(void *acl_process, const RawData &data) const; - APP_ERROR JPEG_D_WITH_DATA(void *acl_process, const RawData &data) const; - APP_ERROR JPEG_R_WITH_DATA(void *acl_process, const DvppDataInfo &data) const; - APP_ERROR JPEG_C_WITH_DATA(void *acl_process, const DvppDataInfo &data) const; - APP_ERROR PNG_D_WITH_DATA(void *acl_process, const RawData &data) const; - APP_ERROR JPEG_DRC(void *acl_process) const; - APP_ERROR JPEG_DR(void *acl_process) const; - APP_ERROR JPEG_D(void *acl_process) const; - APP_ERROR JPEG_R(void *acl_process, const std::string &last_step) const; - APP_ERROR JPEG_C(void *acl_process, const std::string &last_step) const; - APP_ERROR PNG_D(void *acl_process) const; - void *GetMemoryData(void *acl_process) const; - DvppDataInfo *GetCropedDeviceData(void *acl_process) const; - DvppDataInfo *GetResizedDeviceData(void *acl_process) const; - DvppDataInfo *GetDecodeDeviceData(void *acl_process) const; - APP_ERROR H2D_Sink(void *acl_process, const std::shared_ptr &input, - std::shared_ptr *device_input) const; - APP_ERROR D2H_Pop(void *acl_process, const std::shared_ptr &device_output, - std::shared_ptr *output) const; - APP_ERROR DeviceMemoryRelease(void *acl_process) const; - APP_ERROR SetResizeParas(void *acl_process, uint32_t width, uint32_t height) const; - APP_ERROR SetCropParas(void *acl_process, uint32_t width, uint32_t height) const; - int Memcpy(void *dst, size_t dest_max, const void *src, size_t count, int kind) const; - int MallocHost(void **host_ptr, size_t size) const; - int FreeHost(void *host_ptr) const; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Ascend910B - APP_ERROR DvppAdjustBrightness(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - - APP_ERROR DvppAdjustContrast(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - - APP_ERROR DvppAdjustHue(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - - APP_ERROR DvppAdjustSaturation(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - - APP_ERROR DvppAdjustSharpness(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - - APP_ERROR DvppAffine(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &matrix, - uint32_t interpolation_mode, uint32_t padding_mode, const std::vector &fill); - - APP_ERROR DvppAutoContrast(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &cutoff, - const std::vector &ignore); - APP_ERROR DvppConvertColor(const std::shared_ptr &input, - std::shared_ptr *output, ConvertMode convertMode); - - APP_ERROR DvppCrop(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width); - - APP_ERROR DvppDecode(const std::shared_ptr &input, - std::shared_ptr *output); - - APP_ERROR DvppEqualize(const std::shared_ptr &input, - std::shared_ptr *output); - - APP_ERROR DvppErase(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width, const std::vector &value); - - APP_ERROR DvppGaussianBlur(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &kernel_size, - const std::vector &sigma, uint32_t padding_mode); - - APP_ERROR DvppHorizontalFlip(const std::shared_ptr &input, - std::shared_ptr *output); - - APP_ERROR DvppInvert(const std::shared_ptr &input, - std::shared_ptr *output); - - APP_ERROR DvppNormalize(const std::shared_ptr &input, - std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc); - - APP_ERROR DvppPad(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &padding, - uint32_t padding_mode, const std::vector &fill); - - APP_ERROR DvppPerspective(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation); - - APP_ERROR DvppPosterize(const std::shared_ptr &input, - std::shared_ptr *output, uint8_t bits); - - APP_ERROR DvppResize(const std::shared_ptr &input, - std::shared_ptr *output, int32_t output_height, int32_t output_width, - double fx, double fy, InterpolationMode mode); - - APP_ERROR DvppResizedCrop(const std::shared_ptr &input, - std::shared_ptr *output, int32_t top, int32_t left, int32_t height, - int32_t width, int32_t output_height, int32_t output_width, - InterpolationMode interpolation); - - APP_ERROR DvppRotate(const std::shared_ptr &input, - std::shared_ptr *output, float degrees, InterpolationMode mode, - bool expand, const std::vector ¢er, const std::vector &fill); - APP_ERROR DvppSolarize(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &threshold); - - APP_ERROR DvppVerticalFlip(const std::shared_ptr &input, - std::shared_ptr *output); - - // acl - APP_ERROR GetSocName(std::string *soc_name); - - APP_ERROR CreateAclTensor(const int64_t *view_dims, uint64_t view_dims_num, mindspore::TypeId data_type, - const int64_t *stride, int64_t offset, const int64_t *storage_dims, - uint64_t storage_dims_num, void *tensor_data, bool is_hwc, void **acl_tensor); - - APP_ERROR DestroyTensor(void *tensor); - - APP_ERROR DestroyFloatArray(void *float_array); - - APP_ERROR DestroyIntArray(void *int_array); -#endif - - private: - AclAdapter() = default; - ~AclAdapter() { FinalizePlugin(); } - void InitPlugin(); - void FinalizePlugin(); - - void *plugin_handle_ = nullptr; - - CreateDvppVideoFunObj create_dvpp_video_fun_obj_; - InitDvppVideoFunObj init_dvpp_video_fun_obj_; - CloseDvppVideoFunObj close_dvpp_video_fun_obj_; - DvppVideoDumpFrameFunObj dvpp_video_dump_frame_fun_obj_; - InitResourceFunObj init_resource_fun_obj_; - GetContextFunObj get_context_fun_obj_; - ReleaseFunObj release_fun_obj_; - CreateAclProcessWithResizeFunObj create_acl_process_with_resize_fun_obj_; - CreateAclProcessWithParaFunObj create_acl_process_with_para_fun_obj_; - CreateAclProcessFunObj create_acl_process_fun_obj_; - DestroyAclProcessFunObj destroy_acl_process_fun_obj_; - ReleaseAclProcessFunObj release_acl_process_fun_obj_; - InitAclProcessFunObj init_acl_process_fun_obj_; - GetContextFromAclProcessFunObj get_context_from_acl_process_fun_obj_; - GetStreamFromAclProcessFunObj get_stream_from_acl_process_fun_obj_; - JPEG_DRC_WITH_DATAFunObj jpeg_drc_with_data_fun_obj_; - JPEG_DR_WITH_DATAFunObj jpeg_dr_with_data_fun_obj_; - JPEG_D_WITH_DATAFunObj jpeg_d_with_data_fun_obj_; - JPEG_R_WITH_DATAFunObj jpeg_r_with_data_fun_obj_; - JPEG_C_WITH_DATAFunObj jpeg_c_with_data_fun_obj_; - PNG_D_WITH_DATAFunObj png_d_with_data_fun_obj_; - JPEG_DRCFunObj jpeg_drc_fun_obj_; - JPEG_DRFunObj jpeg_dr_fun_obj_; - JPEG_DFunObj jpeg_d_fun_obj_; - JPEG_RFunObj jpeg_r_fun_obj_; - JPEG_CFunObj jpeg_c_fun_obj_; - PNG_DFunObj png_d_fun_obj_; - GetMemoryDataFunObj get_memory_data_fun_obj_; - GetCropedDeviceDataFunObj get_croped_device_data_fun_obj_; - GetResizedDeviceDataFunObj get_resized_device_data_fun_obj_; - GetDecodeDeviceDataFunObj get_decode_device_data_fun_obj_; - H2D_SinkFunObj h_2_d_sink_fun_obj_; - D2H_PopFunObj d_2_h_pop_fun_obj_; - DeviceMemoryReleaseFunObj device_memory_release_fun_obj_; - SetResizeParasFunObj set_resize_paras_fun_obj_; - SetCropParasFunObj set_crop_paras_fun_obj_; - aclrtMallocHostFunObj aclrt_malloc_host_fun_obj_; - aclrtFreeHostFunObj aclrt_free_host_fun_obj_; - aclrtMemcpyFunObj aclrt_memcpy_fun_obj_; - -#if !defined(BUILD_LITE) && defined(ENABLE_D) - // Ascend910B - DvppAdjustBrightnessFunObj dvpp_brightness_fun_obj_; - DvppAdjustContrastFunObj dvpp_contrast_fun_obj_; - DvppAdjustHueFunObj dvpp_hue_fun_obj_; - DvppAdjustSaturationFunObj dvpp_saturation_fun_obj_; - DvppAdjustSharpnessFunObj dvpp_sharpness_fun_obj_; - DvppAffineFunObj dvpp_affine_fun_obj_; - DvppAutoContrastFunObj dvpp_auto_contrast_fun_obj_; - DvppConvertColorFunObj dvpp_convert_color_fun_obj_; - DvppCropFunObj dvpp_crop_fun_obj_; - DvppDecodeFunObj dvpp_decode_fun_obj_; - DvppEqualizeFunObj dvpp_equalize_fun_obj_; - DvppEraseFunObj dvpp_erase_fun_obj_; - DvppGaussianBlurFunObj dvpp_gaussian_blur_fun_obj_; - DvppHorizontalFlipFunObj dvpp_horizontal_flip_fun_obj_; - DvppInvertFunObj dvpp_invert_fun_obj_; - DvppNormalizeFunObj dvpp_normalize_fun_obj_; - DvppPadFunObj dvpp_pad_fun_obj_; - DvppPerspectiveFunObj dvpp_perspective_fun_obj_; - DvppPosterizeFunObj dvpp_posterize_fun_obj_; - DvppResizeFunObj dvpp_resize_fun_obj_; - DvppResizedCropFunObj dvpp_resized_crop_fun_obj_; - DvppRotateFunObj dvpp_rotate_fun_obj_; - DvppSolarizeFunObj dvpp_solarize_fun_obj_; - DvppVerticalFlipFunObj dvpp_vertical_flip_fun_obj_; - - // acl interface - GetSocNameFunObj get_soc_name_fun_obj_; - CreateAclTensorFunObj create_acl_tensor_fun_obj_; - DestroyTensorFunObj destroy_tensor_fun_obj_; - DestroyFloatArrayFunObj destroy_float_array_fun_obj_; - DestroyIntArrayFunObj destroy_int_array_fun_obj_; -#endif -}; -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_ACL_ADAPTER_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.cc deleted file mode 100644 index ffef87fdd..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.cc +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status DvppCropJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer is empty."); - std::string last_step = "Resize"; - DvppDataInfo *imageinfo = AclAdapter::GetInstance().GetResizedDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(imageinfo); - if (!imageinfo->data) { - last_step = "Decode"; - } - APP_ERROR ret = AclAdapter::GetInstance().JPEG_C(processor_.get(), last_step); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp crop processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *CropOut = AclAdapter::GetInstance().GetCropedDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(CropOut); - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(CropOut->data, CropOut->dataSize, CropOut->width, CropOut->widthStride, - CropOut->height, CropOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from device memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppCropJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppCropJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyJPEG(input)) { - RETURN_STATUS_UNEXPECTED("DvppCropJpegOp only support process jpeg image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - DvppDataInfo imageinfo; - imageinfo.dataSize = input->SizeInBytes(); - imageinfo.data = static_cast(buffer); - std::vector yuv_shape_ = input->GetYuvShape(); - const size_t yuv_shape_size = 4; - CHECK_FAIL_RETURN_UNEXPECTED(yuv_shape_.size() == yuv_shape_size, "yuv_shape requires 4 elements."); - imageinfo.width = yuv_shape_[0]; - imageinfo.widthStride = yuv_shape_[1]; - imageinfo.height = yuv_shape_[2]; - imageinfo.heightStride = yuv_shape_[3]; - imageinfo.format = 1; // 1 means PIXEL_FORMAT_YUV_SEMIPLANAR_420 - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D-chip and set up all configures - std::shared_ptr process( - AclAdapter::GetInstance().CreateAclProcessWithPara(crop_width_, crop_height_, context, true, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(process.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - ret = AclAdapter::GetInstance().JPEG_C_WITH_DATA(process.get(), imageinfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp crop processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(process.get())); - DvppDataInfo *CropOut(AclAdapter::GetInstance().GetCropedDeviceData(process.get())); - RETURN_UNEXPECTED_IF_NULL(CropOut); - dsize_t dvpp_length = CropOut->dataSize; - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - uint32_t crop_height = CropOut->height; - uint32_t crop_heightStride = CropOut->heightStride; - uint32_t crop_width = CropOut->width; - uint32_t crop_widthStride = CropOut->widthStride; - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output)); - RETURN_IF_NOT_OK((*output)->SetYuvShape(crop_width, crop_widthStride, crop_height, crop_heightStride)); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppCropJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppCropJpegOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 1 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppCropJpeg: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppCropJpeg: Invalid input shape."); - return Status::OK(); -} - -Status DvppCropJpegOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env."); - } - APP_ERROR ret = AclAdapter::GetInstance().SetCropParas(processor_.get(), crop_width_, crop_height_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "SetCropParas failed."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h deleted file mode 100644 index d976b24a9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_CROP_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_CROP_JPEG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppCropJpegOp : public TensorOp { - public: - DvppCropJpegOp(int32_t crop_height, int32_t crop_width) : crop_height_(crop_height), crop_width_(crop_width) {} - - /// \brief Destructor - ~DvppCropJpegOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppCropJpegOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - uint32_t crop_height_; - uint32_t crop_width_; - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_CROP_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.cc deleted file mode 100644 index 0208ae2be..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.cc +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -// Compute() will be called when context=="Ascend310" -Status DvppDecodeJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer on device is empty."); - APP_ERROR ret = AclAdapter::GetInstance().JPEG_D(processor_.get()); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *DecodeOut = AclAdapter::GetInstance().GetDecodeDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(DecodeOut); - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(DecodeOut->data, DecodeOut->dataSize, DecodeOut->width, - DecodeOut->widthStride, DecodeOut->height, DecodeOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -// Compute() will be called when context=="CPU" -Status DvppDecodeJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyJPEG(input)) { - RETURN_STATUS_UNEXPECTED("DvppDecodeJpegOp only support process JPEG image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - RawData imageInfo{}; - uint32_t filesize = input->SizeInBytes(); - imageInfo.lenOfByte = filesize; - imageInfo.data = static_cast(buffer); - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D-chip and set up all configures - std::shared_ptr process(AclAdapter::GetInstance().CreateAclProcess(context, false, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(process.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().JPEG_D_WITH_DATA(process.get(), imageInfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(process.get())); - DvppDataInfo *DecodeOut = AclAdapter::GetInstance().GetDecodeDeviceData(process.get()); - RETURN_UNEXPECTED_IF_NULL(DecodeOut); - dsize_t dvpp_length = DecodeOut->dataSize; - uint32_t decoded_height = DecodeOut->height; - uint32_t decoded_heightStride = DecodeOut->heightStride; - uint32_t decoded_width = DecodeOut->width; - uint32_t decoded_widthStride = DecodeOut->widthStride; - - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output)); - RETURN_IF_NOT_OK((*output)->SetYuvShape(decoded_width, decoded_widthStride, decoded_height, decoded_heightStride)); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from device memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeJpegOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env."); - } - return Status::OK(); -} - -Status DvppDecodeJpegOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 3 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppDecodeJpeg: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppDecodeJpeg: Invalid input shape."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h deleted file mode 100644 index 35135eca5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_JPEG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppDecodeJpegOp : public TensorOp { - public: - DvppDecodeJpegOp() : processor_(nullptr) {} - - /// \brief Destructor - ~DvppDecodeJpegOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodeJpegOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.cc deleted file mode 100644 index b60d982b7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.cc +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status DvppDecodePngOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer on device is empty."); - APP_ERROR ret = AclAdapter::GetInstance().PNG_D(processor_.get()); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *DecodeOut = AclAdapter::GetInstance().GetDecodeDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(DecodeOut); - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(DecodeOut->data, DecodeOut->dataSize, DecodeOut->width, - DecodeOut->widthStride, DecodeOut->height, DecodeOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodePngOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodePngOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyPNG(input)) { - RETURN_STATUS_UNEXPECTED("DvppDecodePngOp only support process PNG image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - RawData imageInfo{}; - uint32_t filesize = input->SizeInBytes(); - imageInfo.lenOfByte = filesize; - imageInfo.data = static_cast(buffer); - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D-chip and set up all configures - std::shared_ptr process(AclAdapter::GetInstance().CreateAclProcess(context, false, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(process.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - ret = AclAdapter::GetInstance().PNG_D_WITH_DATA(process.get(), imageInfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(process.get())); - DvppDataInfo *DecodeOut = AclAdapter::GetInstance().GetDecodeDeviceData(process.get()); - RETURN_UNEXPECTED_IF_NULL(DecodeOut); - dsize_t dvpp_length = DecodeOut->dataSize; - - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodePngOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodePngOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 3 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppDecodePng: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppDecodePng: Invalid input shape."); - return Status::OK(); -} - -Status DvppDecodePngOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env."); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h deleted file mode 100644 index f329f87d4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_PNG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_PNG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppDecodePngOp : public TensorOp { - public: - DvppDecodePngOp() {} - - /// \brief Destructor - ~DvppDecodePngOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodePngOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_PNG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.cc deleted file mode 100644 index 0ace3a58e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.cc +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status DvppDecodeResizeCropJpegOp::Compute(const std::shared_ptr &input, - std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer on device is empty."); - APP_ERROR ret = AclAdapter::GetInstance().JPEG_DRC(processor_.get()); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *CropOut = AclAdapter::GetInstance().GetCropedDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(CropOut); - - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(CropOut->data, CropOut->dataSize, CropOut->width, CropOut->widthStride, - CropOut->height, CropOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeResizeCropJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeResizeCropJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyJPEG(input)) { - RETURN_STATUS_UNEXPECTED("DvppDecodeReiszeCropJpegOp only support process jpeg image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - RawData imageInfo{}; - uint32_t filesize = input->SizeInBytes(); - imageInfo.lenOfByte = filesize; - imageInfo.data = static_cast(buffer); - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D chip and set up all configures - std::shared_ptr processor( - AclAdapter::GetInstance().CreateAclProcessWithResize(resized_width_, resized_height_, crop_width_, crop_height_, - context, true, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(processor.get()); - - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - ret = AclAdapter::GetInstance().JPEG_DRC_WITH_DATA(processor.get(), imageInfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(processor.get())); - DvppDataInfo *CropOut = AclAdapter::GetInstance().GetCropedDeviceData(processor.get()); - RETURN_UNEXPECTED_IF_NULL(CropOut); - uint32_t dvpp_length = CropOut->dataSize; - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output)); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(processor.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeResizeCropJpegOp:" + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeResizeCropJpegOp::OutputShape(const std::vector &inputs, - std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 3 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppDecodeResizeCropJpeg: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppDecodeResizeCropJpeg: Invalid input shape."); - return Status::OK(); -} - -Status DvppDecodeResizeCropJpegOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env"); - } - APP_ERROR ret = AclAdapter::GetInstance().SetResizeParas(processor_.get(), resized_width_, resized_height_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "SetResizeParas failed."); - ret = AclAdapter::GetInstance().SetCropParas(processor_.get(), crop_width_, crop_height_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "SetCropParas failed."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h deleted file mode 100644 index f5107cceb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_CROP_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_CROP_JPEG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppDecodeResizeCropJpegOp : public TensorOp { - public: - DvppDecodeResizeCropJpegOp(int32_t crop_height, int32_t crop_width, int32_t resized_height, int32_t resized_width) - : crop_height_(crop_height), - crop_width_(crop_width), - resized_height_(resized_height), - resized_width_(resized_width) {} - - /// \brief Destructor - ~DvppDecodeResizeCropJpegOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodeResizeCropJpegOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - int32_t crop_height_; - int32_t crop_width_; - int32_t resized_height_; - int32_t resized_width_; - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_CROP_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.cc deleted file mode 100644 index a85eb8d65..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.cc +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status DvppDecodeResizeJpegOp::Compute(const std::shared_ptr &input, - std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer on device is empty."); - APP_ERROR ret = AclAdapter::GetInstance().JPEG_DR(processor_.get()); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *ResizeOut = AclAdapter::GetInstance().GetResizedDeviceData(processor_.get()); - RETURN_UNEXPECTED_IF_NULL(ResizeOut); - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(ResizeOut->data, ResizeOut->dataSize, ResizeOut->width, - ResizeOut->widthStride, ResizeOut->height, ResizeOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeResizeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeResizeJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyJPEG(input)) { - RETURN_STATUS_UNEXPECTED("DvppDecodeReiszeJpegOp only support process jpeg image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - RawData imageInfo{}; - uint32_t filesize = input->SizeInBytes(); - imageInfo.lenOfByte = filesize; - imageInfo.data = static_cast(buffer); - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D-chip and set up all configures - std::shared_ptr process(AclAdapter::GetInstance().CreateAclProcessWithPara(resized_width_, resized_height_, - context, false, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - ret = AclAdapter::GetInstance().InitAclProcess(process.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - ret = AclAdapter::GetInstance().JPEG_DR_WITH_DATA(process.get(), imageInfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(process.get())); - DvppDataInfo *ResizeOut = AclAdapter::GetInstance().GetResizedDeviceData(process.get()); - RETURN_UNEXPECTED_IF_NULL(ResizeOut); - dsize_t dvpp_length = ResizeOut->dataSize; - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output)); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppDecodeResizeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeResizeJpegOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 1 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppDecodeResizeJpeg: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppDecodeResizeJpeg: Invalid input shape."); - return Status::OK(); -} - -Status DvppDecodeResizeJpegOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env"); - } - APP_ERROR ret = AclAdapter::GetInstance().SetResizeParas(processor_.get(), resized_width_, resized_height_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "SetResizeParas failed."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h deleted file mode 100644 index 2ac1a320d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_JPEG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppDecodeResizeJpegOp : public TensorOp { - public: - DvppDecodeResizeJpegOp(int32_t resized_height, int32_t resized_width) - : resized_height_(resized_height), resized_width_(resized_width) {} - - /// \brief Destructor - ~DvppDecodeResizeJpegOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodeResizeJpegOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - int32_t resized_height_; - int32_t resized_width_; - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_RESIZE_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.cc deleted file mode 100644 index 46b3031a4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h" - -#include "include/api/context.h" -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" - -namespace mindspore { -namespace dataset { -const VdecOutputFormat DvppDecodeVideoOp::kDefVdecOutputFormat = VdecOutputFormat::kYuvSemiplanar420; -const char DvppDecodeVideoOp::kDefOutput[] = "./output"; - -Status DvppDecodeVideoOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input video buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - auto data_size = input->SizeInBytes(); - // assuem that output equals to input - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromTensor(input, output)); - - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "DvppDecodeVideo: Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // initialize the resource of D-chip and set up all configures - - auto dvpp_video = AclAdapter::GetInstance().CreateDvppVideo(context, buffer, data_size, width_, height_, - static_cast(en_type_), - static_cast(format_), output_); - AclLiteError res = AclAdapter::GetInstance().InitDvppVideo(dvpp_video); - if (res != ACLLITE_OK) { - (void)AclAdapter::GetInstance().CloseDvppVideo(dvpp_video); - AclAdapter::GetInstance().Release(); - std::string error = "DvppDecodeVideo: Failed to initialize DvppVideo:" + std::to_string(res); - RETURN_STATUS_UNEXPECTED(error); - } - - res = AclAdapter::GetInstance().DvppVideoDumpFrame(dvpp_video); - if (res != ACLLITE_OK) { - (void)AclAdapter::GetInstance().CloseDvppVideo(dvpp_video); - AclAdapter::GetInstance().Release(); - std::string error = "DvppDecodeVideo: Error in DumpFrame:" + std::to_string(res); - RETURN_STATUS_UNEXPECTED(error); - } - (void)AclAdapter::GetInstance().CloseDvppVideo(dvpp_video); - } catch (const std::exception &e) { - std::string error = "[ERROR] Error in DvppDecodeVideoOp:" + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppDecodeVideoOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppDecodeVideo: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - outputs = inputs; - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppDecodeVideo: Invalid input shape."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h deleted file mode 100644 index 9e240388e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_VIDEO_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_VIDEO_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "src/common/log_adapter.h" - -namespace mindspore { -namespace dataset { -class DvppDecodeVideoOp : public TensorOp { - public: - // Default values - static const VdecOutputFormat kDefVdecOutputFormat; - static const char kDefOutput[]; - - DvppDecodeVideoOp(uint32_t width, uint32_t height, VdecStreamFormat type, - VdecOutputFormat out_format = kDefVdecOutputFormat, const std::string &output = kDefOutput) - : width_(width), height_(height), format_(out_format), en_type_(type), output_(output) {} - - /// \brief Destructor - ~DvppDecodeVideoOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodeVideoOp; } - - private: - uint32_t width_; - uint32_t height_; - - /* 1:YUV420 semi-planner(nv12) - 2:YVU420 semi-planner(nv21) - */ - VdecOutputFormat format_; - - /* 0:H265 main level - * 1:H264 baseline level - * 2:H264 main level - * 3:H264 high level - */ - VdecStreamFormat en_type_; - std::string output_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_DECODE_VIDEO_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.cc deleted file mode 100644 index c66f4fdd4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.cc +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h" - -#include - -namespace mindspore { -namespace dataset { -Status DvppNormalizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - std::vector yuv_shape = input->GetYuvStrideShape(); - const size_t yuv_shape_size = 4; - CHECK_FAIL_RETURN_UNEXPECTED(yuv_shape.size() == yuv_shape_size, "yuv_shape requires 4 elements."); - RETURN_IF_NOT_OK((*output)->SetAttributes(input->GetDeviceMutableBuffer(), input->DeviceDataSize(), yuv_shape[0], - yuv_shape[1], yuv_shape[2], yuv_shape[3])); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the output result from device memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppNormalizeOp::SetAscendResource(const std::shared_ptr &resource) { return Status::OK(); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h deleted file mode 100644 index 00544d7aa..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_NORMALIZE_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_NORMALIZE_JPEG_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppNormalizeOp : public TensorOp { - public: - DvppNormalizeOp(std::vector mean, std::vector std) : mean_(std::move(mean)), std_(std::move(std)) {} - - ~DvppNormalizeOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kDvppNormalizeOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - std::vector mean_; - std::vector std_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_NORMALIZE_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.cc deleted file mode 100644 index 8b288abeb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.cc +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status DvppResizeJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetDeviceBuffer() != nullptr, "The input image buffer is empty."); - std::string last_step = "Decode"; - DvppDataInfo *imageinfo(AclAdapter::GetInstance().GetDecodeDeviceData(processor_.get())); - RETURN_UNEXPECTED_IF_NULL(imageinfo); - if (!imageinfo->data) { - last_step = "Crop"; - } - APP_ERROR ret = AclAdapter::GetInstance().JPEG_R(processor_.get(), last_step); - if (ret != APP_ERR_OK) { - ret = AclAdapter::GetInstance().ReleaseAclProcess(processor_.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release memory failed."); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - DvppDataInfo *ResizeOut(AclAdapter::GetInstance().GetResizedDeviceData(processor_.get())); - RETURN_UNEXPECTED_IF_NULL(ResizeOut); - const TensorShape dvpp_shape({1, 1, 1}); - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, output)); - RETURN_IF_NOT_OK((*output)->SetAttributes(ResizeOut->data, ResizeOut->dataSize, ResizeOut->width, - ResizeOut->widthStride, ResizeOut->height, ResizeOut->heightStride)); - if (!((*output)->HasDeviceData())) { - std::string error = "[ERROR] Fail to get the Output result from device memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppResizeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppResizeJpegOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!IsNonEmptyJPEG(input)) { - RETURN_STATUS_UNEXPECTED("DvppReiszeJpegOp only support process jpeg image."); - } - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->GetBuffer() != nullptr, "The input image buffer is empty."); - auto *buffer = const_cast(input->GetBuffer()); - DvppDataInfo imageinfo; - imageinfo.dataSize = input->SizeInBytes(); - imageinfo.data = static_cast(buffer); - std::vector yuv_shape_ = input->GetYuvShape(); - imageinfo.width = yuv_shape_[0]; - imageinfo.widthStride = yuv_shape_[1]; - imageinfo.height = yuv_shape_[2]; - imageinfo.heightStride = yuv_shape_[3]; - imageinfo.format = 1; // 1 means PIXEL_FORMAT_YUV_SEMIPLANAR_420 - ResourceInfo resource; - resource.deviceIds.insert(0); - APP_ERROR ret = AclAdapter::GetInstance().InitResource(&resource); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init D-chip: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - int deviceId = *(resource.deviceIds.begin()); - void *context = AclAdapter::GetInstance().GetContext(deviceId); - // Second part end where we initialize the resource of D-chip and set up all configures - std::shared_ptr process(AclAdapter::GetInstance().CreateAclProcessWithPara(resized_width_, resized_height_, - context, false, nullptr, nullptr), - [](void *ptr) { AclAdapter::GetInstance().DestroyAclProcess(ptr); }); - - ret = AclAdapter::GetInstance().InitAclProcess(process.get()); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in Init resource: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - ret = AclAdapter::GetInstance().JPEG_R_WITH_DATA(process.get(), imageinfo); - if (ret != APP_ERR_OK) { - AclAdapter::GetInstance().Release(); - std::string error = "Error in dvpp processing: " + std::to_string(ret); - RETURN_STATUS_UNEXPECTED(error); - } - - // Third part end where we execute the core function of dvpp - auto *ret_ptr = static_cast(AclAdapter::GetInstance().GetMemoryData(process.get())); - DvppDataInfo *ResizeOut = AclAdapter::GetInstance().GetResizedDeviceData(process.get()); - RETURN_UNEXPECTED_IF_NULL(ResizeOut); - dsize_t dvpp_length = ResizeOut->dataSize; - const TensorShape dvpp_shape({dvpp_length, 1, 1}); - uint32_t resized_height = ResizeOut->height; - uint32_t resized_heightStride = ResizeOut->heightStride; - uint32_t resized_width = ResizeOut->width; - uint32_t resized_widthStride = ResizeOut->widthStride; - const DataType dvpp_data_type(DataType::DE_UINT8); - RETURN_IF_NOT_OK(mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, output)); - RETURN_IF_NOT_OK((*output)->SetYuvShape(resized_width, resized_widthStride, resized_height, resized_heightStride)); - if (!((*output)->HasData())) { - std::string error = "[ERROR] Fail to get the Output result from memory!"; - RETURN_STATUS_UNEXPECTED(error); - } - ret = AclAdapter::GetInstance().DeviceMemoryRelease(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release device memory failed."); - ret = AclAdapter::GetInstance().ReleaseAclProcess(process.get()); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "Release host memory failed."); - // Last part end where we transform the processed data into a tensor which can be applied in later units. - } catch (const std::exception &e) { - std::string error = "[ERROR] Fail in DvppResizeJpegOp: " + std::string(e.what()); - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -Status DvppResizeJpegOp::SetAscendResource(const std::shared_ptr &resource) { - processor_ = resource->GetInstance(); - if (!processor_) { - RETURN_STATUS_UNEXPECTED("Resource initialize fail, please check your env."); - } - APP_ERROR ret = AclAdapter::GetInstance().SetResizeParas(processor_.get(), resized_width_, resized_height_); - CHECK_FAIL_RETURN_UNEXPECTED(ret == APP_ERR_OK, "SetResizeParas failed."); - return Status::OK(); -} - -Status DvppResizeJpegOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, 1, 1}); // we don't know what is output image size, but we know it should be 1 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "DvppResizeJpeg: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - (void)outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), "DvppResizeJpeg: Invalid input shape."); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h deleted file mode 100644 index 1e6b98dc2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_RESIZE_JPEG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_RESIZE_JPEG_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/acl_adapter.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class DvppResizeJpegOp : public TensorOp { - public: - DvppResizeJpegOp(int32_t resized_height, int32_t resized_width) - : resized_height_(resized_height), resized_width_(resized_width) {} - - /// \brief Destructor - ~DvppResizeJpegOp() = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kDvppDecodeResizeJpegOp; } - - Status SetAscendResource(const std::shared_ptr &resource) override; - - private: - int32_t resized_height_; - int32_t resized_width_; - std::shared_ptr processor_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_DVPP_RESIZE_JPEG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h deleted file mode 100644 index f38e39ecc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h +++ /dev/null @@ -1,191 +0,0 @@ -/** -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_ERROR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_ERROR_H_ - -using AclLiteError = int; - -constexpr int ACLLITE_OK = 0; -constexpr int ACLLITE_ERROR = 1; -constexpr int ACLLITE_ERROR_INVALID_ARGS = 2; -constexpr int ACLLITE_ERROR_SET_ACL_CONTEXT = 3; -constexpr int ACLLITE_ERROR_GET_ACL_CONTEXT = 4; -constexpr int ACLLITE_ERROR_CREATE_ACL_CONTEXT = 5; -constexpr int ACLLITE_ERROR_CREATE_THREAD = 6; -constexpr int ACLLITE_ERROR_CREATE_STREAM = 7; -constexpr int ACLLITE_ERROR_GET_RUM_MODE = 8; -constexpr int ACLLITE_ERROR_APP_INIT = 9; -constexpr int ACLLITE_ERROR_DEST_INVALID = 10; -constexpr int ACLLITE_ERROR_INITED_ALREADY = 11; -constexpr int ACLLITE_ERROR_ENQUEUE = 12; -constexpr int ACLLITE_ERROR_WRITE_FILE = 13; -constexpr int ACLLITE_ERROR_THREAD_ABNORMAL = 14; -constexpr int ACLLITE_ERROR_START_THREAD = 15; -constexpr int ACLLITE_ERROR_ADD_THREAD = 16; - -// malloc or new memory failed -constexpr int ACLLITE_ERROR_MALLOC = 101; -// aclrtMalloc failed -constexpr int ACLLITE_ERROR_MALLOC_DEVICE = 102; - -constexpr int ACLLITE_ERROR_MALLOC_DVPP = 103; -// access file failed -constexpr int ACLLITE_ERROR_ACCESS_FILE = 201; -// the file is invalid -constexpr int ACLLITE_ERROR_INVALID_FILE = 202; -// open file failed -constexpr int ACLLITE_ERROR_OPEN_FILE = 203; - -// load model repeated -constexpr int ACLLITE_ERROR_LOAD_MODEL_REPEATED = 301; - -constexpr int ACLLITE_ERROR_NO_MODEL_DESC = 302; -// load mode by acl failed -constexpr int ACLLITE_ERROR_LOAD_MODEL = 303; - -constexpr int ACLLITE_ERROR_CREATE_MODEL_DESC = 304; - -constexpr int ACLLITE_ERROR_GET_MODEL_DESC = 305; - -constexpr int ACLLITE_ERROR_CREATE_DATASET = 306; - -constexpr int ACLLITE_ERROR_CREATE_DATA_BUFFER = 307; - -constexpr int ACLLITE_ERROR_ADD_DATASET_BUFFER = 308; - -constexpr int ACLLITE_ERROR_EXECUTE_MODEL = 309; - -constexpr int ACLLITE_ERROR_GET_DATASET_BUFFER = 310; - -constexpr int ACLLITE_ERROR_GET_DATA_BUFFER_ADDR = 311; - -constexpr int ACLLITE_ERROR_GET_DATA_BUFFER_SIZE = 312; - -constexpr int ACLLITE_ERROR_COPY_DATA = 313; - -constexpr int ACLLITE_ERROR_SET_CAMERA = 400; - -constexpr int ACLLITE_ERROR_CAMERA_NO_ACCESSABLE = 401; - -constexpr int ACLLITE_ERROR_OPEN_CAMERA = 402; - -constexpr int ACLLITE_ERROR_READ_CAMERA_FRAME = 403; - -constexpr int ACLLITE_ERROR_UNSURPPORT_PROPERTY = 404; - -constexpr int ACLLITE_ERROR_INVALID_PROPERTY_VALUE = 405; - -constexpr int ACLLITE_ERROR_UNSURPPORT_VIDEO_CAPTURE = 406; - -constexpr int ACLLITE_ERROR_CREATE_DVPP_CHANNEL_DESC = 501; - -constexpr int ACLLITE_ERRROR_CREATE_DVPP_CHANNEL = 502; - -constexpr int ACLLITE_ERROR_CREATE_PIC_DESC = 503; - -constexpr int ACLLITE_ERROR_CREATE_RESIZE_CONFIG = 504; - -constexpr int ACLLITE_ERROR_RESIZE_ASYNC = 505; - -constexpr int ACLLITE_ERROR_SYNC_STREAM = 506; - -constexpr int ACLLITE_ERROR_JPEGE_ASYNC = 507; - -constexpr int ACLLITE_ERROR_JPEGD_ASYNC = 508; - -constexpr int ACLLITE_ERROR_FFMPEG_DECODER_INIT = 601; - -constexpr int ACLLITE_ERROR_OPEN_VIDEO_UNREADY = 602; - -constexpr int ACLLITE_ERROR_TOO_MANY_VIDEO_DECODERS = 603; - -constexpr int ACLLITE_ERROR_SET_VDEC_CHANNEL_ID = 604; - -constexpr int ACLLITE_ERROR_SET_STREAM_DESC_DATA = 605; - -constexpr int ACLLITE_ERROR_SET_VDEC_CHANNEL_THREAD_ID = 606; - -constexpr int ACLLITE_ERROR_SET_VDEC_CALLBACK = 607; - -constexpr int ACLLITE_ERROR_SET_VDEC_ENTYPE = 608; - -constexpr int ACLLITE_ERROR_SET_VDEC_PIC_FORMAT = 609; - -constexpr int ACLLITE_ERROR_CREATE_VDEC_CHANNEL = 610; - -constexpr int ACLLITE_ERROR_CREATE_STREAM_DESC = 611; - -constexpr int ACLLITE_ERROR_SET_STREAM_DESC_EOS = 612; - -constexpr int ACLLITE_ERROR_SET_STREAM_DESC_SIZE = 613; - -constexpr int ACLLITE_ERROR_SET_PIC_DESC_DATA = 614; - -constexpr int ACLLITE_ERROR_SET_PIC_DESC_SIZE = 615; - -constexpr int ACLLITE_ERROR_SET_PIC_DESC_FORMAT = 616; - -constexpr int ACLLITE_ERROR_VDEC_IS_EXITTING = 617; - -constexpr int ACLLITE_ERROR_VDEC_SET_WIDTH = 618; - -constexpr int ACLLITE_ERROR_VDEC_WIDTH_INVALID = 619; - -constexpr int ACLLITE_ERROR_VDEC_HEIGHT_INVALID = 620; - -constexpr int ACLLITE_ERROR_VDEC_SET_HEIGHT = 621; - -constexpr int ACLLITE_ERROR_VDEC_ENTYPE_INVALID = 622; - -constexpr int ACLLITE_ERROR_VDEC_FORMAT_INVALID = 623; - -constexpr int ACLLITE_ERROR_VDEC_INVALID_PARAM = 624; - -constexpr int ACLLITE_ERROR_VDEC_SEND_FRAME = 625; - -constexpr int ACLLITE_ERROR_VDEC_QUEUE_FULL = 626; - -constexpr int ACLLITE_ERROR_SET_RTSP_TRANS = 627; - -constexpr int ACLLITE_ERROR_READ_EMPTY = 628; - -constexpr int ACLLITE_ERROR_VIDEO_DECODER_STATUS = 629; - -constexpr int ACLLITE_ERROR_DECODE_FINISH = 630; - -constexpr int ACLLITE_ERROR_H26X_FRAME = 631; - -constexpr int ACLLITE_ERROR_VENC_STATUS = 701; - -constexpr int ACLLITE_ERROR_VENC_QUEUE_FULL = 702; - -constexpr int ACLLITE_ERROR_CREATE_VENC_CHAN_DESC = 703; - -constexpr int ACLLITE_ERROR_SET_VENC_CHAN_TID = 704; - -constexpr int ACLLITE_ERROR_VENC_SET_EOS = 705; - -constexpr int ACLLITE_ERROR_VENC_SET_IF_FRAME = 706; - -constexpr int ACLLITE_ERROR_CREATE_VENC_CHAN = 707; - -constexpr int ACLLITE_ERROR_VENC_CREATE_FRAME_CONFIG = 708; - -constexpr int ACLLITE_ERROR_VENC_SEND_FRAME = 709; - -constexpr int ACLLITE_ERROR_SUBSCRIBE_REPORT = 710; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_ERROR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h deleted file mode 100644 index 385cc6a72..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h +++ /dev/null @@ -1,102 +0,0 @@ -/** -* Adapted from -* https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/include/AclLiteType.h -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_TYPE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_TYPE_H_ - -#include - -#include -#include - -#include "acl/acl.h" -#include "acl/ops/acl_dvpp.h" - -enum class MemoryType { MEMORY_NORMAL = 0, MEMORY_HOST, MEMORY_DEVICE, MEMORY_DVPP, MEMORY_INVALID_TYPE }; - -enum class CopyDirection { TO_DEVICE = 0, TO_HOST, INVALID_COPY_DIRECT }; - -enum class CameraId { - CAMERA_ID_0 = 0, - CAMERA_ID_1, - CAMERA_ID_INVALID, -}; - -enum VencStatus { STATUS_VENC_INIT = 0, STATUS_VENC_WORK, STATUS_VENC_FINISH, STATUS_VENC_EXIT, STATUS_VENC_ERROR }; - -struct VencConfig { - uint32_t maxWidth = 0; - uint32_t maxHeight = 0; - std::string outFile; - acldvppPixelFormat format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; - acldvppStreamFormat enType = H264_MAIN_LEVEL; - aclrtContext context = nullptr; - aclrtRunMode runMode = ACL_HOST; -}; - -struct ImageData { - acldvppPixelFormat format; - uint32_t width = 0; - uint32_t height = 0; - uint32_t alignWidth = 0; - uint32_t alignHeight = 0; - uint32_t size = 0; - std::shared_ptr data = nullptr; -}; - -struct FrameData { - bool isFinished = false; - uint32_t frameId = 0; - uint32_t size = 0; - void *data = nullptr; -}; - -struct Resolution { - uint32_t width = 0; - uint32_t height = 0; -}; - -struct Rect { - uint32_t ltX = 0; - uint32_t ltY = 0; - uint32_t rbX = 0; - uint32_t rbY = 0; -}; - -struct BBox { - Rect rect; - uint32_t score = 0; - std::string text; -}; - -struct AclLiteMessage { - int dest; - int msgId; - std::shared_ptr data = nullptr; -}; - -struct DataInfo { - void *data; - uint32_t size; -}; - -struct InferenceOutput { - std::shared_ptr data = nullptr; - uint32_t size; -}; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_TYPE_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc deleted file mode 100644 index 88be2dae7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.cc +++ /dev/null @@ -1,524 +0,0 @@ -/** -* Adapted from -* https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/src/AclLiteUtils.cpp -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "acl/ops/acl_dvpp.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -namespace { -const char COMMENT_CHAR = '#'; -const char EQUALS_CHAR = '='; -const char BLANK_SPACE_CHAR = ' '; -const char TABLE_CHAR = '\t'; - -const std::string kImagePathSeparator = ","; -const int kStatSuccess = 0; -const std::string kFileSperator = "/"; -const std::string kPathSeparator = "/"; -// output image prefix -const std::string kOutputFilePrefix = "out_"; - -const std::string kRegexIpAddr = - "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[0-9])\\." - "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." - "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." - "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)" - ":([1-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|" - "6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$"; - -// regex for verify video file name -const std::string kRegexVideoFile = "^.+\\.(mp4|h264|h265)$"; - -// regex for verify RTSP rtsp://ip:port/channelname -const std::string kRegexRtsp = "^rtsp://.*"; -} // namespace - -bool IsDigitStr(const std::string &str) { return std::all_of(str.begin(), str.end(), isdigit); } - -bool IsPathExist(const std::string &path) { - std::ifstream file(path, std::ios::in); - if (!file) { - return false; - } - file.close(); - return true; -} - -bool IsVideoFile(const std::string &path) { - std::regex regexVideoFile(kRegexVideoFile.c_str()); - return regex_match(path, regexVideoFile); -} - -bool IsRtspAddr(const std::string &str) { - std::regex regexRtspAddress(kRegexRtsp.c_str()); - - return regex_match(str, regexRtspAddress); -} - -bool IsIpAddrWithPort(const std::string &addrStr) { - std::regex regexIpAddr(kRegexIpAddr.c_str()); - - return regex_match(addrStr, regexIpAddr); -} - -void ParseIpAddr(std::string &ip, std::string &port, const std::string &addr) { - std::string::size_type pos = addr.find(':'); - - (void)ip.assign(addr.substr(0, pos)); - (void)port.assign(addr.substr(pos + 1)); -} - -bool IsDirectory(const std::string &path) { - // get path stat - struct stat buf {}; - if (stat(path.c_str(), &buf) != kStatSuccess) { - return false; - } - - // check - return S_ISDIR(buf.st_mode); -} - -void SplitPath(const std::string &path, std::vector &pathVec) { - char *imageFile = strtok(const_cast(path.c_str()), kImagePathSeparator.c_str()); - while (imageFile) { - (void)pathVec.emplace_back(imageFile); - imageFile = strtok(nullptr, kImagePathSeparator.c_str()); - } -} - -void GetPathFiles(const std::string &path, std::vector &fileVec) { - if (IsDirectory(path)) { - DIR *dir = opendir(path.c_str()); - struct dirent *direntPtr; - while ((direntPtr = readdir(dir)) != nullptr) { - // skip . and .. - if (direntPtr->d_name[0] == '.') { - continue; - } - - // file path - std::string fullPath = path + kPathSeparator + direntPtr->d_name; - // directory need recursion - if (IsDirectory(fullPath)) { - GetPathFiles(fullPath, fileVec); - } else { - // put file - (void)fileVec.emplace_back(fullPath); - } - } - closedir(dir); - } else { - (void)fileVec.emplace_back(path); - } -} - -void GetAllFiles(const std::string &pathList, std::vector &fileVec) { - // split file path - std::vector pathVec; - SplitPath(pathList, pathVec); - - for (const std::string &everyPath : pathVec) { - // check path exist or not - if (!IsPathExist(pathList)) { - ACLLITE_LOG_ERROR("Failed to deal path=%s. Reason: not exist or can not access.", everyPath.c_str()); - continue; - } - // get files in path and sub-path - GetPathFiles(everyPath, fileVec); - } -} - -void *MallocMemory(uint32_t dataSize, MemoryType memType) { - void *buffer = nullptr; - aclError aclRet = ACL_SUCCESS; - - switch (memType) { - case MemoryType::MEMORY_NORMAL: - buffer = new uint8_t[dataSize]; - break; - case MemoryType::MEMORY_HOST: - aclRet = CALL_ASCEND_API(aclrtMallocHost, &buffer, dataSize); - break; - case MemoryType::MEMORY_DEVICE: - aclRet = CALL_ASCEND_API(aclrtMalloc, &buffer, dataSize, ACL_MEM_MALLOC_HUGE_FIRST); - break; - case MemoryType::MEMORY_DVPP: - aclRet = acldvppMalloc(&buffer, dataSize); - break; - default: - ACLLITE_LOG_ERROR("Invalid memory type %d", memType); - aclRet = ACL_ERROR_INVALID_PARAM; - break; - } - - if ((aclRet != ACL_SUCCESS) || (buffer == nullptr)) { - ACLLITE_LOG_ERROR("Malloc memory failed, type: %d, errorno:%d", memType, aclRet); - return nullptr; - } - - return buffer; -} - -void FreeMemory(void *mem, MemoryType memType) { - if (mem == nullptr) { - ACLLITE_LOG_ERROR("Invalid mem"); - return; - } - aclError ret = ACL_SUCCESS; - switch (memType) { - case MemoryType::MEMORY_NORMAL: - delete[](reinterpret_cast(mem)); - break; - case MemoryType::MEMORY_HOST: - ret = CALL_ASCEND_API(aclrtFreeHost, mem); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("aclrtFreeHost failed, errorno: %d", ret); - } - break; - case MemoryType::MEMORY_DEVICE: - ret = CALL_ASCEND_API(aclrtFree, mem); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("aclrtFree failed, errorno: %d", ret); - } - break; - case MemoryType::MEMORY_DVPP: - ret = acldvppFree(mem); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("acldvppFree failed, errorno: %d", ret); - } - break; - default: - ACLLITE_LOG_ERROR("Invalid memory type %d", memType); - break; - } -} - -aclrtMemcpyKind GetCopyPolicy(aclrtRunMode srcDev, CopyDirection direct, MemoryType memType) { - aclrtMemcpyKind policy = ACL_MEMCPY_HOST_TO_HOST; - - if (direct == CopyDirection::TO_DEVICE) { - if (srcDev == ACL_HOST) { - policy = ACL_MEMCPY_HOST_TO_DEVICE; - } else { - policy = ACL_MEMCPY_DEVICE_TO_DEVICE; - } - } else { // TO_HOST - if (srcDev == ACL_DEVICE) { - policy = ACL_MEMCPY_DEVICE_TO_HOST; - } - } - - return policy; -} - -void *CopyDataToDevice(const void *data, uint32_t size, aclrtRunMode curRunMode, MemoryType memType) { - if ((data == nullptr) || (size == 0) || ((curRunMode != ACL_HOST) && (curRunMode != ACL_DEVICE)) || - (memType >= MemoryType::MEMORY_INVALID_TYPE) || (memType == MemoryType::MEMORY_HOST)) { - ACLLITE_LOG_ERROR( - "Copy data args invalid, data %p, " - "size %d, src dev %d, memory type %d", - data, size, curRunMode, memType); - return nullptr; - } - - aclrtMemcpyKind policy = GetCopyPolicy(curRunMode, CopyDirection::TO_DEVICE, memType); - - return CopyData(data, size, policy, memType); -} - -AclLiteError CopyDataToDeviceEx(void *dest, uint32_t destSize, const void *src, uint32_t srcSize, - aclrtRunMode runMode) { - aclrtMemcpyKind policy = ACL_MEMCPY_HOST_TO_DEVICE; - if (runMode == ACL_DEVICE) { - policy = ACL_MEMCPY_DEVICE_TO_DEVICE; - } - - aclError aclRet = CALL_ASCEND_API(aclrtMemcpy, dest, destSize, src, srcSize, policy); - if (aclRet != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Copy data to device failed, aclRet is %d", aclRet); - return ACLLITE_ERROR; - } - - return ACLLITE_OK; -} - -void *CopyDataToHost(const void *data, uint32_t size, aclrtRunMode curRunMode, MemoryType memType) { - if ((data == nullptr) || (size == 0) || ((curRunMode != ACL_HOST) && (curRunMode != ACL_DEVICE)) || - ((memType != MemoryType::MEMORY_HOST) && (memType != MemoryType::MEMORY_NORMAL))) { - ACLLITE_LOG_ERROR( - "Copy data args invalid, data %p, " - "size %d, src dev %d, memory type %d", - data, size, curRunMode, memType); - return nullptr; - } - - aclrtMemcpyKind policy = GetCopyPolicy(curRunMode, CopyDirection::TO_HOST, memType); - - return CopyData(data, size, policy, memType); -} - -AclLiteError CopyDataToHostEx(void *dest, uint32_t destSize, const void *src, uint32_t srcSize, aclrtRunMode runMode) { - aclrtMemcpyKind policy = ACL_MEMCPY_DEVICE_TO_HOST; - if (runMode == ACL_DEVICE) { - policy = ACL_MEMCPY_DEVICE_TO_DEVICE; - } - - aclError aclRet = CALL_ASCEND_API(aclrtMemcpy, dest, destSize, src, srcSize, policy); - if (aclRet != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Copy data to device failed, aclRet is %d", aclRet); - return ACLLITE_ERROR; - } - - return ACLLITE_OK; -} - -void *CopyData(const void *data, uint32_t size, aclrtMemcpyKind policy, MemoryType memType) { - void *buffer = MallocMemory(size, memType); - if (buffer == nullptr) { - return nullptr; - } - - aclError aclRet = CALL_ASCEND_API(aclrtMemcpy, buffer, size, data, size, policy); - if (aclRet != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Copy data to device failed, aclRet is %d", aclRet); - FreeMemory(buffer, memType); - return nullptr; - } - - return buffer; -} - -AclLiteError CopyImageToLocal(ImageData &destImage, ImageData &srcImage, aclrtRunMode curRunMode) { - void *data = CopyDataToHost(srcImage.data.get(), srcImage.size, curRunMode, MemoryType::MEMORY_NORMAL); - if (data == nullptr) { - return ACLLITE_ERROR_COPY_DATA; - } - - destImage.format = srcImage.format; - destImage.width = srcImage.width; - destImage.height = srcImage.height; - destImage.size = srcImage.size; - destImage.alignWidth = srcImage.alignWidth; - destImage.alignHeight = srcImage.alignHeight; - destImage.data = SHARED_PTR_U8_BUF(data); - - return ACLLITE_OK; -} - -AclLiteError CopyImageToDevice(ImageData &destImage, ImageData &srcImage, aclrtRunMode curRunMode, MemoryType memType) { - void *data = CopyDataToDevice(srcImage.data.get(), srcImage.size, curRunMode, memType); - if (data == nullptr) { - return ACLLITE_ERROR_COPY_DATA; - } - - destImage.format = srcImage.format; - destImage.width = srcImage.width; - destImage.height = srcImage.height; - destImage.size = srcImage.size; - destImage.alignWidth = srcImage.alignWidth; - destImage.alignHeight = srcImage.alignHeight; - - if (memType == MemoryType::MEMORY_DEVICE) { - destImage.data = SHARED_PTR_DEV_BUF(data); - } else { - destImage.data = SHARED_PTR_DVPP_BUF(data); - } - - return ACLLITE_OK; -} - -AclLiteError ReadBinFile(const std::string &fileName, void *&data, uint32_t &size) { - struct stat sBuf {}; - int fileStatus = stat(fileName.data(), &sBuf); - if (fileStatus == -1) { - ACLLITE_LOG_ERROR("failed to get file"); - return ACLLITE_ERROR_ACCESS_FILE; - } - if (S_ISREG(sBuf.st_mode) == 0) { - ACLLITE_LOG_ERROR("%s is not a file, please enter a file", fileName.c_str()); - return ACLLITE_ERROR_INVALID_FILE; - } - std::ifstream binFile(fileName, std::ifstream::in | std::ifstream::binary); - if (!binFile.is_open()) { - ACLLITE_LOG_ERROR("open file %s failed", fileName.c_str()); - return ACLLITE_ERROR_OPEN_FILE; - } - - (void)binFile.seekg(0, std::ifstream::end); - uint32_t binFileBufferLen = binFile.tellg(); - if (binFileBufferLen == 0) { - ACLLITE_LOG_ERROR("binfile is empty, filename is %s", fileName.c_str()); - binFile.close(); - return ACLLITE_ERROR_INVALID_FILE; - } - - (void)binFile.seekg(0, std::ifstream::beg); - - auto *binFileBufferData = new (std::nothrow) uint8_t[binFileBufferLen]; - if (binFileBufferData == nullptr) { - ACLLITE_LOG_ERROR("malloc binFileBufferData failed"); - binFile.close(); - return ACLLITE_ERROR_MALLOC; - } - (void)binFile.read(reinterpret_cast(binFileBufferData), binFileBufferLen); - binFile.close(); - - data = binFileBufferData; - size = binFileBufferLen; - - return ACLLITE_OK; -} - -AclLiteError ReadJpeg(ImageData &image, const std::string &fileName) { - uint32_t size = 0; - void *buf = nullptr; - - auto lite_ret = ReadBinFile(fileName, buf, size); - if (lite_ret != ACLLITE_OK) { - delete[](reinterpret_cast(buf)); - return lite_ret; - } - - int32_t ch = 0; - auto ret = acldvppJpegGetImageInfo(buf, size, &(image.width), &(image.height), &ch); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("acldvppJpegGetImageInfo failed, errorno: %d", ret); - delete[](reinterpret_cast(buf)); - return ACLLITE_ERROR; - } - if (image.width == 0 || image.height == 0) { - ACLLITE_LOG_ERROR("unsupported format, only Baseline JPEG"); - delete[](reinterpret_cast(buf)); - return ACLLITE_ERROR; - } - image.data.reset(reinterpret_cast(buf), [](const uint8_t *p) { delete[](p); }); - image.size = size; - - return ACLLITE_OK; -} - -void SaveBinFile(const std::string &filename, const void *data, uint32_t size) { - FILE *outFileFp = fopen(filename.c_str(), "wb+"); - if (outFileFp == nullptr) { - ACLLITE_LOG_ERROR("Save file %s failed for open error", filename.c_str()); - return; - } - (void)fwrite(data, 1, size, outFileFp); - - (void)fflush(outFileFp); - (void)fclose(outFileFp); -} - -bool IsSpace(char c) { return (c == BLANK_SPACE_CHAR || c == TABLE_CHAR); } - -void Trim(std::string &str) { - if (str.empty()) { - return; - } - int32_t i; - int32_t start_pos; - int32_t end_pos; - for (i = 0; i < str.size(); ++i) { - if (!IsSpace(str[i])) { - break; - } - } - if (i == str.size()) { // is all blank space - str = ""; - return; - } - - start_pos = i; - - for (i = str.size() - 1; i >= 0; --i) { - if (!IsSpace(str[i])) { - break; - } - } - end_pos = i; - - str = str.substr(start_pos, end_pos - start_pos + 1); -} - -bool AnalyseLine(const std::string &line, std::string &key, std::string &value) { - if (line.empty()) { - return false; - } - - int start_pos = 0; - auto end_pos = line.size() - 1; - std::string::size_type pos = line.find(COMMENT_CHAR); - if (pos != std::string::npos) { - if (pos == 0) { // the first character is # - return false; - } - end_pos = pos - 1; - } - std::string new_line = line.substr(start_pos, end_pos - start_pos + 1); // delete comment - pos = new_line.find(EQUALS_CHAR); - if (pos == std::string::npos) { // has no = - return false; - } - - key = new_line.substr(0, pos); - value = new_line.substr(pos + 1, end_pos + 1 - (pos + 1)); - - Trim(key); - if (key.empty()) { - return false; - } - Trim(value); - return true; -} - -bool ReadConfig(std::map &config, const char *configFile) { - config.clear(); - std::ifstream infile(configFile, std::ifstream::in); - if (!infile) { - return false; - } - std::string line; - std::string key; - std::string value; - while (getline(infile, line)) { - if (AnalyseLine(line, key, value)) { - config[key] = value; - } - } - - infile.close(); - return true; -} - -void PrintConfig(const std::map &config) { - auto mIter = config.begin(); - for (; mIter != config.end(); ++mIter) { - std::cout << mIter->first << "=" << mIter->second << std::endl; - } -} diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h deleted file mode 100644 index b669c8c98..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h +++ /dev/null @@ -1,473 +0,0 @@ -/** -* Adapted from -* https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/include/AclLiteUtils.h -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_UTILS_H_ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "acl/ops/acl_dvpp.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -/** - * @brief calculate RGB 24bits image size - * @param [in]: width: image width - * @param [in]: height: image height - * @return bytes size of image - */ -#define RGBU8_IMAGE_SIZE(width, height) ((width) * (height)*3) - -/** - * @brief calculate RGB C3F32 image size - * @param [in]: width: image width - * @param [in]: height: image height - * @return bytes size of image - */ -#define RGBF32_IMAGE_SIZE(width, height) ((width) * (height)*3 * sizeof(float)) - -/** - * @brief calculate YUVSP420 image size - * @param [in]: width: image width - * @param [in]: height: image height - * @return bytes size of image - */ -#define YUV420SP_SIZE(width, height) ((width) * (height)*3 / 2) - -/** - * @brief calculate YUVSP420 nv12 load to opencv mat height parameter - * @param [in]: height: yuv image height - * @return bytes size of image - */ -#define YUV420SP_CV_MAT_HEIGHT(height) ((height)*3 / 2) - -/** - * @brief generate shared pointer of dvpp memory - * @param [in]: buf: memory pointer, malloc by acldvppMalloc - * @return shared pointer of input buffer - */ -#define SHARED_PTR_DVPP_BUF(buf) \ - (std::shared_ptr(reinterpret_cast(buf), [](uint8_t *p) { acldvppFree(p); })) - -/** - * @brief generate shared pointer of device memory - * @param [in]: buf: memory pointer, malloc by acldvppMalloc - * @return shared pointer of input buffer - */ -#define SHARED_PTR_DEV_BUF(buf) \ - (std::shared_ptr(reinterpret_cast(buf), [](uint8_t *p) { CALL_ASCEND_API(aclrtFree, p); })) - -/** - * @brief generate shared pointer of memory - * @param [in]: buf memory pointer, malloc by new - * @return shared pointer of input buffer - */ -#define SHARED_PTR_U8_BUF(buf) \ - (std::shared_ptr(reinterpret_cast(buf), [](uint8_t *p) { delete[](p); })) - -/** - * @brief calculate aligned number - * @param [in]: num: the original number that to aligned - * @param [in]: align: the align factor - * @return the number after aligned - */ -#define ALIGN_UP(num, align) (((num) + (align)-1) & ~((align)-1)) - -/** - * @brief calculate number align with 2 - * @param [in]: num: the original number that to aligned - * @return the number after aligned - */ -#define ALIGN_UP2(num) ALIGN_UP(num, 2) - -/** - * @brief calculate number align with 16 - * @param [in]: num: the original number that to aligned - * @return the number after aligned - */ -#define ALIGN_UP16(num) ALIGN_UP(num, 16) - -/** - * @brief calculate number align with 128 - * @param [in]: num: the original number that to aligned - * @return the number after aligned - */ -#define ALIGN_UP128(num) ALIGN_UP(num, 128) - -/** - * @brief calculate elements num of array - * @param [in]: array: the array variable - * @return elements num of array - */ -#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0])) - -/** - * @brief Write acl error level log to host log - * @param [in]: fmt: the input format string - * @return none - */ -#define ACLLITE_LOG_ERROR(fmt, ...) \ - do { \ - aclAppLog(ACL_ERROR, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - fprintf(stdout, "[ERROR] " fmt "\n", ##__VA_ARGS__); \ - } while (0) - -/** - * @brief Write acl info level log to host log - * @param [in]: fmt: the input format string - * @return none - */ -#define ACLLITE_LOG_INFO(fmt, ...) \ - do { \ - aclAppLog(ACL_INFO, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - fprintf(stdout, "[INFO] " fmt "\n", ##__VA_ARGS__); \ - } while (0) - -/** - * @brief Write acl warining level log to host log - * @param [in]: fmt: the input format string - * @return none - */ -#define ACLLITE_LOG_WARNING(fmt, ...) \ - do { \ - aclAppLog(ACL_WARNING, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - fprintf(stdout, "[WARNING] " fmt "\n", ##__VA_ARGS__); \ - } while (0) - -/** - * @brief Write acl debug level log to host log - * @param [in]: fmt: the input format string - * @return none - */ -#define ACLLITE_LOG_DEBUG(fmt, ...) \ - do { \ - aclAppLog(ACL_DEBUG, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - fprintf(stdout, "[INFO] " fmt "\n", ##__VA_ARGS__); \ - } while (0) - -/** - * @brief define variable record time && - set start time - * @param [X]: function name - * @return X_START X_END - */ -#define TIME_START(X) auto X##_START = std::chrono::steady_clock::now(), X##_END = X##_START - -/** - * @brief set end time - * @param [X]: function name - * @return none - */ -#define TIME_END(X) X##_END = std::chrono::steady_clock::now() - -/** - * @brief calculate time by nanosecond - * @param [X]: function name - * @return none - */ -#define TIME_NSEC(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by nanosecond - * @param [X]: function name - * @return none - */ -#define TIME_NSEC_SHOW(X) cout << "Func " << #X << " cost : " << TIME_NSEC(X) << " ns " << endl - -/** - * @brief calculate time and show by microsecond - * @param [X]: variable name - * @return none - */ -#define TIME_USEC(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by microsecond - * @param [X]: function name - * @return none - */ -#define TIME_USEC_SHOW(X) cout << "Func " << #X << " cost : " << TIME_USEC(X) << " us " << endl - -/** - * @brief calculate time and show by millisecond - * @param [X]: variable name - * @return none - */ -#define TIME_MSEC(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by millisecond - * @param [X]: function name - * @return none - */ -#define TIME_MSEC_SHOW(X) cout << "Func " << #X << " cost : " << TIME_MSEC(X) << " ms " << endl - -/** - * @brief calculate time and show by second - * @param [X]: variable name - * @return none - */ -#define TIME_SEC(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by second - * @param [X]: function name - * @return none - */ -#define TIME_SEC_SHOW(X) cout << "Func " << #X << " cost : " << TIME_SEC(X) << " s " << endl - -/** - * @brief calculate time and show by minute - * @param [X]: variable name - * @return none - */ -#define TIME_MINUTE(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by minute - * @param [X]: function name - * @return none - */ -#define TIME_MINUTE_SHOW(X) cout << "Func " << #X << " cost : " << TIME_MINUTE(X) << " min " << endl - -/** - * @brief calculate time and show by hour - * @param [X]: variable name - * @return none - */ -#define TIME_HOUR(X) std::chrono::duration_cast(X##_END - X##_START).count() - -/** - * @brief show time by hour - * @param [X]: function name - * @return none - */ -#define TIME_HOUR_SHOW(X) cout << "Func " << #X << " cost : " << TIME_HOUR(X) << " h " << endl - -/** - * @brief Recognize the string is a accessible directory or not - * @param [in]: path: the input string - * @return bool true: is directory; false: not directory - */ -bool IsDirectory(const std::string &path); - -/** - * @brief Copy data to device - * @param [in]: data: The data to copy - * @param [in]: size: The data bytes size - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @param [in]: memType: The dest memory type:MEMORY_NORMAL(in Atlas200DK), - * MEMORY_DEVICE, MEMORY_DVPP - * @return void* The dest memory pointer - */ -void *CopyDataToDevice(const void *data, uint32_t size, aclrtRunMode curRunMode, MemoryType memType); - -/** - * @brief Copy data to device buffer - * @param [in]: dest: The device buffer - * @param [in]: destSize: The device buffer size - * @param [in]: src: The data to copy - * @param [in]: srcSize: The data bytes size - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @return AclLiteError ACLLITE_OK: copy success - * others: copy failed - */ -AclLiteError CopyDataToDeviceEx(void *dest, uint32_t destSize, const void *src, uint32_t srcSize, aclrtRunMode runMode); - -/** - * @brief Copy data to host - * @param [in]: data: The data to be copy - * @param [in]: size: The data bytes size - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @param [in]: memType: The dest memory type:MEMORY_NORMAL, MEMORY_HOST - * @return void* The dest memory pointer - */ -void *CopyDataToHost(const void *data, uint32_t size, aclrtRunMode curRunMode, MemoryType memType); - -/** - * @brief Copy data to host buffer - * @param [in]: dest: The host buffer - * @param [in]: destSize: The host buffer size - * @param [in]: src: The data to copy - * @param [in]: srcSize: The data bytes size - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @return AclLiteError ACLLITE_OK: copy success - * others: copy failed - */ -AclLiteError CopyDataToHostEx(void *dest, uint32_t destSize, const void *src, uint32_t srcSize, aclrtRunMode runMode); - -/** - * @brief Copy data to memory - * @param [in]: data: The data to be copy - * @param [in]: size: The data bytes size - * @param [in]: policy: the kind of sync, - * typedef enum aclrtMemcpyKind { - * ACL_MEMCPY_HOST_TO_HOST, // Memory copy from Host to Host - * ACL_MEMCPY_HOST_TO_DEVICE, // Memory copy from Host to Device - * ACL_MEMCPY_DEVICE_TO_HOST, // Memory copy from Device to Host - * ACL_MEMCPY_DEVICE_TO_DEVICE, // Memory copy from Device to Device - * } aclrtMemcpyKind; - * @param [in]: memType: The dest memory type - * @return void* The dest memory pointer - */ -void *CopyData(const void *data, uint32_t size, aclrtMemcpyKind policy, MemoryType memType); - -/** - * @brief Read jpeg image file. Only support baseline, not support progressive - * @param [out]: image: image data read from file. - * @param [in]: fileName: The data bytes size - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -AclLiteError ReadJpeg(ImageData &image, const std::string &fileName); - -/** - * @brief Get all files from file list string - * @param [in]: pathList: files list string, separate by ',', - * the element could be file path or directory - * @param [in]: fileVec: The data bytes size - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -void GetAllFiles(const std::string &pathList, std::vector &fileVec); - -/** - * @brief Save data to binary file - * @param [in]: filename: binary file name with path - * @param [in]: data: binary data - * @param [in]: size: bytes size of data - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -void SaveBinFile(const std::string &filename, const void *data, uint32_t size); - -/** - * @brief Read binary file to buffer - * @param [in]: fileName: binary file name with path - * @param [in]: data: buffer - * @param [in]: size: buffer size - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -AclLiteError ReadBinFile(const std::string &fileName, void *&data, uint32_t &size); - -/** - * @brief Copy image to memory that malloc by new - * @param [out]: destImage: The image after copy - * @param [in]: srcImage: The image to copy - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -AclLiteError CopyImageToLocal(ImageData &destImage, ImageData &srcImage, aclrtRunMode curRunMode); - -/** - * @brief Copy image to acl device - * @param [out]: destImage: The image after copy - * @param [in]: srcImage: The image to copy - * @param [in]: curRunMode: The run mode, get by aclrtGetRunMode, - * Atlas200DK is ACL_DEVICE, Atlas300 is ACL_HOST - * @param [in]: memType: memory type, dvpp is MEMORY_DVPP, - * device is MEMPRY_DEVICE - * @return AclLiteError ACLLITE_OK: read success - * others: read failed - */ -AclLiteError CopyImageToDevice(ImageData &destImage, ImageData &srcImage, aclrtRunMode curRunMode, MemoryType memType); - -/** - * @brief Match ip address string as <1-255>.<0-255>.<0-255>.<0-255>: - * @param [in]: addrStr: Ip address string - * @return bool true: The input string match success - * false: is not match - */ -bool IsIpAddrWithPort(const std::string &addrStr); - -/** - * @brief Split ip address string <1-255>.<0-255>.<0-255>.<0-255>: to - * ip and port - * @param [out]: ip: Ip address <1-255>.<0-255>.<0-255>.<0-255> - * @param [out]: port: port string - * @param [in]: addr: Ip address string - * @return None - */ -void ParseIpAddr(std::string &ip, std::string &port, const std::string &addr); - -/** - * @brief Judge input string is mp4 file path - * @param [in]: path: file path - * @return bool true: input string is mp4 file path - * false: is not mp4 file path - */ -bool IsVideoFile(const std::string &path); - -/** - * @brief Judge input string is rtsp addr link rtsp:// - * @param [in]: str: input string - * @return bool true: input string is rtsp address - * false: is not rtsp address - */ -bool IsRtspAddr(const std::string &str); - -/** - * @brief Judge input string is digit string - * @param [in]: str: input string - * @return bool true: input string is digit string - * false: is not rtsp address - */ -bool IsDigitStr(const std::string &str); - -/** - * @brief Test file path is exist or not - * @param [in]: path: file path - * @return bool true: file path is exist - * false: is not exist - */ -bool IsPathExist(const std::string &path); - -/** - * @brief read file and save information to config - * @param [out]: config: map, save option information - * @param [in]: configFile: string, file - * @return bool true: read config success - * false: read config fail - */ -bool ReadConfig(std::map &config, const char *configFile); - -/** - * @brief print option information - * @param [in]: m: map, save option information - * @return None - */ -void PrintConfig(const std::map &m); - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_LITE_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CMakeLists.txt b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CMakeLists.txt deleted file mode 100644 index 0255e0e9f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -file(GLOB_RECURSE _CURRENT_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") -set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_MD) -add_definitions(-DENABLE_DVPP_INTERFACE) - -set(DVPP_UTILS_SRC - # Ascend310 - MDAclProcess.cc - DvppCommon.cc - ErrorCode.cpp - ResourceManager.cc - AclLiteUtils.cc - VdecHelper.cc - dvpp_video.cc - # plugin - acl_plugin.cc - ) -if(NOT MSLITE_ENABLE_ACL) - set(DVPP_UTILS_SRC - ${DVPP_UTILS_SRC} - acl_env_guard.cc # in lite, src code has acl_env_guard.cc impl - ) -endif() - -add_library(dvpp_utils SHARED ${DVPP_UTILS_SRC}) -enable_target_when_only_build_plugins(dvpp_utils) - -if(NOT MSLITE_ENABLE_ACL) - find_library(acl_dvpp libacl_dvpp.so ${ASCEND_CANN_RUNTIME_PATH} ${ASCEND_TOOLKIT_RUNTIME_PATH}) - find_library(acl libascendcl.so ${ASCEND_CANN_RUNTIME_PATH} ${ASCEND_TOOLKIT_RUNTIME_PATH}) - find_library(nnopbase libnnopbase.so ${ASCEND_CANN_RUNTIME_PATH} ${ASCEND_TOOLKIT_RUNTIME_PATH}) - find_library(acl_dvpp_op libacl_dvpp_op.so ${ASCEND_CANN_RUNTIME_PATH} ${ASCEND_TOOLKIT_RUNTIME_PATH}) - find_library(acl_dvpp_mpi libacl_dvpp_mpi.so ${ASCEND_CANN_RUNTIME_PATH} ${ASCEND_TOOLKIT_RUNTIME_PATH}) - target_link_libraries(dvpp_utils PRIVATE _c_dataengine ${acl} ${acl_dvpp} - mindspore_core ${nnopbase} ${acl_dvpp_op} ${acl_dvpp_mpi}) -endif() -add_dependencies(dvpp_utils _mindspore_ascend_symbol_obj) -target_link_libraries(dvpp_utils PRIVATE $) - -if(MSLITE_ENABLE_CLOUD_MIND_DATA) - add_dependencies(dvpp_utils fbs_src) -endif() diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h deleted file mode 100644 index 421f4c771..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h +++ /dev/null @@ -1,172 +0,0 @@ -/** -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_COMMON_DATA_TYPE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_COMMON_DATA_TYPE_H_ - -#ifndef ENABLE_DVPP_INTERFACE -#define ENABLE_DVPP_INTERFACE -#endif -#include -#include -#include -#include -#include - -#define DVPP_ALIGN_UP(x, align) ((((x) + ((align)-1)) / (align)) * (align)) - -const uint32_t VIDEO_H264 = 0; -const uint32_t VIDEO_H265 = 1; - -const float SEC2MS = 1000.0; -const uint32_t VIDEO_PROCESS_THREAD = 16; -const int YUV_BGR_SIZE_CONVERT_3 = 3; -const int YUV_BGR_SIZE_CONVERT_2 = 2; -const int DVPP_JPEG_OFFSET = 8; -const int VPC_WIDTH_ALIGN = 16; -const int VPC_HEIGHT_ALIGN = 2; -const int JPEG_WIDTH_ALIGN = 128; -const int JPEG_HEIGHT_ALIGN = 16; -const int VPC_OFFSET_ALIGN = 2; - -// Data type of tensor -enum OpAttrType { - kBool = 0, - kInt = 1, - kFloat = 2, - kString = 3, - kListBool = 4, - kListInt = 6, - kListFloat = 7, - kListString = 8, - kListListInt = 9, -}; - -// operator attribution describe -// type decide whether the other attribute needed to set a value -struct OpAttr { - std::string name; - OpAttrType type; - int num; // LIST_BOOL/INT/FLOAT/STRING/LIST_LIST_INT need - uint8_t numBool; // BOOL need - int64_t numInt; // INT need - float numFloat; // FLOAT need - std::string numString; // STRING need - std::vector valuesBool; // LIST_BOOL need - std::vector valuesInt; // LIST_INT need - std::vector valuesFloat; // LIST_FLOAT need - std::vector valuesString; // LIST_STRING need - std::vector numLists; // LIST_LIST_INT need - std::vector> valuesListList; // LIST_LIST_INT need -}; - -// Description of image data -struct ImageInfo { - uint32_t width; // Image width - uint32_t height; // Image height - uint32_t lenOfByte; // Size of image data, bytes - std::shared_ptr data; // Smart pointer of image data -}; - -// Description of data in device -struct StreamData { - size_t size; // Size of memory, bytes - std::shared_ptr data; // Smart pointer of data -}; - -// Description of stream data -struct StreamInfo { - std::string format; - uint32_t height; - uint32_t width; - uint32_t channelId; - std::string streamPath; -}; - -// define the structure of an rectangle -struct Rectangle { - uint32_t leftTopX; - uint32_t leftTopY; - uint32_t rightBottomX; - uint32_t rightBottomY; -}; - -struct ObjectDetectInfo { - int32_t classId; - float confidence; - struct Rectangle location; -}; - -enum VpcProcessType { - VPC_PT_DEFAULT = 0, - VPC_PT_PADDING, // Resize with locked ratio and paste on upper left corner - VPC_PT_FIT, // Resize with locked ratio and paste on middle location - VPC_PT_FILL, // Resize with locked ratio and paste on whole locatin, the input image may be cropped -}; - -struct DvppDataInfo { - uint32_t width = 0; // Width of image - uint32_t height = 0; // Height of image - uint32_t widthStride = 0; // Width after align up - uint32_t heightStride = 0; // Height after align up - int format = 1; // Format of image - uint32_t frameId = 0; // Needed by video - uint32_t dataSize = 0; // Size of data in byte - uint8_t *data = nullptr; // Image data -}; - -struct CropRoiConfig { - uint32_t left; - uint32_t right; - uint32_t down; - uint32_t up; -}; - -struct DvppCropInputInfo { - struct DvppDataInfo dataInfo; - struct CropRoiConfig roi {}; -}; - -// Description of matrix info -struct MatrixInfo { - uint32_t row = 0; // row of matrix - uint32_t col = 0; // col of matrix - uint32_t dataSize = 0; // size of memory, bytes - std::shared_ptr data = nullptr; // data of matrix - int dataType = 1; // data Type of matrix -}; - -// Description of coefficient info -struct CoefficientInfo { - std::shared_ptr data = nullptr; // data of coefficient - int dataType = 1; // dataType -}; - -// define the input of BLAS operator such as producing: -// C = alpha * A * B + beta * C -struct BlasInput { - MatrixInfo A; - MatrixInfo B; - MatrixInfo C; - struct CoefficientInfo alpha; - struct CoefficientInfo beta; -}; - -extern bool g_vdecNotified[VIDEO_PROCESS_THREAD]; -extern bool g_vpcNotified[VIDEO_PROCESS_THREAD]; -extern bool g_inferNotified[VIDEO_PROCESS_THREAD]; -extern bool g_postNotified[VIDEO_PROCESS_THREAD]; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_COMMON_DATA_TYPE_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.cc deleted file mode 100644 index 291be3878..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.cc +++ /dev/null @@ -1,1829 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h" -#include -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -static const auto g_resizeConfigDeleter = [](acldvppResizeConfig *p) { (void)acldvppDestroyResizeConfig(p); }; -static const auto g_picDescDeleter = [](acldvppPicDesc *picDesc) { (void)acldvppDestroyPicDesc(picDesc); }; -static const auto g_roiConfigDeleter = [](acldvppRoiConfig *p) { (void)acldvppDestroyRoiConfig(p); }; -static const auto g_jpegeConfigDeleter = [](acldvppJpegeConfig *p) { (void)acldvppDestroyJpegeConfig(p); }; - -namespace { -constexpr int32_t kTimeLimit = -1; -} // namespace - -DvppCommon::DvppCommon(aclrtContext dvppContext, aclrtStream dvppStream) - : dvppContext_(dvppContext), dvppStream_(dvppStream) {} - -DvppCommon::DvppCommon(const VdecConfig &vdecConfig) : vdecConfig_(vdecConfig) {} - -/* - * @description: Create a channel for processing image data, - * the channel description is created by acldvppCreateChannelDesc - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::Init() { - dvppChannelDesc_ = acldvppCreateChannelDesc(); - if (dvppChannelDesc_ == nullptr) { - return APP_ERR_COMM_INVALID_POINTER; - } - - APP_ERROR ret = acldvppCreateChannel(dvppChannelDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to create dvpp channel: " << GetAppErrCodeInfo(ret) << "."; - (void)acldvppDestroyChannelDesc(dvppChannelDesc_); - dvppChannelDesc_ = nullptr; - return ret; - } - - return APP_ERR_OK; -} - -/* - * @description: Create a channel for processing video data, - * the channel description is created by aclvdecCreateChannelDesc - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::InitVdec() { - isVdec_ = true; - // create vdec channelDesc - vdecChannelDesc_ = aclvdecCreateChannelDesc(); - if (vdecChannelDesc_ == nullptr) { - MS_LOG(ERROR) << "Failed to create vdec channel description."; - return APP_ERR_ACL_FAILURE; - } - - // channelId: 0-15 - aclError ret = aclvdecSetChannelDescChannelId(vdecChannelDesc_, vdecConfig_.channelId); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to set vdec channel id, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - ret = aclvdecSetChannelDescThreadId(vdecChannelDesc_, vdecConfig_.threadId); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to set thread id, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - // callback func - ret = aclvdecSetChannelDescCallback(vdecChannelDesc_, vdecConfig_.callback); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to set vdec callback function, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - ret = aclvdecSetChannelDescEnType(vdecChannelDesc_, vdecConfig_.inFormat); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to set encoded type of input video, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - ret = aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, vdecConfig_.outFormat); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to set vdec output format, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - // create vdec channel - ret = aclvdecCreateChannel(vdecChannelDesc_); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Failed to create vdec channel, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - MS_LOG(INFO) << "Vdec init resource successfully."; - return APP_ERR_OK; -} - -/* - * @description: If isVdec_ is true, destroy the channel and the channel description used by video. - * Otherwise destroy the channel and the channel description used by image. - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::DeInit() { - if (isVdec_) { - return DestroyResource(); - } - - // Obtain the dvppContext_ allocated by AscendResource which contains the dvppStream_, they mush bind each other - APP_ERROR ret = CALL_ASCEND_API(aclrtSetCurrentContext, dvppContext_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get ACL context, ret = " << ret; - return ret; - } - - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); // APP_ERROR ret - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - return ret; - } - - ret = acldvppDestroyChannel(dvppChannelDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to destroy dvpp channel, ret = " << ret << "."; - return ret; - } - - ret = acldvppDestroyChannelDesc(dvppChannelDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to destroy dvpp channel description, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Destroy the channel and the channel description used by video. - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::DestroyResource() { - APP_ERROR ret = APP_ERR_OK; - isVdec_ = true; - if (vdecChannelDesc_ != nullptr) { - ret = aclvdecDestroyChannel(vdecChannelDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to destroy dvpp channel, ret = " << ret; - } - (void)aclvdecDestroyChannelDesc(vdecChannelDesc_); - vdecChannelDesc_ = nullptr; - } - return ret; -} - -/* - * @description: Release the memory that is allocated in the interfaces which are started with "Combine" - */ -void DvppCommon::ReleaseDvppBuffer() { - if (cropImage_ != nullptr) { - RELEASE_DVPP_DATA(cropImage_->data); - } - if (resizedImage_ != nullptr) { - RELEASE_DVPP_DATA(resizedImage_->data); - } - if (decodedImage_ != nullptr) { - RELEASE_DVPP_DATA(decodedImage_->data); - } - if (inputImage_ != nullptr) { - RELEASE_DVPP_DATA(inputImage_->data); - } - if (encodedImage_ != nullptr) { - RELEASE_DVPP_DATA(encodedImage_->data); - } -} - -/* - * @description: Get the size of buffer used to save image for VPC according to width, height and format - * @param width specifies the width of the output image - * @param height specifies the height of the output image - * @param format specifies the format of the output image - * @param: vpcSize is used to save the result size - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetVpcDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format, uint32_t &vpcSize) { - // Check the invalid format of VPC function and calculate the output buffer size - if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420 && - format != PIXEL_FORMAT_RGB_888) { - MS_LOG(ERROR) << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21 or RGB888."; - return APP_ERR_COMM_INVALID_PARAM; - } - uint32_t widthStride = DVPP_ALIGN_UP(width, VPC_WIDTH_ALIGN); - if (format == PIXEL_FORMAT_RGB_888) { - widthStride *= 3; - } - - uint32_t heightStride = DVPP_ALIGN_UP(height, VPC_HEIGHT_ALIGN); - vpcSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2; - return APP_ERR_OK; -} - -/* - * @description: Get the aligned width and height of the input image according to the image format - * @param: width specifies the width before alignment - * @param: height specifies the height before alignment - * @param: format specifies the image format - * @param: widthStride is used to save the width after alignment - * @param: heightStride is used to save the height after alignment - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetVpcInputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride) { - uint32_t inputWidthStride; - // Check the invalidty of input format and calculate the input width stride - if (format >= PIXEL_FORMAT_YUV_400 && format <= PIXEL_FORMAT_YVU_SEMIPLANAR_444) { - // If format is YUV SP, keep widthStride not change. - inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH); - } else if (format >= PIXEL_FORMAT_YUYV_PACKED_422 && format <= PIXEL_FORMAT_VYUY_PACKED_422) { - // If format is YUV422 packed, image size = H x W * 2; - inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV422_WIDTH_NU; - } else if (format >= PIXEL_FORMAT_YUV_PACKED_444 && format <= PIXEL_FORMAT_BGR_888) { - // If format is YUV444 packed or RGB, image size = H x W * 3; - inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * YUV444_RGB_WIDTH_NU; - } else if (format >= PIXEL_FORMAT_ARGB_8888 && format <= PIXEL_FORMAT_BGRA_8888) { - // If format is XRGB8888, image size = H x W * 4 - inputWidthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH) * XRGB_WIDTH_NU; - } else { - MS_LOG(ERROR) << "Input format[" << format << "] for VPC is invalid, please check it."; - return APP_ERR_COMM_INVALID_PARAM; - } - uint32_t inputHeightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT); - // Check the input validity width stride. - if (inputWidthStride > MAX_RESIZE_WIDTH || inputWidthStride < MIN_RESIZE_WIDTH) { - MS_LOG(ERROR) << "Input width stride " << inputWidthStride << " is invalid, not in [" << MIN_RESIZE_WIDTH << ", " - << MAX_RESIZE_WIDTH << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - // Check the input validity height stride. - if (inputHeightStride > MAX_RESIZE_HEIGHT || inputHeightStride < MIN_RESIZE_HEIGHT) { - MS_LOG(ERROR) << "Input height stride " << inputHeightStride << " is invalid, not in [" << MIN_RESIZE_HEIGHT << ", " - << MAX_RESIZE_HEIGHT << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - widthStride = inputWidthStride; - heightStride = inputHeightStride; - return APP_ERR_OK; -} - -/* - * @description: Get the aligned width and height of the output image according to the image format - * @param: width specifies the width before alignment - * @param: height specifies the height before alignment - * @param: format specifies the image format - * @param: widthStride is used to save the width after alignment - * @param: heightStride is used to save the height after alignment - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetVpcOutputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride) { - // Check the invalidty of output format and calculate the output width and height - if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420 && - format != PIXEL_FORMAT_RGB_888) { - MS_LOG(ERROR) << "Output format[" << format << "] for VPC is not supported, just support NV12 or NV21 or RGB888."; - return APP_ERR_COMM_INVALID_PARAM; - } - - widthStride = DVPP_ALIGN_UP(width, VPC_STRIDE_WIDTH); - if (format == PIXEL_FORMAT_RGB_888) { - widthStride *= 3; - } - - heightStride = DVPP_ALIGN_UP(height, VPC_STRIDE_HEIGHT); - return APP_ERR_OK; -} - -/* - * @description: Set picture description information and execute resize function - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @param: processType specifies whether to perform proportional scaling, default is non-proportional resize - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::VpcResize(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize, - VpcProcessType processType) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "VpcResize cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - acldvppPicDesc *inputDesc = acldvppCreatePicDesc(); - acldvppPicDesc *outputDesc = acldvppCreatePicDesc(); - resizeInputDesc_.reset(inputDesc, g_picDescDeleter); - resizeOutputDesc_.reset(outputDesc, g_picDescDeleter); - - // Set dvpp picture descriptin info of input image - APP_ERROR ret = SetDvppPicDescData(input, *resizeInputDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set dvpp input picture description, ret = " << ret << "."; - return ret; - } - - // Set dvpp picture descriptin info of output image - ret = SetDvppPicDescData(output, *resizeOutputDesc_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set dvpp output picture description, ret = " << ret << "."; - return ret; - } - if (processType == VPC_PT_DEFAULT) { - return ResizeProcess(*resizeInputDesc_, *resizeOutputDesc_, withSynchronize); - } - - // Get crop area according to the processType - // When the processType is VPC_PT_FILL, the image will be cropped if the image size is different from the target - // resolution - CropRoiConfig cropRoi = {0}; - GetCropRoi(input, output, processType, cropRoi); - - // The width and height of the original image will be resized by the same ratio - // The cropped image will be pasted on the upper left corner or the middle location or the whole location according to - // the processType - CropRoiConfig pasteRoi = {0}; - GetPasteRoi(input, output, processType, pasteRoi); - - return ResizeWithPadding(*resizeInputDesc_, *resizeOutputDesc_, cropRoi, pasteRoi, withSynchronize); -} - -/* - * @description: Set image description information - * @param: dataInfo specifies the image information - * @param: picsDesc specifies the picture description information to be set - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::SetDvppPicDescData(const DvppDataInfo &dataInfo, acldvppPicDesc &picDesc) const { - APP_ERROR ret = acldvppSetPicDescData(&picDesc, dataInfo.data); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set data for dvpp picture description, ret = " << ret << "."; - return ret; - } - ret = acldvppSetPicDescSize(&picDesc, dataInfo.dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set size for dvpp picture description, ret = " << ret << "."; - return ret; - } - ret = acldvppSetPicDescFormat(&picDesc, static_cast(dataInfo.format)); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set format for dvpp picture description, ret = " << ret << "."; - return ret; - } - ret = acldvppSetPicDescWidth(&picDesc, dataInfo.width); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set width for dvpp picture description, ret = " << ret << "."; - return ret; - } - ret = acldvppSetPicDescHeight(&picDesc, dataInfo.height); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set height for dvpp picture description, ret = " << ret << "."; - return ret; - } - if (!isVdec_) { - ret = acldvppSetPicDescWidthStride(&picDesc, dataInfo.widthStride); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set aligned width for dvpp picture description, ret = " << ret << "."; - return ret; - } - ret = acldvppSetPicDescHeightStride(&picDesc, dataInfo.heightStride); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set aligned height for dvpp picture description, ret = " << ret << "."; - return ret; - } - } - - return APP_ERR_OK; -} - -/* - * @description: Check whether the image format and zoom ratio meet the requirements - * @param: input specifies the input image information - * @param: output specifies the output image information - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::CheckResizeParams(const DvppDataInfo &input, const DvppDataInfo &output) const { - if (output.format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && output.format != PIXEL_FORMAT_YVU_SEMIPLANAR_420 && - output.format != PIXEL_FORMAT_RGB_888) { - MS_LOG(ERROR) << "Output format[" << output.format << "] for VPC is not supported, only NV12 or NV21 or RGB888."; - return APP_ERR_COMM_INVALID_PARAM; - } - if (static_cast(output.height) / static_cast(input.height) < MIN_RESIZE_SCALE || - static_cast(output.height) / static_cast(input.height) > MAX_RESIZE_SCALE) { - MS_LOG(ERROR) << "Resize scale should be in range [1/16, 16], which is " << (output.height / input.height) << "."; - return APP_ERR_COMM_INVALID_PARAM; - } - if (static_cast(output.width) / static_cast(input.width) < MIN_RESIZE_SCALE || - static_cast(output.width) / static_cast(input.width) > MAX_RESIZE_SCALE) { - MS_LOG(ERROR) << "Resize scale should be in range [1/16, 16], which is " << (output.width / input.width) << "."; - return APP_ERR_COMM_INVALID_PARAM; - } - return APP_ERR_OK; -} - -/* - * @description: Scale the input image to the size specified by the output image and - * saves the result to the output image (non-proportionate scaling) - * @param: inputDesc specifies the description information of the input image - * @param: outputDesc specifies the description information of the output image - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::ResizeProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, bool withSynchronize) { - acldvppResizeConfig *resizeConfig = acldvppCreateResizeConfig(); - if (resizeConfig == nullptr) { - MS_LOG(ERROR) << "Failed to create dvpp resize config."; - return APP_ERR_COMM_INVALID_POINTER; - } - - resizeConfig_.reset(resizeConfig, g_resizeConfigDeleter); - APP_ERROR ret = acldvppVpcResizeAsync(dvppChannelDesc_, &inputDesc, &outputDesc, resizeConfig_.get(), dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to resize asynchronously, ret = " << ret << "."; - return ret; - } - - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - return ret; - } - } - - return APP_ERR_OK; -} - -/* - * @description: Crop the image from the input image based on the specified area and - * paste the cropped image to the specified position of the target image - * as the output image - * @param: inputDesc specifies the description information of the input image - * @param: outputDesc specifies the description information of the output image - * @param: cropRoi specifies the cropped area - * @param: pasteRoi specifies the pasting area - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: If the width and height of the crop area are different from those of the - * paste area, the image is scaled again - */ -APP_ERROR DvppCommon::ResizeWithPadding(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, - const CropRoiConfig &cropRoi, const CropRoiConfig &pasteRoi, - bool withSynchronize) { - acldvppRoiConfig *cropRoiCfg = acldvppCreateRoiConfig(cropRoi.left, cropRoi.right, cropRoi.up, cropRoi.down); - if (cropRoiCfg == nullptr) { - MS_LOG(ERROR) << "Failed to create dvpp roi config for corp area."; - return APP_ERR_COMM_FAILURE; - } - cropAreaConfig_.reset(cropRoiCfg, g_roiConfigDeleter); - - acldvppRoiConfig *pastRoiCfg = acldvppCreateRoiConfig(pasteRoi.left, pasteRoi.right, pasteRoi.up, pasteRoi.down); - if (pastRoiCfg == nullptr) { - MS_LOG(ERROR) << "Failed to create dvpp roi config for paster area."; - return APP_ERR_COMM_FAILURE; - } - pasteAreaConfig_.reset(pastRoiCfg, g_roiConfigDeleter); - - APP_ERROR ret = acldvppVpcCropAndPasteAsync(dvppChannelDesc_, &inputDesc, &outputDesc, cropAreaConfig_.get(), - pasteAreaConfig_.get(), dvppStream_); - if (ret != APP_ERR_OK) { - // release resource. - MS_LOG(ERROR) << "Failed to crop and paste asynchronously, ret = " << ret << "."; - return ret; - } - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed tp synchronize stream, ret = " << ret << "."; - return ret; - } - } - return APP_ERR_OK; -} - -/* - * @description: Get crop area - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: processType specifies whether to perform proportional scaling - * @param: cropRoi is used to save the info of the crop roi area - * @return: APP_ERR_OK if success, other values if failure - */ -void DvppCommon::GetCropRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType, - CropRoiConfig &cropRoi) const { - // When processType is not VPC_PT_FILL, crop area is the whole input image - if (processType != VPC_PT_FILL) { - cropRoi.right = CONVERT_TO_ODD(input.width - ODD_NUM_1); - cropRoi.down = CONVERT_TO_ODD(input.height - ODD_NUM_1); - return; - } - - bool widthRatioSmaller = true; - // The scaling ratio is based on the smaller ratio to ensure the smallest edge to fill the targe edge - float resizeRatio = static_cast(input.width) / static_cast(output.width); - if (resizeRatio > (static_cast(input.height) / static_cast(output.height))) { - resizeRatio = static_cast(input.height) / static_cast(output.height); - widthRatioSmaller = false; - } - - const int halfValue = 2; - // The left and up must be even, right and down must be odd which is required by acl - if (widthRatioSmaller) { - cropRoi.left = 0; - cropRoi.right = CONVERT_TO_ODD(input.width - ODD_NUM_1); - cropRoi.up = CONVERT_TO_EVEN(static_cast((input.height - output.height * resizeRatio) / halfValue)); - cropRoi.down = CONVERT_TO_ODD((input.height - cropRoi.up) - ODD_NUM_1); - return; - } - - cropRoi.up = 0; - cropRoi.down = CONVERT_TO_ODD(input.height - ODD_NUM_1); - cropRoi.left = CONVERT_TO_EVEN(static_cast((input.width - output.width * resizeRatio) / halfValue)); - cropRoi.right = CONVERT_TO_ODD((input.width - cropRoi.left) - ODD_NUM_1); -} - -/* - * @description: Get paste area - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: processType specifies whether to perform proportional scaling - * @param: pasteRio is used to save the info of the paste area - * @return: APP_ERR_OK if success, other values if failure - */ -void DvppCommon::GetPasteRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType, - CropRoiConfig &pasteRoi) const { - if (processType == VPC_PT_FILL) { - pasteRoi.right = CONVERT_TO_ODD(output.width - ODD_NUM_1); - pasteRoi.down = CONVERT_TO_ODD(output.height - ODD_NUM_1); - return; - } - - bool widthRatioLarger = true; - // The scaling ratio is based on the larger ratio to ensure the largest edge to fill the targe edge - float resizeRatio = static_cast(input.width) / static_cast(output.width); - if (resizeRatio < (static_cast(input.height) / static_cast(output.height))) { - resizeRatio = static_cast(input.height) / static_cast(output.height); - widthRatioLarger = false; - } - - // Left and up is 0 when the roi paste on the upper left corner - if (processType == VPC_PT_PADDING) { - pasteRoi.right = - static_cast((static_cast(input.width) / resizeRatio) - static_cast(ODD_NUM_1)); - pasteRoi.down = - static_cast((static_cast(input.height) / resizeRatio) - static_cast(ODD_NUM_1)); - pasteRoi.right = CONVERT_TO_ODD(pasteRoi.right); - pasteRoi.down = CONVERT_TO_ODD(pasteRoi.down); - return; - } - - const int halfValue = 2; - // Left and up is 0 when the roi paste on the middler location - if (widthRatioLarger) { - pasteRoi.left = 0; - pasteRoi.right = output.width - ODD_NUM_1; - pasteRoi.up = - static_cast((static_cast(output.height) - (static_cast(input.height) / resizeRatio)) / - static_cast(halfValue)); - pasteRoi.down = (output.height - pasteRoi.up) - ODD_NUM_1; - } else { - pasteRoi.up = 0; - pasteRoi.down = output.height - ODD_NUM_1; - pasteRoi.left = - static_cast((static_cast(output.width) - (static_cast(input.width) / resizeRatio)) / - static_cast(halfValue)); - pasteRoi.right = (output.width - pasteRoi.left) - ODD_NUM_1; - } - - // The left must be even and align to 16, up must be even, right and down must be odd which is required by acl - pasteRoi.left = DVPP_ALIGN_UP(CONVERT_TO_EVEN(pasteRoi.left), VPC_WIDTH_ALIGN); - pasteRoi.right = CONVERT_TO_ODD(pasteRoi.right); - pasteRoi.up = CONVERT_TO_EVEN(pasteRoi.up); - pasteRoi.down = CONVERT_TO_ODD(pasteRoi.down); -} - -/* - * @description: Resize the image specified by input and save the result to member variable resizedImage_ - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @param: processType specifies whether to perform proportional scaling, default is non-proportional resize - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::CombineResizeProcess(DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize, - VpcProcessType processType) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) - << "CombineResizeProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - APP_ERROR ret = CheckResizeParams(input, output); - if (ret != APP_ERR_OK) { - return ret; - } - // Get widthStride and heightStride for input and output image according to the format - ret = GetVpcInputStrideSize(input.widthStride, input.heightStride, static_cast(input.format), - input.widthStride, input.heightStride); - if (ret != APP_ERR_OK) { - return ret; - } - - resizedImage_ = std::make_shared(); - resizedImage_->width = output.width; - resizedImage_->height = output.height; - resizedImage_->format = output.format; - ret = GetVpcOutputStrideSize(output.width, output.height, static_cast(output.format), - resizedImage_->widthStride, resizedImage_->heightStride); - if (ret != APP_ERR_OK) { - return ret; - } - // Get output buffer size for resize output - ret = GetVpcDataSize(output.width, output.height, static_cast(output.format), - resizedImage_->dataSize); - if (ret != APP_ERR_OK) { - return ret; - } - // Malloc buffer for output of resize module - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(resizedImage_->data)), resizedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc " << resizedImage_->dataSize << " bytes on dvpp for resize, ret = " << ret - << "."; - return ret; - } - - (void)CALL_ASCEND_API(aclrtMemset, resizedImage_->data, resizedImage_->dataSize, YUV_GREYER_VALUE, - resizedImage_->dataSize); - resizedImage_->frameId = input.frameId; - ret = VpcResize(input, *resizedImage_, withSynchronize, processType); - if (ret != APP_ERR_OK) { - // Release the output buffer when resize failed, otherwise release it after use - RELEASE_DVPP_DATA(resizedImage_->data); - } - return ret; -} - -/* - * @description: Set picture description information and execute crop function - * @param: cropInput specifies the input image information and cropping area - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::VpcCrop(const DvppCropInputInfo &cropInput, const DvppDataInfo &output, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "VpcCrop cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - acldvppPicDesc *inputDesc = acldvppCreatePicDesc(); - acldvppPicDesc *outputDesc = acldvppCreatePicDesc(); - cropInputDesc_.reset(inputDesc, g_picDescDeleter); - cropOutputDesc_.reset(outputDesc, g_picDescDeleter); - - // Set dvpp picture descriptin info of input image - APP_ERROR ret = SetDvppPicDescData(cropInput.dataInfo, *cropInputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - // Set dvpp picture descriptin info of output image - ret = SetDvppPicDescData(output, *cropOutputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - return CropProcess(*cropInputDesc_, *cropOutputDesc_, cropInput.roi, withSynchronize); -} - -/* - * @description: Check whether the size of the cropped data and the cropped area meet the requirements - * @param: input specifies the image information and the information about the area to be cropped - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::CheckCropParams(const DvppCropInputInfo &input) const { - APP_ERROR ret; - uint32_t payloadSize; - ret = GetVpcDataSize(input.dataInfo.widthStride, input.dataInfo.heightStride, PIXEL_FORMAT_YUV_SEMIPLANAR_420, - payloadSize); - if (ret != APP_ERR_OK) { - return ret; - } - if (payloadSize != input.dataInfo.dataSize) { - MS_LOG(ERROR) << "Input data size: " << payloadSize - << " to crop does not match input yuv image size: " << input.dataInfo.dataSize << "."; - return APP_ERR_COMM_INVALID_PARAM; - } - - if ((!CHECK_EVEN(input.roi.left)) || (!CHECK_EVEN(input.roi.up)) || (!CHECK_ODD(input.roi.right)) || - (!CHECK_ODD(input.roi.down))) { - MS_LOG(ERROR) << "Crop area left and top(" << input.roi.left << ", " << input.roi.up - << ") must be even, right bottom(" << input.roi.right << "," << input.roi.down << ") must be odd."; - return APP_ERR_COMM_INVALID_PARAM; - } - - // Calculate crop width and height according to the input location - uint32_t cropWidth = (input.roi.right - input.roi.left) + ODD_NUM_1; - uint32_t cropHeight = (input.roi.down - input.roi.up) + ODD_NUM_1; - if ((cropWidth < MIN_CROP_WIDTH) || (cropHeight < MIN_CROP_HEIGHT)) { - MS_LOG(ERROR) << "Crop area width:" << cropWidth << " need to be larger than 10 and height:" << cropHeight - << " need to be larger than 6."; - return APP_ERR_COMM_INVALID_PARAM; - } - - if ((input.roi.left + cropWidth > input.dataInfo.width) || (input.roi.up + cropHeight > input.dataInfo.height)) { - MS_LOG(ERROR) << "Target rectangle start location(" << input.roi.left << "," << input.roi.up << ") with size(" - << cropWidth << "," << cropHeight << ") is out of the input image(" << input.dataInfo.width << "," - << input.dataInfo.height << ") to be cropped."; - return APP_ERR_COMM_INVALID_PARAM; - } - - return APP_ERR_OK; -} - -/* - * @description: It is used to crop an input image based on a specified region and - * store the cropped image to the output memory as an output image - * @param: inputDesc specifies the description information of the input image - * @param: outputDesc specifies the description information of the output image - * @param: CropRoiConfig specifies the cropped area - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: if the region of the output image is inconsistent with the crop area, the image is scaled again - */ -APP_ERROR DvppCommon::CropProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, const CropRoiConfig &cropArea, - bool withSynchronize) { - uint32_t leftOffset = CONVERT_TO_EVEN(cropArea.left); - uint32_t rightOffset = CONVERT_TO_ODD(cropArea.right); - uint32_t upOffset = CONVERT_TO_EVEN(cropArea.up); - uint32_t downOffset = CONVERT_TO_ODD(cropArea.down); - - auto cropRioCfg = acldvppCreateRoiConfig(leftOffset, rightOffset, upOffset, downOffset); - if (cropRioCfg == nullptr) { - MS_LOG(ERROR) << "DvppCommon: create dvpp vpc resize failed."; - return APP_ERR_DVPP_RESIZE_FAIL; - } - cropRoiConfig_.reset(cropRioCfg, g_roiConfigDeleter); - - APP_ERROR ret = acldvppVpcCropAsync(dvppChannelDesc_, &inputDesc, &outputDesc, cropRoiConfig_.get(), dvppStream_); - if (ret != APP_ERR_OK) { - // release resource. - MS_LOG(ERROR) << "Failed to crop, ret = " << ret << "."; - return ret; - } - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - return ret; - } - } - return APP_ERR_OK; -} - -/* - * @description: Crop the image specified by the input parameter and saves the result to member variable cropImage_ - * @param: input specifies the input image information and cropping area - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::CombineCropProcess(DvppCropInputInfo &input, const DvppDataInfo &output, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "CombineCropProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - // Get widthStride and heightStride for input and output image according to the format - APP_ERROR ret = GetVpcInputStrideSize(input.dataInfo.width, input.dataInfo.height, - static_cast(input.dataInfo.format), - input.dataInfo.widthStride, input.dataInfo.heightStride); - if (ret != APP_ERR_OK) { - return ret; - } - ret = CheckCropParams(input); - if (ret != APP_ERR_OK) { - return ret; - } - // cropImage_所持有的成员变量 uint8_t *data通过acldvppMalloc()接口申请,位于Device上 - cropImage_ = std::make_shared(); - cropImage_->width = output.width; - cropImage_->height = output.height; - cropImage_->format = output.format; - ret = GetVpcOutputStrideSize(output.width, output.height, static_cast(output.format), - cropImage_->widthStride, cropImage_->heightStride); - if (ret != APP_ERR_OK) { - return ret; - } - // Get output buffer size for resize output - ret = GetVpcDataSize(output.width, output.height, static_cast(output.format), - cropImage_->dataSize); - if (ret != APP_ERR_OK) { - return ret; - } - - // Malloc buffer for output of resize module - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(cropImage_->data)), cropImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc " << cropImage_->dataSize << " bytes on dvpp for resize, ret = " << ret << "."; - return ret; - } - cropImage_->frameId = input.dataInfo.frameId; - ret = VpcCrop(input, *cropImage_, withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when resize failed, otherwise release it after use - RELEASE_DVPP_DATA(cropImage_->data); - } - return ret; -} - -/* - * @description: Set the description of the output image and decode - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::JpegDecode(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "JpegDecode cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - acldvppPicDesc *outputDesc = acldvppCreatePicDesc(); - decodeOutputDesc_.reset(outputDesc, g_picDescDeleter); - - APP_ERROR ret = SetDvppPicDescData(output, *decodeOutputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - - ret = acldvppJpegDecodeAsync(dvppChannelDesc_, input.data, input.dataSize, decodeOutputDesc_.get(), dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode jpeg, ret = " << ret << "."; - return ret; - } - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - } - - decodedImage_->height = acldvppGetPicDescHeight(decodeOutputDesc_.get()); - decodedImage_->width = acldvppGetPicDescWidth(decodeOutputDesc_.get()); - decodedImage_->format = acldvppGetPicDescFormat(decodeOutputDesc_.get()); - - decodedImage_->heightStride = acldvppGetPicDescHeightStride(decodeOutputDesc_.get()); - decodedImage_->widthStride = acldvppGetPicDescWidthStride(decodeOutputDesc_.get()); - return APP_ERR_OK; -} - -/* - * @description: Set the description of the output image and decode - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::PngDecode(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "PngDecode cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - acldvppPicDesc *outputDesc = acldvppCreatePicDesc(); - decodeOutputDesc_.reset(outputDesc, g_picDescDeleter); - - APP_ERROR ret = SetDvppPicDescData(output, *decodeOutputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - - ret = acldvppPngDecodeAsync(dvppChannelDesc_, input.data, input.dataSize, decodeOutputDesc_.get(), dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode png, ret = " << ret << "."; - return ret; - } - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - } - return APP_ERR_OK; -} - -/* - * @description: Get the aligned width and height of the image after decoding - * @param: width specifies the width before alignment - * @param: height specifies the height before alignment - * @param: widthStride is used to save the width after alignment - * @param: heightStride is used to save the height after alignment - * @return: APP_ERR_OK if success, other values if failure - */ -void DvppCommon::GetJpegDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, - uint32_t &heightStride) { - widthStride = DVPP_ALIGN_UP(width, JPEGD_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(height, JPEGD_STRIDE_HEIGHT); -} - -void DvppCommon::GetPngDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, uint32_t &heightStride, - acldvppPixelFormat format) { - if (format == PIXEL_FORMAT_RGB_888) { - widthStride = DVPP_ALIGN_UP(width * 3, JPEGD_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(height, JPEGD_STRIDE_HEIGHT); - } else { - widthStride = DVPP_ALIGN_UP(width * 4, JPEGD_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(height, JPEGD_STRIDE_HEIGHT); - } -} - -/* - * @description: Get picture width and height and number of channels from image data - * @param: data specifies the memory to store the image data - * @param: dataSize specifies the size of the image data - * @param: width is used to save the image width - * @param: height is used to save the image height - * @param: components is used to save the number of channels - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetJpegImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height, - int32_t &components) { - uint32_t widthTmp; - uint32_t heightTmp; - int32_t componentsTmp; - APP_ERROR ret = acldvppJpegGetImageInfo(data, dataSize, &widthTmp, &heightTmp, &componentsTmp); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get image info of jpeg, ret = " << ret << "."; - return ret; - } - if (widthTmp > MAX_JPEGD_WIDTH || widthTmp < MIN_JPEGD_WIDTH) { - MS_LOG(ERROR) << "Input width is invalid, not in [" << MIN_JPEGD_WIDTH << ", " << MAX_JPEGD_WIDTH << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - if (heightTmp > MAX_JPEGD_HEIGHT || heightTmp < MIN_JPEGD_HEIGHT) { - MS_LOG(ERROR) << "Input height is invalid, not in [" << MIN_JPEGD_HEIGHT << ", " << MAX_JPEGD_HEIGHT << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - width = widthTmp; - height = heightTmp; - components = componentsTmp; - return APP_ERR_OK; -} - -/* - * @description: Get picture width and height and number of channels from PNG image data - * @param: data specifies the memory to store the image data - * @param: dataSize specifies the size of the image data - * @param: width is used to save the image width - * @param: height is used to save the image height - * @param: components is used to save the number of channels - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetPngImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height, - int32_t &components) { - uint32_t widthTmp; - uint32_t heightTmp; - int32_t componentsTmp; - APP_ERROR ret = acldvppPngGetImageInfo(data, dataSize, &widthTmp, &heightTmp, &componentsTmp); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get image info of PNG, ret = " << ret << "."; - return ret; - } - if (widthTmp > MAX_PNGD_WIDTH || widthTmp < MIN_PNGD_WIDTH) { - MS_LOG(ERROR) << "Input width is invalid, not in [" << MIN_PNGD_WIDTH << ", " << MAX_PNGD_WIDTH << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - if (heightTmp > MAX_PNGD_HEIGHT || heightTmp < MIN_PNGD_HEIGHT) { - MS_LOG(ERROR) << "Input height is invalid, not in [" << MIN_PNGD_HEIGHT << ", " << MAX_PNGD_HEIGHT << "]."; - return APP_ERR_COMM_INVALID_PARAM; - } - width = widthTmp; - height = heightTmp; - components = componentsTmp; - return APP_ERR_OK; -} - -/* - * @description: Get the size of the buffer for storing decoded images based on the image data, size, and format - * @param: data specifies the memory to store the image data - * @param: dataSize specifies the size of the image data - * @param: format specifies the image format - * @param: decSize is used to store the result size - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetJpegDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format, - uint32_t &decSize) { - uint32_t outputSize; - APP_ERROR ret = acldvppJpegPredictDecSize(data, dataSize, format, &outputSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to predict decode size of jpeg image, ret = " << ret << "."; - return ret; - } - decSize = outputSize; - return APP_ERR_OK; -} - -/* - * @description: Get the size of the buffer for storing decoded images based on the PNG image data, size, and format - * @param: data specifies the memory to store the image data - * @param: dataSize specifies the size of the image data - * @param: format specifies the image format - * @param: decSize is used to store the result size - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetPngDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format, - uint32_t &decSize) { - uint32_t outputSize; - APP_ERROR ret = acldvppPngPredictDecSize(data, dataSize, format, &outputSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to predict decode size of png image, ret = " << ret << "."; - return ret; - } - decSize = outputSize; - return APP_ERR_OK; -} - -/* - * @description: Decode the image specified by imageInfo and save the result to member variable decodedImage_ - * @param: imageInfo specifies image information - * @param: format specifies the image format - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::CombineJpegdProcess(const RawData &imageInfo, acldvppPixelFormat format, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) - << "CombineJpegdProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - int32_t components; - // Member variable of inputImage_, uint8_t *data will be on device - inputImage_ = std::make_shared(); - inputImage_->format = format; - APP_ERROR ret = GetJpegImageInfo(imageInfo.data, static_cast(imageInfo.lenOfByte), inputImage_->width, - inputImage_->height, components); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get input image info, ret = " << ret << "."; - return ret; - } - - // Get the buffer size(On device) of decode output according to the input data and output format - uint32_t outBuffSize; - ret = GetJpegDecodeDataSize(imageInfo.data, static_cast(imageInfo.lenOfByte), format, outBuffSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get size of decode output buffer, ret = " << ret << "."; - return ret; - } - - // In TransferImageH2D function, device buffer will be allocated to store the input image before decode - // Need to pay attention to release of the buffer - ret = TransferImageH2D(imageInfo, inputImage_); - if (ret != APP_ERR_OK) { - return ret; - } - - decodedImage_ = std::make_shared(); - decodedImage_->format = format; - decodedImage_->width = inputImage_->width; - decodedImage_->height = inputImage_->height; - GetJpegDecodeStrideSize(inputImage_->width, inputImage_->height, decodedImage_->widthStride, - decodedImage_->heightStride); - decodedImage_->dataSize = outBuffSize; - // Malloc dvpp buffer to store the output data after decoding - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(decodedImage_->data)), decodedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory on dvpp, ret = " << ret << "."; - RELEASE_DVPP_DATA(inputImage_->data); - return ret; - } - - ret = JpegDecode(*inputImage_, *decodedImage_, withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when decode failed, otherwise release it after use - RELEASE_DVPP_DATA(inputImage_->data); - inputImage_->data = nullptr; - RELEASE_DVPP_DATA(decodedImage_->data); - decodedImage_->data = nullptr; - return ret; - } - - return APP_ERR_OK; -} - -APP_ERROR DvppCommon::SinkCombineJpegdProcess(const std::shared_ptr &input, - const std::shared_ptr &output, bool withSynchronize) { - // Both input and output are locate on device, so we must release them if fail in decode - APP_ERROR ret = JpegDecode(*input, *output, withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when decode failed, otherwise release it after use - RELEASE_DVPP_DATA(inputImage_->data); - inputImage_->data = nullptr; - RELEASE_DVPP_DATA(decodedImage_->data); - decodedImage_->data = nullptr; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR DvppCommon::SinkCombinePngdProcess(const std::shared_ptr &input, - const std::shared_ptr &output, bool withSynchronize) { - // Both input and output are locate on device, so we must release them if fail in decode - APP_ERROR ret = PngDecode(*input, *output, withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when decode failed, otherwise release it after use - RELEASE_DVPP_DATA(inputImage_->data); - inputImage_->data = nullptr; - RELEASE_DVPP_DATA(decodedImage_->data); - decodedImage_->data = nullptr; - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Decode the image specified by imageInfo and save the result to member variable decodedImage_ - * This function is for PNG format image - * @param: imageInfo specifies image information - * @param: format specifies the image format - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::CombinePngdProcess(const RawData &imageInfo, acldvppPixelFormat format, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "CombinePngdProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - int32_t components; - inputImage_ = std::make_shared(); - inputImage_->format = format; - APP_ERROR ret = GetPngImageInfo(imageInfo.data, static_cast(imageInfo.lenOfByte), inputImage_->width, - inputImage_->height, components); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get input image info, ret = " << ret << "."; - return ret; - } - - // Get the buffer size of decode output according to the input data and output format - uint32_t outBuffSize; - ret = GetPngDecodeDataSize(imageInfo.data, static_cast(imageInfo.lenOfByte), format, outBuffSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get size of decode output buffer, ret = " << ret << "."; - return ret; - } - - // In TransferImageH2D function, device buffer will be allocated to store the input image - // Need to pay attention to release of the buffer - ret = TransferImageH2D(imageInfo, inputImage_); - if (ret != APP_ERR_OK) { - return ret; - } - - decodedImage_ = std::make_shared(); - decodedImage_->format = format; - decodedImage_->width = inputImage_->width; - decodedImage_->height = inputImage_->height; - GetPngDecodeStrideSize(inputImage_->width, inputImage_->height, decodedImage_->widthStride, - decodedImage_->heightStride, format); - decodedImage_->dataSize = outBuffSize; - // Malloc dvpp buffer to store the output data after decoding - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(decodedImage_->data)), decodedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory on dvpp, ret = " << ret << "."; - RELEASE_DVPP_DATA(inputImage_->data); - return ret; - } - ret = PngDecode(*inputImage_, *decodedImage_, withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when decode failed, otherwise release it after use - RELEASE_DVPP_DATA(inputImage_->data); - inputImage_->data = nullptr; - RELEASE_DVPP_DATA(decodedImage_->data); - decodedImage_->data = nullptr; - return ret; - } - - return APP_ERR_OK; -} - -/* - * @description: Transfer data from host to device - * @param: imageInfo specifies the image data on the host - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::TransferYuvDataH2D(const DvppDataInfo &imageinfo) { - if (imageinfo.dataSize == 0) { - MS_LOG(ERROR) << "The input buffer size on host should not be empty."; - return APP_ERR_COMM_INVALID_PARAM; - } - uint8_t *device_ptr = nullptr; - APP_ERROR ret = acldvppMalloc(reinterpret_cast(&device_ptr), imageinfo.dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc " << imageinfo.dataSize << " bytes on dvpp, ret = " << ret << "."; - return ret; - } - ret = CALL_ASCEND_API(aclrtMemcpyAsync, device_ptr, imageinfo.dataSize, imageinfo.data, imageinfo.dataSize, - ACL_MEMCPY_HOST_TO_DEVICE, dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy " << imageinfo.dataSize << " bytes from host to device, ret = " << ret << "."; - RELEASE_DVPP_DATA(device_ptr); - device_ptr = nullptr; - return ret; - } - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - RELEASE_DVPP_DATA(device_ptr); - device_ptr = nullptr; - return ret; - } - /* Important!!! decodedImage_ speifies the image in decoded format(RGB OR YUV) - * Not essentially to be the image after decode.(Specifies the data not in RAW encode format) - * It can also be the image after resize(Very important) - */ - decodedImage_ = std::make_shared(); - decodedImage_->data = device_ptr; - decodedImage_->dataSize = imageinfo.dataSize; - decodedImage_->height = imageinfo.height; - decodedImage_->heightStride = imageinfo.heightStride; - decodedImage_->width = imageinfo.width; - decodedImage_->widthStride = imageinfo.widthStride; - return APP_ERR_OK; -} - -/* - * @description: Transfer data from host to device - * @param: imageInfo specifies the image data on the host - * @param: jpegInput is used to save the buffer and its size which is allocate on the device - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::TransferImageH2D(const RawData &imageInfo, const std::shared_ptr &jpegInput) { - // Check image buffer size validity - if (imageInfo.lenOfByte == 0) { - MS_LOG(ERROR) << "The input buffer size on host should not be empty."; - return APP_ERR_COMM_INVALID_PARAM; - } - - uint8_t *inDevBuff = nullptr; // This pointer will be on device - APP_ERROR ret = acldvppMalloc(reinterpret_cast(&inDevBuff), imageInfo.lenOfByte); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc " << imageInfo.lenOfByte << " bytes on dvpp, ret = " << ret << "."; - return ret; - } - - // Copy the image data from host to device - ret = CALL_ASCEND_API(aclrtMemcpyAsync, inDevBuff, imageInfo.lenOfByte, imageInfo.data, imageInfo.lenOfByte, - ACL_MEMCPY_HOST_TO_DEVICE, dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy " << imageInfo.lenOfByte << " bytes from host to device, ret = " << ret << "."; - RELEASE_DVPP_DATA(inDevBuff); - return ret; - } - // Attention: We must call the aclrtSynchronizeStream to ensure the task of memory replication has been completed - // after calling aclrtMemcpyAsync - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to synchronize stream, ret = " << ret << "."; - RELEASE_DVPP_DATA(inDevBuff); - return ret; - } - jpegInput->data = inDevBuff; - jpegInput->dataSize = static_cast(imageInfo.lenOfByte); - return APP_ERR_OK; -} - -/* - * Sink RawData(On host) into DvppDataInfo(On device) - */ -APP_ERROR DvppCommon::SinkImageH2D(const RawData &imageInfo, acldvppPixelFormat format) { - if (isVdec_) { - MS_LOG(ERROR) - << "CombineJpegdProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - APP_ERROR ret = CALL_ASCEND_API(aclrtSetCurrentContext, dvppContext_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get ACL context, ret = " << ret; - return ret; - } - - int32_t components; - // Member variable of inputImage_, uint8_t *data will be on device - inputImage_ = std::make_shared(); - inputImage_->format = format; - ret = GetJpegImageInfo(imageInfo.data, static_cast(imageInfo.lenOfByte), inputImage_->width, - inputImage_->height, components); - - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get input image info, ret = " << ret << "."; - return ret; - } - - // Get the buffer size(On device) of decode output according to the input data and output format - uint32_t outBufferSize; - - ret = GetJpegDecodeDataSize(imageInfo.data, static_cast(imageInfo.lenOfByte), format, outBufferSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get size of decode output buffer, ret = " << ret << "."; - return ret; - } - // In TransferImageH2D function, device buffer will be allocated to store the input image before decode - // Need to pay attention to release of the buffer - ret = TransferImageH2D(imageInfo, inputImage_); - if (ret != APP_ERR_OK) { - return ret; - } - // This part is to define the data after decode (MALLOC ON DEVICE!!) - decodedImage_ = std::make_shared(); - decodedImage_->format = format; - decodedImage_->width = inputImage_->width; - decodedImage_->height = inputImage_->height; - GetJpegDecodeStrideSize(inputImage_->width, inputImage_->height, decodedImage_->widthStride, - decodedImage_->heightStride); - // Obtain all the attributes of inputImage_ - GetJpegDecodeStrideSize(inputImage_->width, inputImage_->height, inputImage_->widthStride, inputImage_->heightStride); - decodedImage_->dataSize = outBufferSize; - // Malloc dvpp buffer to store the output data after decoding - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(decodedImage_->data)), decodedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory on dvpp, ret = " << ret << "."; - RELEASE_DVPP_DATA(inputImage_->data); - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR DvppCommon::SinkImageH2D(const RawData &imageInfo) { - if (isVdec_) { - MS_LOG(ERROR) << "CombinePngdProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - APP_ERROR ret = CALL_ASCEND_API(aclrtSetCurrentContext, dvppContext_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get ACL context, ret = " << ret; - return ret; - } - - int32_t components; - inputImage_ = std::make_shared(); - acldvppPixelFormat format = PIXEL_FORMAT_RGB_888; - inputImage_->format = format; - - ret = GetPngImageInfo(imageInfo.data, static_cast(imageInfo.lenOfByte), inputImage_->width, - inputImage_->height, components); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get input image info, ret = " << ret << "."; - return ret; - } - - // Get the buffer size of decode output according to the input data and output format - uint32_t outBuffSize; - - ret = GetPngDecodeDataSize(imageInfo.data, static_cast(imageInfo.lenOfByte), format, outBuffSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get size of decode output buffer, ret = " << ret << "."; - return ret; - } - - // In TransferImageH2D function, device buffer will be allocated to store the input image - // Need to pay attention to release of the buffer - ret = TransferImageH2D(imageInfo, inputImage_); - if (ret != APP_ERR_OK) { - return ret; - } - - decodedImage_ = std::make_shared(); - decodedImage_->format = format; - decodedImage_->width = inputImage_->width; - decodedImage_->height = inputImage_->height; - GetPngDecodeStrideSize(inputImage_->width, inputImage_->height, decodedImage_->widthStride, - decodedImage_->heightStride, format); - decodedImage_->dataSize = outBuffSize; - // Malloc dvpp buffer to store the output data after decoding - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(decodedImage_->data)), decodedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory on dvpp, ret = " << ret << "."; - RELEASE_DVPP_DATA(inputImage_->data); - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Create and set the description of a video stream - * @param: data specifies the information about the video stream - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::CreateStreamDesc(const std::shared_ptr &data) { - // Malloc input device memory which need to be released in vdec callback function - void *modelInBuff = nullptr; - APP_ERROR ret = acldvppMalloc(&modelInBuff, data->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc dvpp data with " << data->dataSize << " bytes, ret = " << ret << "."; - return APP_ERR_ACL_BAD_ALLOC; - } - // copy input to device memory - ret = CALL_ASCEND_API(aclrtMemcpy, modelInBuff, data->dataSize, static_cast(data->data), data->dataSize, - ACL_MEMCPY_HOST_TO_DEVICE); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory with " << data->dataSize << " bytes from host to device, ret = " << ret - << "."; - (void)acldvppFree(modelInBuff); - modelInBuff = nullptr; - return APP_ERR_ACL_FAILURE; - } - // Create input stream desc which need to be destroyed in vdec callback function - streamInputDesc_ = acldvppCreateStreamDesc(); - if (streamInputDesc_ == nullptr) { - MS_LOG(ERROR) << "Failed to create input stream description."; - return APP_ERR_ACL_FAILURE; - } - ret = acldvppSetStreamDescData(streamInputDesc_, modelInBuff); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set data for stream desdescription, ret = " << ret << "."; - return ret; - } - // set size for dvpp stream desc - ret = acldvppSetStreamDescSize(streamInputDesc_, data->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set data size for stream desdescription, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Decode the video based on the video stream specified by data and user-defined data, - * and outputs the image of each frame - * @param: data specifies the information about the video stream - * @param: userdata is specified for user-defined data - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with InitVdec - */ -APP_ERROR DvppCommon::CombineVdecProcess(const std::shared_ptr &data, void *userData) { - // Return special error code when the DvppCommon object is not initialized with InitVdec - if (!isVdec_) { - MS_LOG(ERROR) - << "CombineVdecProcess cannot be called by the DvppCommon object which is not initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - // create stream desc - APP_ERROR ret = CreateStreamDesc(data); - if (ret != APP_ERR_OK) { - return ret; - } - - uint32_t dataSize; - ret = GetVideoDecodeDataSize(vdecConfig_.inputWidth, vdecConfig_.inputHeight, vdecConfig_.outFormat, dataSize); - if (ret != APP_ERR_OK) { - return ret; - } - - void *picOutBufferDev = nullptr; - // picOutBufferDev need to be destroyed in vdec callback function - ret = acldvppMalloc(&picOutBufferDev, dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory with " << dataSize << " bytes, ret = " << ret << "."; - return APP_ERR_ACL_BAD_ALLOC; - } - - // picOutputDesc_ will be destroyed in vdec callback function - picOutputDesc_ = acldvppCreatePicDesc(); - if (picOutputDesc_ == nullptr) { - return APP_ERR_ACL_BAD_ALLOC; - } - - DvppDataInfo dataInfo; - dataInfo.width = static_cast(vdecConfig_.inputWidth); - dataInfo.height = static_cast(vdecConfig_.inputHeight); - dataInfo.format = vdecConfig_.outFormat; - dataInfo.dataSize = dataSize; - dataInfo.data = static_cast(picOutBufferDev); - ret = SetDvppPicDescData(dataInfo, *picOutputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - - // send frame - ret = aclvdecSendFrame(vdecChannelDesc_, streamInputDesc_, picOutputDesc_, nullptr, userData); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to send frame, ret = " << ret << "."; - return APP_ERR_ACL_FAILURE; - } - - return APP_ERR_OK; -} - -/* - * @description: Send eos frame when video stream ends - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::VdecSendEosFrame() const { - // create input stream desc - acldvppStreamDesc *eosStreamDesc = acldvppCreateStreamDesc(); - if (eosStreamDesc == nullptr) { - MS_LOG(ERROR) << "Fail to create dvpp stream desc for eos."; - return ACL_ERROR_FAILURE; - } - - // set eos for eos stream desc - APP_ERROR ret = acldvppSetStreamDescEos(eosStreamDesc, 1); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Fail to set eos for stream desc, ret = " << ret << "."; - (void)acldvppDestroyStreamDesc(eosStreamDesc); - return ret; - } - - // send eos and synchronize - ret = aclvdecSendFrame(vdecChannelDesc_, eosStreamDesc, nullptr, nullptr, nullptr); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Fail to send eos, ret = " << ret << "."; - (void)acldvppDestroyStreamDesc(eosStreamDesc); - return ret; - } - - // destroy input stream desc - ret = acldvppDestroyStreamDesc(eosStreamDesc); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Fail to destroy dvpp stream desc for eos, ret = " << ret << "."; - return ret; - } - return ret; -} - -/* - * @description: Get the aligned width and height of the output image after video decoding - * @param: width specifies the width before alignment - * @param: height specifies the height before alignment - * @param: format specifies the format of the output image - * @param: widthStride is used to save the width after alignment - * @param: heightStride is used to save the height after alignment - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetVideoDecodeStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride) { - // Check the invalidty of output format and calculate the output width and height - if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420) { - MS_LOG(ERROR) << "Input format[" << format << "] for VPC is not supported, just support NV12 or NV21."; - return APP_ERR_COMM_INVALID_PARAM; - } - widthStride = DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT); - return APP_ERR_OK; -} - -/* - * @description: Get the buffer size for storing results after video decoding - * @param width specifies the width of the output image after video decoding - * @param height specifies the height of the output image after video decoding - * @param format specifies the format of the output image - * @param: vpcSize is used to save the result size - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetVideoDecodeDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &vdecSize) { - // Check the invalid format of vdec output and calculate the output buffer size - if (format != PIXEL_FORMAT_YUV_SEMIPLANAR_420 && format != PIXEL_FORMAT_YVU_SEMIPLANAR_420) { - MS_LOG(ERROR) << "Format[" << format << "] for VPC is not supported, just support NV12 or NV21."; - return APP_ERR_COMM_INVALID_PARAM; - } - uint32_t widthStride = DVPP_ALIGN_UP(width, VDEC_STRIDE_WIDTH); - uint32_t heightStride = DVPP_ALIGN_UP(height, VDEC_STRIDE_HEIGHT); - vdecSize = widthStride * heightStride * YUV_BGR_SIZE_CONVERT_3 / YUV_BGR_SIZE_CONVERT_2; - return APP_ERR_OK; -} - -/* - * @description: Encode a YUV image into a JPG image - * @param: input specifies the input image information - * @param: output specifies the output image information - * @param: jpegeConfig specifies the encoding configuration data - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::JpegEncode(const DvppDataInfo &input, DvppDataInfo &output, acldvppJpegeConfig *jpegeConfig, - bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) << "JpegEncode cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - APP_ERROR ret = SetDvppPicDescData(input, *encodeInputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - - ret = acldvppJpegEncodeAsync(dvppChannelDesc_, encodeInputDesc_.get(), output.data, &output.dataSize, jpegeConfig, - dvppStream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to encode image, ret = " << ret << "."; - return ret; - } - if (withSynchronize) { - ret = CALL_ASCEND_API(aclrtSynchronizeStreamWithTimeout, dvppStream_, kTimeLimit); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to aclrtSynchronizeStream, ret = " << ret << "."; - return APP_ERR_DVPP_JPEG_ENCODE_FAIL; - } - } - MS_LOG(INFO) << "Encode successfully."; - return APP_ERR_OK; -} - -/* - * @description: Get the aligned width, height, and data size of the input image - * @param: inputImage specifies the input image information - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::GetJpegEncodeStrideSize(const std::shared_ptr &inputImage) { - uint32_t inputWidth = inputImage->width; - uint32_t inputHeight = inputImage->height; - auto format = static_cast(inputImage->format); - uint32_t widthStride; - uint32_t heightStride; - uint32_t encodedBufferSize; - // Align up the input width and height and calculate buffer size of encoded input file - if (format == PIXEL_FORMAT_YUV_SEMIPLANAR_420 || format == PIXEL_FORMAT_YVU_SEMIPLANAR_420) { - widthStride = DVPP_ALIGN_UP(inputWidth, JPEGE_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT); - encodedBufferSize = widthStride * heightStride * YUV_BYTES_NU / YUV_BYTES_DE; - } else if (format == PIXEL_FORMAT_YUYV_PACKED_422 || format == PIXEL_FORMAT_UYVY_PACKED_422 || - format == PIXEL_FORMAT_YVYU_PACKED_422 || format == PIXEL_FORMAT_VYUY_PACKED_422) { - widthStride = DVPP_ALIGN_UP(inputWidth * YUV422_WIDTH_NU, JPEGE_STRIDE_WIDTH); - heightStride = DVPP_ALIGN_UP(inputHeight, JPEGE_STRIDE_HEIGHT); - encodedBufferSize = widthStride * heightStride; - } else { - return APP_ERR_COMM_INVALID_PARAM; - } - if (encodedBufferSize == 0) { - MS_LOG(ERROR) << "Input host buffer size is empty."; - return APP_ERR_COMM_INVALID_PARAM; - } - inputImage->widthStride = widthStride; - inputImage->heightStride = heightStride; - inputImage->dataSize = encodedBufferSize; - return APP_ERR_OK; -} - -/* - * @description: Estimate the size of the output memory required by image encoding according to - * the input image description and image encoding configuration data - * @param: input specifies specifies the input image information - * @param: jpegeConfig specifies the encoding configuration data - * @param: encSize is used to save the result size - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::GetJpegEncodeDataSize(const DvppDataInfo &input, const acldvppJpegeConfig *jpegeConfig, - uint32_t &encSize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) - << "GetJpegEncodeDataSize cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - - acldvppPicDesc *inputDesc = acldvppCreatePicDesc(); - encodeInputDesc_.reset(inputDesc, g_picDescDeleter); - - APP_ERROR ret = SetDvppPicDescData(input, *encodeInputDesc_); - if (ret != APP_ERR_OK) { - return ret; - } - - uint32_t outputSize; - ret = acldvppJpegPredictEncSize(encodeInputDesc_.get(), jpegeConfig, &outputSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to predict encode size of jpeg image, ret = " << ret << "."; - return ret; - } - encSize = outputSize; - return APP_ERR_OK; -} - -/* - * @description: Set the encoding configuration data - * @param: level specifies the encode quality range - * @param: jpegeConfig specifies the encoding configuration data - * @return: APP_ERR_OK if success, other values if failure - */ -APP_ERROR DvppCommon::SetEncodeLevel(uint32_t level, acldvppJpegeConfig &jpegeConfig) { - // Set the encoding quality - // The coding quality range [0, 100] - // The level 0 coding quality is similar to the level 100 - // The smaller the value in [1, 100], the worse the quality of the output picture - auto ret = static_cast(acldvppSetJpegeConfigLevel(&jpegeConfig, level)); - if (ret != APP_ERR_OK) { - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Encode the image specified by imageInfo and save the result to member variable encodedImage_ - * @param: imageInfo specifies image information - * @param: width specifies the width of the input image - * @param: height specifies the height of the input image - * @param: format specifies the format of the input image - * @param: withSynchronize specifies whether to execute synchronously - * @return: APP_ERR_OK if success, other values if failure - * @attention: This function can be called only when the DvppCommon object is initialized with Init - */ -APP_ERROR DvppCommon::CombineJpegeProcess(const RawData &imageInfo, uint32_t width, uint32_t height, - acldvppPixelFormat format, bool withSynchronize) { - // Return special error code when the DvppCommon object is initialized with InitVdec - if (isVdec_) { - MS_LOG(ERROR) - << "CombineJpegeProcess cannot be called by the DvppCommon object which is initialized with InitVdec."; - return APP_ERR_DVPP_OBJ_FUNC_MISMATCH; - } - inputImage_ = std::make_shared(); - inputImage_->format = format; - inputImage_->width = width; - inputImage_->height = height; - // In TransferImageH2D function, device buffer will be allocated to store the input image - // Need to pay attention to release of the buffer - APP_ERROR ret = TransferImageH2D(imageInfo, inputImage_); - if (ret != APP_ERR_OK) { - return ret; - } - // Get stride size of encoded image - ret = GetJpegEncodeStrideSize(inputImage_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get encode stride size of input image file, ret = " << ret << "."; - return ret; - } - - auto jpegeConfig = acldvppCreateJpegeConfig(); - jpegeConfig_.reset(jpegeConfig, g_jpegeConfigDeleter); - - uint32_t encodeLevel = 100; - ret = SetEncodeLevel(encodeLevel, *jpegeConfig_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to set encode level, ret = " << ret << "."; - return ret; - } - - // Get the buffer size of encode output according to the input data and jpeg encode config - uint32_t encodeOutBufferSize; - ret = GetJpegEncodeDataSize(*inputImage_, jpegeConfig_.get(), encodeOutBufferSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get size of encode output buffer, ret = " << ret << "."; - return ret; - } - - encodedImage_ = std::make_shared(); - encodedImage_->dataSize = encodeOutBufferSize; - // Malloc dvpp buffer to store the output data after decoding - // Need to pay attention to release of the buffer - ret = acldvppMalloc(reinterpret_cast(&(encodedImage_->data)), encodedImage_->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to malloc memory on dvpp, ret = " << ret << "."; - (void)acldvppFree(inputImage_->data); - return ret; - } - - // Encode input image - ret = JpegEncode(*inputImage_, *encodedImage_, jpegeConfig_.get(), withSynchronize); - if (ret != APP_ERR_OK) { - // Release the output buffer when decode failed, otherwise release it after use - (void)acldvppFree(inputImage_->data); - (void)acldvppFree(encodedImage_->data); - return ret; - } - return APP_ERR_OK; -} - -std::shared_ptr DvppCommon::GetInputImage() { return inputImage_; } - -std::shared_ptr DvppCommon::GetDecodedImage() { return decodedImage_; } - -std::shared_ptr DvppCommon::GetResizedImage() { return resizedImage_; } - -std::shared_ptr DvppCommon::GetEncodedImage() { return encodedImage_; } - -std::shared_ptr DvppCommon::GetCropedImage() { return cropImage_; } - -DvppCommon::~DvppCommon() = default; diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h deleted file mode 100644 index e8c99e29b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h +++ /dev/null @@ -1,249 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_COMMON_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_COMMON_H_ - -#include -#include - -#include "acl/ops/acl_dvpp.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h" - -const int MODULUS_NUM_2 = 2; -const uint32_t ODD_NUM_1 = 1; - -struct DvppBaseData { - uint32_t dataSize; // Size of data in byte - uint8_t *data; -}; - -struct VdecConfig { - int inputWidth = 0; - int inputHeight = 0; - acldvppStreamFormat inFormat = H264_MAIN_LEVEL; // stream format renference acldvppStreamFormat - acldvppPixelFormat outFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420; // output format renference acldvppPixelFormat - uint32_t channelId = 0; // user define channelId: 0-15 - uint32_t deviceId = 0; - pthread_t threadId = 0; // thread for callback - aclvdecCallback callback = {nullptr}; // user define how to process vdec out data - bool runflag = true; -}; - -struct DeviceStreamData { - std::vector detectResult; - uint32_t framId; - uint32_t channelId; -}; - -const uint32_t JPEGD_STRIDE_WIDTH = 128; // Jpegd module output width need to align up to 128 -const uint32_t JPEGD_STRIDE_HEIGHT = 16; // Jpegd module output height need to align up to 16 -const uint32_t PNGD_STRIDE_WIDTH = 128; // Pngd module output width need to align up to 128 -const uint32_t PNGD_STRIDE_HEIGHT = 16; // Pngd module output height need to align up to 16 -const uint32_t JPEGE_STRIDE_WIDTH = 16; // Jpege module input width need to align up to 16 -const uint32_t JPEGE_STRIDE_HEIGHT = 1; // Jpege module input height remains unchanged -const uint32_t VPC_STRIDE_WIDTH = 16; // Vpc module output width need to align up to 16 -const uint32_t VPC_STRIDE_HEIGHT = 2; // Vpc module output height need to align up to 2 -const uint32_t VDEC_STRIDE_WIDTH = 16; // Vdec module output width need to align up to 16 -const uint32_t VDEC_STRIDE_HEIGHT = 2; // Vdec module output width need to align up to 2 -const uint32_t YUV_BYTES_NU = 3; // Numerator of yuv image, H x W x 3 / 2 -const uint32_t YUV_BYTES_DE = 2; // Denominator of yuv image, H x W x 3 / 2 -const uint32_t YUV422_WIDTH_NU = 2; // Width of YUV422, WidthStride = Width * 2 -const uint32_t YUV444_RGB_WIDTH_NU = 3; // Width of YUV444 and RGB888, WidthStride = Width * 3 -const uint32_t XRGB_WIDTH_NU = 4; // Width of XRGB8888, WidthStride = Width * 4 -const uint32_t JPEG_OFFSET = 8; // Offset of input file for jpegd module -const uint32_t MAX_JPEGD_WIDTH = 8192; // Max width of jpegd module -const uint32_t MAX_JPEGD_HEIGHT = 8192; // Max height of jpegd module -const uint32_t MIN_JPEGD_WIDTH = 32; // Min width of jpegd module -const uint32_t MIN_JPEGD_HEIGHT = 32; // Min height of jpegd module -const uint32_t MAX_PNGD_WIDTH = 4096; // Max width of pngd module -const uint32_t MAX_PNGD_HEIGHT = 4096; // Max height of pngd module -const uint32_t MIN_PNGD_WIDTH = 32; // Min width of pngd module -const uint32_t MIN_PNGD_HEIGHT = 32; // Min height of pngd module -const uint32_t MAX_JPEGE_WIDTH = 8192; // Max width of jpege module -const uint32_t MAX_JPEGE_HEIGHT = 8192; // Max height of jpege module -const uint32_t MIN_JPEGE_WIDTH = 32; // Min width of jpege module -const uint32_t MIN_JPEGE_HEIGHT = 32; // Min height of jpege module -const uint32_t MAX_RESIZE_WIDTH = 8192; // Max width stride of resize module -const uint32_t MAX_RESIZE_HEIGHT = 8192; // Max height stride of resize module -const uint32_t MIN_RESIZE_WIDTH = 32; // Min width stride of resize module -const uint32_t MIN_RESIZE_HEIGHT = 6; // Min height stride of resize module -const float MIN_RESIZE_SCALE = 0.03125; // Min resize scale of resize module -const float MAX_RESIZE_SCALE = 16.0; // Min resize scale of resize module -const uint32_t MAX_VPC_WIDTH = 4096; // Max width of picture to VPC(resize/crop) -const uint32_t MAX_VPC_HEIGHT = 4096; // Max height of picture to VPC(resize/crop) -const uint32_t MIN_VPC_WIDTH = 32; // Min width of picture to VPC(resize/crop) -const uint32_t MIN_VPC_HEIGHT = 6; // Min height of picture to VPC(resize/crop) -const uint32_t MIN_CROP_WIDTH = 10; // Min width of crop area -const uint32_t MIN_CROP_HEIGHT = 6; // Min height of crop area -const uint8_t YUV_GREYER_VALUE = 128; // Filling value of the resized YUV image - -#define CONVERT_TO_ODD(NUM) (((NUM) % MODULUS_NUM_2 != 0) ? (NUM) : ((NUM)-1)) // Convert the input to odd num -#define CONVERT_TO_EVEN(NUM) (((NUM) % MODULUS_NUM_2 == 0) ? (NUM) : ((NUM)-1)) // Convert the input to even num -#define CHECK_ODD(num) ((num) % MODULUS_NUM_2 != 0) -#define CHECK_EVEN(num) ((num) % MODULUS_NUM_2 == 0) -#define RELEASE_DVPP_DATA(dvppDataPtr) \ - do { \ - APP_ERROR retMacro; \ - if (dvppDataPtr != nullptr) { \ - retMacro = acldvppFree(dvppDataPtr); \ - if (retMacro != APP_ERR_OK) { \ - MS_LOG(ERROR) << "Failed to free memory on dvpp, ret = " << retMacro << "."; \ - } \ - dvppDataPtr = nullptr; \ - } \ - } while (0) - -class DvppCommon { - public: - explicit DvppCommon(aclrtContext dvppContext, aclrtStream dvppStream); - explicit DvppCommon(const VdecConfig &vdecConfig); // Need by vdec - ~DvppCommon(); - APP_ERROR Init(); - APP_ERROR InitVdec(); // Needed by vdec - APP_ERROR DeInit(); - - static APP_ERROR GetVpcDataSize(uint32_t widthVpc, uint32_t heightVpc, acldvppPixelFormat format, uint32_t &vpcSize); - static APP_ERROR GetVpcInputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride); - static APP_ERROR GetVpcOutputStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride); - static void GetJpegDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, uint32_t &heightStride); - static void GetPngDecodeStrideSize(uint32_t width, uint32_t height, uint32_t &widthStride, uint32_t &heightStride, - acldvppPixelFormat format); - static APP_ERROR GetJpegImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height, - int32_t &components); - static APP_ERROR GetPngImageInfo(const void *data, uint32_t dataSize, uint32_t &width, uint32_t &height, - int32_t &components); - static APP_ERROR GetJpegDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format, - uint32_t &decSize); - static APP_ERROR GetPngDecodeDataSize(const void *data, uint32_t dataSize, acldvppPixelFormat format, - uint32_t &decSize); - static APP_ERROR GetJpegEncodeStrideSize(const std::shared_ptr &inputImage); - static APP_ERROR SetEncodeLevel(uint32_t level, acldvppJpegeConfig &jpegeConfig); - static APP_ERROR GetVideoDecodeStrideSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &widthStride, uint32_t &heightStride); - static APP_ERROR GetVideoDecodeDataSize(uint32_t width, uint32_t height, acldvppPixelFormat format, - uint32_t &vdecSize); - - // The following interfaces can be called only when the DvppCommon object is initialized with Init - APP_ERROR VpcResize(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize, - VpcProcessType processType = VPC_PT_DEFAULT); - APP_ERROR VpcCrop(const DvppCropInputInfo &cropInput, const DvppDataInfo &output, bool withSynchronize); - APP_ERROR JpegDecode(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize); - - APP_ERROR PngDecode(const DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize); - - APP_ERROR JpegEncode(const DvppDataInfo &input, DvppDataInfo &output, acldvppJpegeConfig *jpegeConfig, - bool withSynchronize); - - APP_ERROR GetJpegEncodeDataSize(const DvppDataInfo &input, const acldvppJpegeConfig *jpegeConfig, uint32_t &encSize); - - // These functions started with "Combine" encapsulate the DVPP process together, malloc DVPP memory, - // transfer pictures from host to device, and then execute the DVPP operation. - // The caller needs to pay attention to the release of the memory allocated in these functions. - // You can call the ReleaseDvppBuffer function to release memory after use completely. - APP_ERROR CombineResizeProcess(DvppDataInfo &input, const DvppDataInfo &output, bool withSynchronize, - VpcProcessType processType = VPC_PT_DEFAULT); - APP_ERROR CombineCropProcess(DvppCropInputInfo &input, const DvppDataInfo &output, bool withSynchronize); - APP_ERROR CombineJpegdProcess(const RawData &imageInfo, acldvppPixelFormat format, bool withSynchronize); - - APP_ERROR SinkCombineJpegdProcess(const std::shared_ptr &input, - const std::shared_ptr &output, bool withSynchronize); - APP_ERROR SinkCombinePngdProcess(const std::shared_ptr &input, - const std::shared_ptr &output, bool withSynchronize); - - APP_ERROR CombineJpegeProcess(const RawData &imageInfo, uint32_t width, uint32_t height, acldvppPixelFormat format, - bool withSynchronize); - // New feature for PNG format image decode - APP_ERROR CombinePngdProcess(const RawData &imageInfo, acldvppPixelFormat format, bool withSynchronize); - - // The following interface can be called only when the DvppCommon object is initialized with InitVdec - APP_ERROR CombineVdecProcess(const std::shared_ptr &data, void *userData); - - // Get the private member variables which are assigned in the interfaces which are started with "Combine" - std::shared_ptr GetInputImage(); - std::shared_ptr GetDecodedImage(); - std::shared_ptr GetResizedImage(); - std::shared_ptr GetEncodedImage(); - std::shared_ptr GetCropedImage(); - - // Transfer DvppDataInfo from host to device, aim at resize and crop - APP_ERROR TransferYuvDataH2D(const DvppDataInfo &imageinfo); - // Transfer RawData(image) from host to device, aim at decode - APP_ERROR TransferImageH2D(const RawData &imageInfo, const std::shared_ptr &jpegInput); - // Transfer RawData(image on host) to inputImage_(data on device), this is for data sink mode - APP_ERROR SinkImageH2D(const RawData &imageInfo, acldvppPixelFormat format); - // This overload function is for PNG image sink, hence we ignore the pixel format - APP_ERROR SinkImageH2D(const RawData &imageInfo); - - // Release the memory that is allocated in the interfaces which are started with "Combine" - void ReleaseDvppBuffer(); - APP_ERROR VdecSendEosFrame() const; - - private: - APP_ERROR SetDvppPicDescData(const DvppDataInfo &dataInfo, acldvppPicDesc &picDesc) const; - APP_ERROR ResizeProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, bool withSynchronize); - APP_ERROR ResizeWithPadding(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, const CropRoiConfig &cropRoi, - const CropRoiConfig &pasteRoi, bool withSynchronize); - void GetCropRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType, - CropRoiConfig &cropRoi) const; - void GetPasteRoi(const DvppDataInfo &input, const DvppDataInfo &output, VpcProcessType processType, - CropRoiConfig &pasteRoi) const; - APP_ERROR CropProcess(acldvppPicDesc &inputDesc, acldvppPicDesc &outputDesc, const CropRoiConfig &cropArea, - bool withSynchronize); - APP_ERROR CheckResizeParams(const DvppDataInfo &input, const DvppDataInfo &output) const; - APP_ERROR CheckCropParams(const DvppCropInputInfo &input) const; - APP_ERROR CreateStreamDesc(const std::shared_ptr &data); - APP_ERROR DestroyResource(); - - std::shared_ptr cropAreaConfig_ = nullptr; - std::shared_ptr pasteAreaConfig_ = nullptr; - - std::shared_ptr cropInputDesc_ = nullptr; - std::shared_ptr cropOutputDesc_ = nullptr; - std::shared_ptr cropRoiConfig_ = nullptr; - - std::shared_ptr encodeInputDesc_ = nullptr; - std::shared_ptr jpegeConfig_ = nullptr; - - std::shared_ptr resizeInputDesc_ = nullptr; - std::shared_ptr resizeOutputDesc_ = nullptr; - std::shared_ptr resizeConfig_ = nullptr; - - std::shared_ptr decodeOutputDesc_ = nullptr; - - acldvppChannelDesc *dvppChannelDesc_ = nullptr; - - // Ascend resource core: (context, stream) is a pair so must bind with each other to make all function perform well - aclrtContext dvppContext_ = nullptr; - aclrtStream dvppStream_ = nullptr; - - std::shared_ptr inputImage_ = nullptr; - std::shared_ptr decodedImage_ = nullptr; - std::shared_ptr encodedImage_ = nullptr; - std::shared_ptr resizedImage_ = nullptr; - std::shared_ptr cropImage_ = nullptr; - bool isVdec_ = false; - aclvdecChannelDesc *vdecChannelDesc_ = nullptr; - acldvppStreamDesc *streamInputDesc_ = nullptr; - acldvppPicDesc *picOutputDesc_ = nullptr; - VdecConfig vdecConfig_; -}; -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_COMMON_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.cpp b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.cpp deleted file mode 100644 index b1ec29055..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -std::string GetAppErrCodeInfo(const APP_ERROR err) { - if ((err < APP_ERR_ACL_END) && (err >= APP_ERR_ACL_FAILURE)) { - return APP_ERR_ACL_LOG_STRING[((err < 0) ? (err + APP_ERR_ACL_END + 1) : err)]; - } else if ((err < APP_ERR_COMM_END) && (err > APP_ERR_COMM_BASE)) { - return (err - APP_ERR_COMM_BASE) < static_cast(sizeof(APP_ERR_COMMON_LOG_STRING)) / - static_cast(sizeof(APP_ERR_COMMON_LOG_STRING[0])) - ? APP_ERR_COMMON_LOG_STRING[err - APP_ERR_COMM_BASE] - : "Undefine the error code information"; - } else if ((err < APP_ERR_DVPP_END) && (err > APP_ERR_DVPP_BASE)) { - return (err - APP_ERR_DVPP_BASE) < - static_cast(sizeof(APP_ERR_DVPP_LOG_STRING)) / static_cast(sizeof(APP_ERR_DVPP_LOG_STRING[0])) - ? APP_ERR_DVPP_LOG_STRING[err - APP_ERR_DVPP_BASE] - : "Undefine the error code information"; - } else if ((err < APP_ERR_QUEUE_END) && (err > APP_ERR_QUEUE_BASE)) { - return (err - APP_ERR_QUEUE_BASE) < static_cast(sizeof(APP_ERR_QUEUE_LOG_STRING)) / - static_cast(sizeof(APP_ERR_QUEUE_LOG_STRING[0])) - ? APP_ERR_QUEUE_LOG_STRING[err - APP_ERR_QUEUE_BASE] - : "Undefine the error code information"; - } else { - return "Error code unknown"; - } -} - -void AssertErrorCode(int code, const std::string &file, const std::string &function, int line) { - if (code != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed at " << file << "->" << function << "->" << line << ": error code=" << code; - } -} - -void CheckErrorCode(int code, const std::string &file, const std::string &function, int line) { - if (code != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed at " << file << "->" << function << "->" << line << ": error code=" << code; - } -} diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h deleted file mode 100644 index 3e3460abf..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ERROR_CODE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ERROR_CODE_H_ - -#include - -using APP_ERROR = int; -// define the data tpye of error code -enum { - APP_ERR_OK = 0, - - // define the error code of ACL model, this is same with the aclError which is - // error code of ACL API Error codes 1~999 are reserved for the ACL. Do not - // add other error codes. Add it after APP_ERR_COMMON_ERR_BASE. - APP_ERR_ACL_FAILURE = -1, // ACL: general error - APP_ERR_ACL_INVALID_PARAM = 1, // ACL: invalid parameter - APP_ERR_ACL_BAD_ALLOC = 2, // ACL: memory allocation fail - APP_ERR_ACL_RT_FAILURE = 3, // ACL: runtime failure - APP_ERR_ACL_GE_FAILURE = 4, // ACL: Graph Engine failure - APP_ERR_ACL_OP_NOT_FOUND = 5, // ACL: operator not found - APP_ERR_ACL_OP_LOAD_FAILED = 6, // ACL: fail to load operator - APP_ERR_ACL_READ_MODEL_FAILURE = 7, // ACL: fail to read model - APP_ERR_ACL_PARSE_MODEL = 8, // ACL: parse model failure - APP_ERR_ACL_MODEL_MISSING_ATTR = 9, // ACL: model missing attribute - APP_ERR_ACL_DESERIALIZE_MODEL = 10, // ACL: deserialize model failure - APP_ERR_ACL_EVENT_NOT_READY = 12, // ACL: event not ready - APP_ERR_ACL_EVENT_COMPLETE = 13, // ACL: event complete - APP_ERR_ACL_UNSUPPORTED_DATA_TYPE = 14, // ACL: unsupported data type - APP_ERR_ACL_REPEAT_INITIALIZE = 15, // ACL: repeat initialize - APP_ERR_ACL_COMPILER_NOT_REGISTERED = 16, // ACL: compiler not registered - APP_ERR_ACL_IO = 17, // ACL: IO failed - APP_ERR_ACL_INVALID_FILE = 18, // ACL: invalid file - APP_ERR_ACL_INVALID_DUMP_CONFIG = 19, // ACL: invalid dump comfig - APP_ERR_ACL_INVALID_PROFILING_CONFIG = 20, // ACL: invalid profiling config - APP_ERR_ACL_OP_TYPE_NOT_MATCH = 21, // ACL: operator type not match - APP_ERR_ACL_OP_INPUT_NOT_MATCH = 22, // ACL: operator input not match - APP_ERR_ACL_OP_OUTPUT_NOT_MATCH = 23, // ACL: operator output not match - APP_ERR_ACL_OP_ATTR_NOT_MATCH = 24, // ACL: operator attribute not match - APP_ERR_ACL_API_NOT_SUPPORT = 25, // ACL: API not support - APP_ERR_ACL_CREATE_DATA_BUF_FAILED = 26, // ACL: create data buffer fail - APP_ERR_ACL_END, // Not an error code, define the range of ACL error code - - // define the common error code, range: 1001~1999 - APP_ERR_COMM_BASE = 1000, - APP_ERR_COMM_FAILURE = APP_ERR_COMM_BASE + 1, // General Failed - APP_ERR_COMM_INNER = APP_ERR_COMM_BASE + 2, // Internal error - APP_ERR_COMM_INVALID_POINTER = APP_ERR_COMM_BASE + 3, // Invalid Pointer - APP_ERR_COMM_INVALID_PARAM = APP_ERR_COMM_BASE + 4, // Invalid parameter - APP_ERR_COMM_UNREALIZED = APP_ERR_COMM_BASE + 5, // Not implemented - APP_ERR_COMM_OUT_OF_MEM = APP_ERR_COMM_BASE + 6, // Out of memory - APP_ERR_COMM_ALLOC_MEM = APP_ERR_COMM_BASE + 7, // memory allocation error - APP_ERR_COMM_FREE_MEM = APP_ERR_COMM_BASE + 8, // free memory error - APP_ERR_COMM_OUT_OF_RANGE = APP_ERR_COMM_BASE + 9, // out of range - APP_ERR_COMM_NO_PERMISSION = APP_ERR_COMM_BASE + 10, // NO Permission - APP_ERR_COMM_TIMEOUT = APP_ERR_COMM_BASE + 11, // Timed out - APP_ERR_COMM_NOT_INIT = APP_ERR_COMM_BASE + 12, // Not initialized - APP_ERR_COMM_INIT_FAIL = APP_ERR_COMM_BASE + 13, // initialize failed - APP_ERR_COMM_INPROGRESS = APP_ERR_COMM_BASE + 14, // Operation now in progress - APP_ERR_COMM_EXIST = APP_ERR_COMM_BASE + 15, // Object, file or other resource already exist - APP_ERR_COMM_NO_EXIST = APP_ERR_COMM_BASE + 16, // Object, file or other resource doesn't exist - APP_ERR_COMM_BUSY = APP_ERR_COMM_BASE + 17, // Object, file or other resource is in use - APP_ERR_COMM_FULL = APP_ERR_COMM_BASE + 18, // No available Device or resource - APP_ERR_COMM_OPEN_FAIL = APP_ERR_COMM_BASE + 19, // Device, file or resource open failed - APP_ERR_COMM_READ_FAIL = APP_ERR_COMM_BASE + 20, // Device, file or resource read failed - APP_ERR_COMM_WRITE_FAIL = APP_ERR_COMM_BASE + 21, // Device, file or resource write failed - APP_ERR_COMM_DESTORY_FAIL = APP_ERR_COMM_BASE + 22, // Device, file or resource destroy failed - APP_ERR_COMM_EXIT = APP_ERR_COMM_BASE + 23, // End of data stream, stop the application - APP_ERR_COMM_CONNECTION_CLOSE = APP_ERR_COMM_BASE + 24, // Out of connection, Communication shutdown - APP_ERR_COMM_CONNECTION_FAILURE = APP_ERR_COMM_BASE + 25, // connection fail - APP_ERR_COMM_STREAM_INVALID = APP_ERR_COMM_BASE + 26, // ACL stream is null pointer - APP_ERR_COMM_END, // Not an error code, define the range of common error code - - // define the error code of DVPP - APP_ERR_DVPP_BASE = 2000, - APP_ERR_DVPP_CROP_FAIL = APP_ERR_DVPP_BASE + 1, // DVPP: crop fail - APP_ERR_DVPP_RESIZE_FAIL = APP_ERR_DVPP_BASE + 2, // DVPP: resize fail - APP_ERR_DVPP_CROP_RESIZE_FAIL = APP_ERR_DVPP_BASE + 3, // DVPP: corp and resize fail - APP_ERR_DVPP_CONVERT_FROMAT_FAIL = APP_ERR_DVPP_BASE + 4, // DVPP: convert image format fail - APP_ERR_DVPP_VPC_FAIL = APP_ERR_DVPP_BASE + 5, // DVPP: VPC(crop, resize, convert format) fail - APP_ERR_DVPP_JPEG_DECODE_FAIL = APP_ERR_DVPP_BASE + 6, // DVPP: decode jpeg or jpg fail - APP_ERR_DVPP_JPEG_ENCODE_FAIL = APP_ERR_DVPP_BASE + 7, // DVPP: encode jpeg or jpg fail - APP_ERR_DVPP_PNG_DECODE_FAIL = APP_ERR_DVPP_BASE + 8, // DVPP: encode png fail - APP_ERR_DVPP_H26X_DECODE_FAIL = APP_ERR_DVPP_BASE + 9, // DVPP: decode H264 or H265 fail - APP_ERR_DVPP_H26X_ENCODE_FAIL = APP_ERR_DVPP_BASE + 10, // DVPP: encode H264 or H265 fail - APP_ERR_DVPP_HANDLE_NULL = APP_ERR_DVPP_BASE + 11, // DVPP: acldvppChannelDesc is nullptr - APP_ERR_DVPP_PICDESC_FAIL = APP_ERR_DVPP_BASE + 12, // DVPP: fail to create acldvppCreatePicDesc or - // fail to set acldvppCreatePicDesc - APP_ERR_DVPP_CONFIG_FAIL = APP_ERR_DVPP_BASE + 13, // DVPP: fail to set dvpp configuration,such as - // resize configuration,crop configuration - APP_ERR_DVPP_OBJ_FUNC_MISMATCH = APP_ERR_DVPP_BASE + 14, // DVPP: DvppCommon object mismatch the function - APP_ERR_DVPP_NORMALIZE_FAIL = APP_ERR_DVPP_BASE + 15, // DVPP: normalize fail - APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL = APP_ERR_DVPP_BASE + 16, // DVPP: adjust brightness fail - APP_ERR_DVPP_ADJUST_CONTRAST_FAIL = APP_ERR_DVPP_BASE + 17, // DVPP: adjust contrast fail - APP_ERR_DVPP_ADJUST_HUE_FAIL = APP_ERR_DVPP_BASE + 18, // DVPP: adjust hue fail - APP_ERR_DVPP_ADJUST_SATURATION_FAIL = APP_ERR_DVPP_BASE + 19, // DVPP: adjust saturation fail - APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL = APP_ERR_DVPP_BASE + 20, // DVPP: Horizontal Flip - APP_ERR_DVPP_VERTICAL_FLIP_FAIL = APP_ERR_DVPP_BASE + 21, // DVPP: vertical Flip - APP_ERR_DVPP_PERSPECTIVE_FAIL = APP_ERR_DVPP_BASE + 22, // DVPP: perspective fail - APP_ERR_DVPP_RESIZED_CROP_FAIL = APP_ERR_DVPP_BASE + 23, // DVPP: crop and resize fail - APP_ERR_DVPP_PAD_FAIL = APP_ERR_DVPP_BASE + 24, // DVPP: pad fail - APP_ERR_DVPP_AFFINE_FAIL = APP_ERR_DVPP_BASE + 25, // DVPP: affine fail - APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL = APP_ERR_DVPP_BASE + 26, // DVPP: gaussian blur fail - APP_ERR_DVPP_EQUALIZE_FAIL = APP_ERR_DVPP_BASE + 27, // DVPP: equalize blur fail - APP_ERR_DVPP_ROTATE_FAIL = APP_ERR_DVPP_BASE + 28, // DVPP: rotate fail - APP_ERR_DVPP_AUTO_CONTRAST_FAIL = APP_ERR_DVPP_BASE + 29, // DVPP: auto contrast fail - APP_ERR_DVPP_POSTERIZE_FAIL = APP_ERR_DVPP_BASE + 30, // DVPP: posterize fail - APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL = APP_ERR_DVPP_BASE + 31, // DVPP: adjust sharpness fail - APP_ERR_DVPP_INVERT_FAIL = APP_ERR_DVPP_BASE + 32, // DVPP: invert fail - APP_ERR_DVPP_SOLARIZE_FAIL = APP_ERR_DVPP_BASE + 33, // DVPP: solarize fail - APP_ERR_DVPP_CONVERT_COLOR_FAIL = APP_ERR_DVPP_BASE + 34, // DVPP: convert color fail - APP_ERR_DVPP_ERASE_FAIL = APP_ERR_DVPP_BASE + 35, // DVPP: erase fail - APP_ERR_DVPP_END, // Not an error code, define the range of common error code - - // define the error code of inference - APP_ERR_INFER_BASE = 3000, - APP_ERR_INFER_SET_INPUT_FAIL = APP_ERR_INFER_BASE + 1, // Infer: set input fail - APP_ERR_INFER_SET_OUTPUT_FAIL = APP_ERR_INFER_BASE + 2, // Infer: set output fail - APP_ERR_INFER_CREATE_OUTPUT_FAIL = APP_ERR_INFER_BASE + 3, // Infer: create output fail - APP_ERR_INFER_OP_SET_ATTR_FAIL = APP_ERR_INFER_BASE + 4, // Infer: set op attribute fail - APP_ERR_INFER_GET_OUTPUT_FAIL = APP_ERR_INFER_BASE + 5, // Infer: get model output fail - APP_ERR_INFER_FIND_MODEL_ID_FAIL = APP_ERR_INFER_BASE + 6, // Infer: find model id fail - APP_ERR_INFER_FIND_MODEL_DESC_FAIL = APP_ERR_INFER_BASE + 7, // Infer: find model description fail - APP_ERR_INFER_FIND_MODEL_MEM_FAIL = APP_ERR_INFER_BASE + 8, // Infer: find model memory fail - APP_ERR_INFER_FIND_MODEL_WEIGHT_FAIL = APP_ERR_INFER_BASE + 9, // Infer: find model weight fail - - APP_ERR_INFER_END, // Not an error code, define the range of inference error - // code - - // define the error code of transmission - APP_ERR_TRANS_BASE = 4000, - - APP_ERR_TRANS_END, // Not an error code, define the range of transmission - // error code - - // define the error code of blocking queue - APP_ERR_QUEUE_BASE = 5000, - APP_ERR_QUEUE_EMPTY = APP_ERR_QUEUE_BASE + 1, // Queue: empty queue - APP_ERR_QUEUE_STOPED = APP_ERR_QUEUE_BASE + 2, // Queue: queue stopped - APP_ERROR_QUEUE_FULL = APP_ERR_QUEUE_BASE + 3, // Queue: full queue - - // define the error code of destroy - APP_ERR_DESTORY_BASE = 6000, - APP_ERR_DESTORY_TENSOR = APP_ERR_DESTORY_BASE + 1, - APP_ERR_DESTORY_SCALAR = APP_ERR_DESTORY_BASE + 2, - APP_ERR_DESTORY_INT_ARRAY = APP_ERR_DESTORY_BASE + 3, - APP_ERR_DESTORY_FLOAT_ARRAY = APP_ERR_DESTORY_BASE + 4, - APP_ERR_DESTORY_BOOL_ARRAY = APP_ERR_DESTORY_BASE + 5, - APP_ERR_DESTORY_TENSOR_LIST = APP_ERR_DESTORY_BASE + 6, - APP_ERR_DESTORY_SCALAR_LIST = APP_ERR_DESTORY_BASE + 7, - - // define the idrecognition web error code - APP_ERROR_FACE_WEB_USE_BASE = 10000, - APP_ERROR_FACE_WEB_USE_SYSTEM_ERROR = APP_ERROR_FACE_WEB_USE_BASE + 1, // Web: system error - APP_ERROR_FACE_WEB_USE_MUL_FACE = APP_ERROR_FACE_WEB_USE_BASE + 2, // Web: multiple cheeks - APP_ERROR_FACE_WEB_USE_REPEAT_REG = APP_ERROR_FACE_WEB_USE_BASE + 3, // Web: repeat registration - APP_ERROR_FACE_WEB_USE_PART_SUCCESS = APP_ERROR_FACE_WEB_USE_BASE + 4, // Web: partial search succeeded - APP_ERROR_FACE_WEB_USE_NO_FACE = APP_ERROR_FACE_WEB_USE_BASE + 5, // Web: no cheek detected - APP_ERR_QUEUE_END, // Not an error code, define the range of blocking queue - // error code -}; - -const std::string APP_ERR_ACL_LOG_STRING[] = { - "Success", // APP_ERR_OK - "ACL: invalid parameter", // APP_ERR_ACL_INVALID_PARAM - "ACL: memory allocation fail", // APP_ERR_ACL_BAD_ALLOC - "ACL: runtime failure", // APP_ERR_ACL_RT_FAILURE - "ACL: Graph Engine failure", // APP_ERR_ACL_GE_FAILURE - "ACL: operator not found", // APP_ERR_ACL_OP_NOT_FOUND - "ACL: fail to load operator", // APP_ERR_ACL_OP_LOAD_FAILED - "ACL: fail to read model", // APP_ERR_ACL_READ_MODEL_FAILURE - "ACL: parse model failure", // APP_ERR_ACL_PARSE_MODEL - "ACL: model missing attribute", // APP_ERR_ACL_MODEL_MISSING_ATTR - "ACL: deserialize model failure", // APP_ERR_ACL_DESERIALIZE_MODEL - "Placeholder", // 11 - "ACL: event not ready", // APP_ERR_ACL_EVENT_NOT_READY - "ACL: event complete", // APP_ERR_ACL_EVENT_COMPLETE - "ACL: unsupported data type", // APP_ERR_ACL_UNSUPPORTED_DATA_TYPE - "ACL: repeat initialize", // APP_ERR_ACL_REPEAT_INITIALIZE - "ACL: compiler not registered", // APP_ERR_ACL_COMPILER_NOT_REGISTERED - "ACL: IO failed", // APP_ERR_ACL_IO - "ACL: invalid file", // APP_ERR_ACL_INVALID_FILE - "ACL: invalid dump comfig", // APP_ERR_ACL_INVALID_DUMP_CONFIG - "ACL: invalid profiling config", // APP_ERR_ACL_INVALID_PROFILING_CONFIG - "ACL: operator type not match", // APP_ERR_ACL_OP_TYPE_NOT_MATCH - "ACL: operator input not match", // APP_ERR_ACL_OP_INPUT_NOT_MATCH - "ACL: operator output not match", // APP_ERR_ACL_OP_OUTPUT_NOT_MATCH - "ACL: operator attribute not match", // APP_ERR_ACL_OP_ATTR_NOT_MATCH - "ACL: API not supported", // APP_ERR_ACL_API_NOT_SUPPORT - "ACL: create data buffer fail", // APP_ERR_ACL_CREATE_DATA_BUF_FAILED -}; - -const std::string APP_ERR_COMMON_LOG_STRING[] = { - "Placeholder", // 0 - "General Failed", - "Internal error", - "Invalid Pointer", // 3 - "Invalid parameter", - "Not implemented", // 5 - "Out of memory", - "memory allocation error", - "free memory error", // 8 - "out of range", - "NO Permission ", // 10 - "Timed out", - "Not initialized", - "initialize failed", // 13 - "Operation now in progress ", - "Object, file or other resource already exist", // 15 - "Object, file or other resource already doesn't exist", - "Object, file or other resource is in use", - "No available Device or resource", // 18 - "Device, file or resource open failed", - "Device, file or resource read failed", // 20 - "Device, file or resource write failed", - "Device, file or resource destroy failed", // 22 - " ", - "Out of connection, Communication shutdown", // 24 - "connection fail", - "ACL stream is null pointer", // 26 -}; - -const std::string APP_ERR_DVPP_LOG_STRING[] = { - "Placeholder", // 0 - "DVPP: crop fail", - "DVPP: resize fail", - "DVPP: corp and resize fail", - "DVPP: convert image format fail", - "DVPP: VPC(crop, resize, convert format) fail", // 5 - "DVPP: decode jpeg or jpg fail", - "DVPP: encode jpeg or jpg fail", - "DVPP: encode png fail", - "DVPP: decode H264 or H265 fail", - "DVPP: encode H264 or H265 fail", // 10 - "DVPP: acldvppChannelDesc is nullptr", - "DVPP: fail to create or set acldvppCreatePicDesc", - "DVPP: fail to set dvpp configuration", - "DVPP: DvppCommon object mismatch the function", // 14 -}; - -const std::string APP_ERR_INFER_LOG_STRING[] = { - "Placeholder", // 0 - "Infer: set input fail", - "Infer: set output fail", - "Infer: create output fail", - "Infer: set op attribute fail", - "Infer: get model output fail", // 5 - "Infer: find model id fail", - "Infer: find model description fail", - "Infer: find model memory fail", - "Infer: find model weight fail", // 9 -}; - -const std::string APP_ERR_QUEUE_LOG_STRING[] = { - "Placeholder", - "empty queue", - "queue stopped", - "full queue", -}; - -const std::string APP_ERR_FACE_LOG_STRING[] = { - "Placeholder", // 0 - "system error", // 1 - "multiple faces", // 2 - "repeat registration", // 3 - "partial search succeeded", // 4 - "no face detected", // 5 -}; - -std::string GetAppErrCodeInfo(APP_ERROR err); -void AssertErrorCode(int code, const std::string &file, const std::string &function, int line); -void CheckErrorCode(int code, const std::string &file, const std::string &function, int line); - -#define RtAssert(code) AssertErrorCode(code, DATASET_SRC_FILE_NAME, __FUNCTION__, __LINE__) -#define RtCheckError(code) CheckErrorCode(code, DATASET_SRC_FILE_NAME, __FUNCTION__, __LINE__) - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ERROR_CODE_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.cc deleted file mode 100644 index 7534ac55c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.cc +++ /dev/null @@ -1,1052 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h" - -#include - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -namespace { -const int BUFFER_SIZE = 2048; -const mode_t DEFAULT_FILE_PERMISSION = 0077U; -} // namespace - -mode_t SetFileDefaultUmask() { return umask(DEFAULT_FILE_PERMISSION); } - -/* - * @description: get progress runtime - * @return: runtime which is vector - */ -std::vector RunTimeUtil::GetRunTime() { - auto sec_duration = std::chrono::duration_cast(this->end - this->start); - int64_t sec = sec_duration.count(); - - auto us_duration = std::chrono::duration_cast(this->end - this->start); - int64_t us = us_duration.count(); - auto cost_ms = static_cast(SEC2MS * static_cast(sec) + static_cast(us) / SEC2MS); - auto fps = static_cast(1.0 * SEC2MS / cost_ms); - std::vector run_time = {cost_ms, fps}; - return run_time; -} - -/* - * @description: Constructor - * @param: resizeWidth specifies the resized width - * @param: resizeHeight specifies the resized hegiht - * @param: stream is used to maintain the execution order of operations - * @param: context is used to manage the life cycle of objects - * @param: dvppCommon is a class for decoding and resizing - */ -MDAclProcess::MDAclProcess(uint32_t resizeWidth, uint32_t resizeHeight, uint32_t cropWidth, uint32_t cropHeight, - aclrtContext context, bool is_crop, aclrtStream stream, - const std::shared_ptr &dvppCommon) - : resizeWidth_(resizeWidth), - resizeHeight_(resizeHeight), - cropWidth_(cropWidth), - cropHeight_(cropHeight), - context_(context), - stream_(stream), - contain_crop_(is_crop), - dvppCommon_(dvppCommon), - processedInfo_(nullptr) {} - -MDAclProcess::MDAclProcess(uint32_t ParaWidth, uint32_t ParaHeight, aclrtContext context, bool is_crop, - aclrtStream stream, const std::shared_ptr &dvppCommon) - : contain_crop_(is_crop), context_(context), stream_(stream), dvppCommon_(dvppCommon), processedInfo_(nullptr) { - if (is_crop) { - resizeWidth_ = 0; - resizeHeight_ = 0; - cropWidth_ = ParaWidth; - cropHeight_ = ParaHeight; - } else { - resizeWidth_ = ParaWidth; - resizeHeight_ = ParaHeight; - cropWidth_ = 0; - cropHeight_ = 0; - } -} - -MDAclProcess::MDAclProcess(aclrtContext context, bool is_crop, aclrtStream stream, - const std::shared_ptr &dvppCommon) - : resizeWidth_(0), - resizeHeight_(0), - cropWidth_(0), - cropHeight_(0), - contain_crop_(is_crop), - context_(context), - stream_(stream), - dvppCommon_(dvppCommon), - processedInfo_(nullptr) {} -/* - * @description: Release MDAclProcess resources - * @return: aclError which is error code of ACL API - */ -APP_ERROR MDAclProcess::Release() { - // Release objects resource - APP_ERROR ret = dvppCommon_->DeInit(); - dvppCommon_->ReleaseDvppBuffer(); - - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to deinitialize dvppCommon_, ret = " << ret; - return ret; - } - MS_LOG(INFO) << "dvppCommon_ object deinitialized successfully"; - dvppCommon_.reset(); - - // Release stream - if (stream_ != nullptr) { - ret = CALL_ASCEND_API(aclrtDestroyStream, stream_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to destroy stream, ret = " << ret; - stream_ = nullptr; - return ret; - } - stream_ = nullptr; - } - MS_LOG(INFO) << "The stream is destroyed successfully"; - return APP_ERR_OK; -} - -/* - * @description: Initialize DvppCommon object - * @return: aclError which is error code of ACL API - */ -APP_ERROR MDAclProcess::InitModule() { - // Create Dvpp JpegD object - dvppCommon_ = std::make_shared(context_, stream_); - if (dvppCommon_ == nullptr) { - MS_LOG(ERROR) << "Failed to create dvppCommon_ object"; - return APP_ERR_COMM_INIT_FAIL; - } - MS_LOG(INFO) << "DvppCommon object created successfully"; - APP_ERROR ret = dvppCommon_->Init(); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to initialize dvppCommon_ object, ret = " << ret; - return ret; - } - MS_LOG(INFO) << "DvppCommon object initialized successfully"; - return APP_ERR_OK; -} - -/* - * @description: Initialize MDAclProcess resources - * @return: aclError which is error code of ACL API - */ -APP_ERROR MDAclProcess::InitResource() { - APP_ERROR ret = CALL_ASCEND_API(aclrtSetCurrentContext, context_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get ACL context, ret = " << ret; - return ret; - } - MS_LOG(INFO) << "The context is created successfully"; - ret = aclrtCreateStream(&stream_); // Create stream for application - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to create ACL stream, ret = " << ret; - return ret; - } - MS_LOG(INFO) << "The stream is created successfully"; - // Initialize dvpp module - if (InitModule() != APP_ERR_OK) { - return APP_ERR_COMM_INIT_FAIL; - } - return APP_ERR_OK; -} - -std::shared_ptr MDAclProcess::GetDeviceModule() { return dvppCommon_; } - -aclrtContext MDAclProcess::GetContext() { return context_; } - -aclrtStream MDAclProcess::GetStream() { return stream_; } - -/* - * Sink data from Tensor(On host) to DeviceTensor(On device) - * Two cases are different, jpeg and png - */ -APP_ERROR MDAclProcess::H2D_Sink(const std::shared_ptr &input, - std::shared_ptr &device_input) { - // Recall the context created in InitResource() - APP_ERROR ret = CALL_ASCEND_API(aclrtSetCurrentContext, context_); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to get ACL context, ret = " << ret; - return ret; - } - - RawData imageinfo{}; - uint32_t filesize = input->SizeInBytes(); - - imageinfo.lenOfByte = filesize; - auto *buffer = const_cast(input->GetBuffer()); - imageinfo.data = static_cast(buffer); - - // Transfer RawData(Raw image) from host to device, which we call sink - if (IsNonEmptyJPEG(input)) { // case JPEG - ret = dvppCommon_->SinkImageH2D(imageinfo, PIXEL_FORMAT_YUV_SEMIPLANAR_420); - } else { // case PNG - ret = dvppCommon_->SinkImageH2D(imageinfo); - } - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to transport Tensor to device, ret = " << ret; - return ret; - } - auto deviceInputData = dvppCommon_->GetInputImage(); - - const mindspore::dataset::DataType dvpp_data_type(mindspore::dataset::DataType::DE_UINT8); - const mindspore::dataset::TensorShape dvpp_shape({1, 1, 1}); - auto rc = mindspore::dataset::DeviceTensor::CreateEmpty(dvpp_shape, dvpp_data_type, &device_input); - if (rc.IsError()) { - MS_LOG(ERROR) << "Failed to allocate memory, error msg is " << rc; - return APP_ERR_ACL_BAD_ALLOC; - } - rc = - device_input->SetAttributes(deviceInputData->data, deviceInputData->dataSize, deviceInputData->width, - deviceInputData->widthStride, deviceInputData->height, deviceInputData->heightStride); - if (rc.IsError()) { - MS_LOG(ERROR) << "Failed to initialize device attribution, error msg is " << rc; - return APP_ERR_ACL_INVALID_PARAM; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::D2H_Pop(const std::shared_ptr &device_output, - std::shared_ptr &output) { - void *resHostBuf = nullptr; - APP_ERROR ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, device_output->DeviceDataSize()); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), device_output->DeviceDataSize(), device_output->GetDeviceBuffer(), - device_output->DeviceDataSize(), ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - auto data = std::static_pointer_cast(processedInfo_); - unsigned char *ret_ptr = data.get(); - - mindspore::dataset::dsize_t dvppDataSize = device_output->DeviceDataSize(); - const mindspore::dataset::TensorShape dvpp_shape({dvppDataSize, 1, 1}); - uint32_t _output_width_ = device_output->GetYuvStrideShape()[0]; - uint32_t _output_widthStride_ = device_output->GetYuvStrideShape()[1]; - uint32_t _output_height_ = device_output->GetYuvStrideShape()[2]; - uint32_t _output_heightStride_ = device_output->GetYuvStrideShape()[3]; - const mindspore::dataset::DataType dvpp_data_type(mindspore::dataset::DataType::DE_UINT8); - auto rc = mindspore::dataset::Tensor::CreateFromMemory(dvpp_shape, dvpp_data_type, ret_ptr, &output); - if (rc.IsError()) { - MS_LOG(ERROR) << "Failed to allocate memory, error msg is " << rc; - return APP_ERR_ACL_BAD_ALLOC; - } - rc = output->SetYuvShape(_output_width_, _output_widthStride_, _output_height_, _output_heightStride_); - if (rc.IsError()) { - MS_LOG(ERROR) << "Failed to set yuv shape, error msg is " << rc; - return APP_ERR_ACL_INVALID_PARAM; - } - if (!output->HasData()) { - return APP_ERR_COMM_ALLOC_MEM; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_D(const RawData &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_D_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp decode Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr DecodeOutData = dvppCommon_->GetDecodedImage(); - if (!DecodeOutData) { - MS_LOG(ERROR) << "Decode Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, DecodeOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), DecodeOutData->dataSize, DecodeOutData->data, - DecodeOutData->dataSize, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_D() { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_D_(); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp decode Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr DecodeOutData = dvppCommon_->GetDecodedImage(); - if (!DecodeOutData) { - MS_LOG(ERROR) << "Decode Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_D_(const RawData &ImageInfo) { - APP_ERROR ret = dvppCommon_->CombineJpegdProcess(ImageInfo, PIXEL_FORMAT_YUV_SEMIPLANAR_420, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_D_() { - auto input_ = dvppCommon_->GetInputImage(); - auto decode_output_ = dvppCommon_->GetDecodedImage(); - APP_ERROR ret = dvppCommon_->SinkCombineJpegdProcess(input_, decode_output_, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_R(const DvppDataInfo &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_R_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to resize, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp resize Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr ResizeOutData = dvppCommon_->GetResizedImage(); - if (!ResizeOutData) { - MS_LOG(ERROR) << "Resize Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, ResizeOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), ResizeOutData->dataSize, ResizeOutData->data, - ResizeOutData->dataSize, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_R(const std::string &last_step) { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_R_(last_step); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to resize, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp resize Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr ResizeOutData = dvppCommon_->GetResizedImage(); - if (!ResizeOutData) { - MS_LOG(ERROR) << "Resize Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_R_(const DvppDataInfo &ImageInfo) { - APP_ERROR ret = dvppCommon_->TransferYuvDataH2D(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy data from host to device, ret = " << ret << "."; - return ret; - } - std::shared_ptr decoded_image = dvppCommon_->GetDecodedImage(); - uint32_t pri_h = decoded_image->heightStride; - uint32_t pri_w = decoded_image->widthStride; - // Define the resize shape - DvppDataInfo resizeOut; - ret = ResizeConfigFilter(resizeOut, pri_w, pri_h); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to config resize parameter, ret = " << ret << "."; - return ret; - } - ret = dvppCommon_->CombineResizeProcess(*decoded_image, resizeOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process resize, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_R_(const std::string &last_step) { - std::shared_ptr input_image; - if (last_step == "Decode") { - input_image = dvppCommon_->GetDecodedImage(); - } else { - input_image = dvppCommon_->GetCropedImage(); - } - if (!input_image->data) { - MS_LOG(ERROR) << "Failed to get data for resize, please verify last step operation"; - return APP_ERR_DVPP_RESIZE_FAIL; - } - uint32_t pri_h = input_image->heightStride; - uint32_t pri_w = input_image->widthStride; - DvppDataInfo resizeOut; - auto ret = ResizeConfigFilter(resizeOut, pri_w, pri_h); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to config resize, ret = " << ret << "."; - return ret; - } - ret = dvppCommon_->CombineResizeProcess(*input_image, resizeOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process resize, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_C(const DvppDataInfo &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_C_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to crop image, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp crop Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr CropOutData = dvppCommon_->GetCropedImage(); - if (!CropOutData) { - MS_LOG(ERROR) << "Crop Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, CropOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), CropOutData->dataSize, CropOutData->data, CropOutData->dataSize, - ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_C(const std::string &last_step) { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_C_(last_step); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to crop image, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp crop Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr CropOutData = dvppCommon_->GetCropedImage(); - if (!CropOutData) { - MS_LOG(ERROR) << "Crop Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_C_(const DvppDataInfo &ImageInfo) { - APP_ERROR ret = dvppCommon_->TransferYuvDataH2D(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy data from host to device, ret = " << ret << "."; - return ret; - } - // Unnecessary to be image after resize, maybe after decode, we store both of them in DecodedImage() - std::shared_ptr resized_image = dvppCommon_->GetDecodedImage(); - uint32_t pri_h = resized_image->heightStride; - uint32_t pri_w = resized_image->widthStride; - // Validate the crop shape - DvppDataInfo cropOut; - cropOut.width = cropWidth_; - cropOut.height = cropHeight_; - if (cropOut.width > pri_w || cropOut.height > pri_h) { - MS_LOG(ERROR) << "Crop size can not excceed resize, please verify your input [CROP SIZE] parameters"; - return APP_ERR_COMM_INVALID_PARAM; - } - cropOut.format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; - DvppCropInputInfo cropInfo; - cropInfo.dataInfo = *resized_image; - // Define crop area - CropRoiConfig cropCfg{}; - CropConfigFilter(cropCfg, cropInfo, *resized_image); - ret = dvppCommon_->CombineCropProcess(cropInfo, cropOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process center crop, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_C_(const std::string &last_step) { - std::shared_ptr input_image; - if (last_step == "Resize") { - input_image = dvppCommon_->GetResizedImage(); - } else { - input_image = dvppCommon_->GetDecodedImage(); - } - if (!input_image->data) { - MS_LOG(ERROR) << "Failed to get input data for crop, please verify last step operation"; - return APP_ERR_DVPP_CROP_FAIL; - } - uint32_t pri_h = input_image->heightStride; - uint32_t pri_w = input_image->widthStride; - DvppDataInfo cropOut; - cropOut.width = cropWidth_; - cropOut.height = cropHeight_; - if (cropOut.width > pri_w || cropOut.height > pri_h) { - MS_LOG(ERROR) << "Crop size can not excceed resize, please verify your input [CROP SIZE] parameters"; - return APP_ERR_COMM_INVALID_PARAM; - } - cropOut.format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; - DvppCropInputInfo cropInfo; - cropInfo.dataInfo = *input_image; - // Define crop area - CropRoiConfig cropCfg{}; - CropConfigFilter(cropCfg, cropInfo, *input_image); - APP_ERROR ret = dvppCommon_->CombineCropProcess(cropInfo, cropOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process center crop, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::PNG_D(const RawData &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = PNG_D_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr DecodeOutData = dvppCommon_->GetDecodedImage(); - if (!DecodeOutData) { - MS_LOG(ERROR) << "ResizedOutData returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, DecodeOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), DecodeOutData->dataSize, DecodeOutData->data, - DecodeOutData->dataSize, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::PNG_D() { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = PNG_D_(); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp decode Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr DecodeOutData = dvppCommon_->GetDecodedImage(); - if (!DecodeOutData) { - MS_LOG(ERROR) << "Decode Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::PNG_D_(const RawData &ImageInfo) { - APP_ERROR ret = dvppCommon_->CombinePngdProcess(ImageInfo, PIXEL_FORMAT_RGB_888, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::PNG_D_() { - auto input_ = dvppCommon_->GetInputImage(); - auto decode_output_ = dvppCommon_->GetDecodedImage(); - APP_ERROR ret = dvppCommon_->SinkCombinePngdProcess(input_, decode_output_, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -/* - * @description: Decode and scale the picture, and write the result to a file - * @param: imageFile specifies the image path to be processed - * @return: aclError which is error code of ACL API - */ -APP_ERROR MDAclProcess::JPEG_DRC(const RawData &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_DRC_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode or resize or crop, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr CropOutData = dvppCommon_->GetCropedImage(); - if (CropOutData->dataSize == 0) { - MS_LOG(ERROR) << "CropOutData return NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, CropOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), CropOutData->dataSize, CropOutData->data, CropOutData->dataSize, - ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_DRC() { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_D_(); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - std::string last_step = "Decode"; - ret = JPEG_R_(last_step); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to resize, ret = " << ret; - return ret; - } - last_step = "Resize"; - ret = JPEG_C_(last_step); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to crop, ret = " << ret; - return ret; - } - // Get output of crop module - std::shared_ptr CropOutData = dvppCommon_->GetCropedImage(); - if (!CropOutData) { - MS_LOG(ERROR) << "Decode Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp (Decode + Resize + Crop) Delay] cost: " << costMs << "ms\tfps: " << fps; - return APP_ERR_OK; -} - -/* - * @description: Read image files, and perform decoding and scaling - * @param: imageFile specifies the image path to be processed - * @return: aclError which is error code of ACL API - */ -APP_ERROR MDAclProcess::JPEG_DRC_(const RawData &ImageInfo) { - // Decode process - APP_ERROR ret = dvppCommon_->CombineJpegdProcess(ImageInfo, PIXEL_FORMAT_YUV_SEMIPLANAR_420, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - // Get output of decoded jpeg image, decodeOutData locates on device - std::shared_ptr decodeOutData = dvppCommon_->GetDecodedImage(); - - if (decodeOutData == nullptr) { - MS_LOG(ERROR) << "Decode output buffer is null."; - return APP_ERR_COMM_INVALID_POINTER; - } - uint32_t pri_h = decodeOutData->heightStride; - uint32_t pri_w = decodeOutData->widthStride; - // Define output of resize jpeg image - DvppDataInfo resizeOut; - ret = ResizeConfigFilter(resizeOut, pri_w, pri_h); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to config resize, ret = " << ret << "."; - return ret; - } - // Run resize application function - ret = dvppCommon_->CombineResizeProcess(*decodeOutData, resizeOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process resize, ret = " << ret << "."; - return ret; - } - // Get output of resize jpeg image, resizeOutData locates on device - std::shared_ptr resizeOutData = dvppCommon_->GetResizedImage(); - if (resizeOutData == nullptr) { - MS_LOG(ERROR) << "resize output buffer is null."; - return APP_ERR_COMM_INVALID_POINTER; - } - // Define output of crop jpeg image - DvppDataInfo cropOut; - cropOut.width = cropWidth_; - cropOut.height = cropHeight_; - cropOut.format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; - // Define input of crop jpeg image - DvppCropInputInfo cropInfo; - cropInfo.dataInfo = *resizeOutData; - // Define crop area - CropRoiConfig cropCfg{}; - CropConfigFilter(cropCfg, cropInfo, resizeOut); - ret = dvppCommon_->CombineCropProcess(cropInfo, cropOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process center crop, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_DR(const RawData &ImageInfo) { - MS_LOG(INFO) << "It's deprecated to use kCpu as input device for Dvpp operators to compute, because it's slow and " - "unsafe, we recommend you to set input device as MapTargetDevice::kAscend for Dvpp operators. " - "This API will be removed later"; - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_DR_(ImageInfo); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode or resize, ret = " << ret; - return ret; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp Delay] cost: " << costMs << "ms\tfps: " << fps; - // Get output of resize module - std::shared_ptr ResizeOutData = dvppCommon_->GetResizedImage(); - if (!ResizeOutData) { - MS_LOG(ERROR) << "ResizedOutData returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - // Alloc host memory for the inference output according to the size of output - void *resHostBuf = nullptr; - ret = CALL_ASCEND_API(aclrtMallocHost, &resHostBuf, ResizeOutData->dataSize); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to allocate memory from host ret = " << ret; - return ret; - } - std::shared_ptr outBuf(resHostBuf, aclrtFreeHost); - processedInfo_ = outBuf; - // Memcpy the output data from device to host - ret = CALL_ASCEND_API(aclrtMemcpy, outBuf.get(), ResizeOutData->dataSize, ResizeOutData->data, - ResizeOutData->dataSize, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to copy memory from device to host, ret = " << ret; - return ret; - } - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_DR() { - RunTimeUtil time_util; - time_util.Start(); - // deal with image - APP_ERROR ret = JPEG_D_(); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to decode, ret = " << ret; - return ret; - } - std::string last_step = "Decode"; - ret = JPEG_R_(last_step); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to resize, ret = " << ret; - return ret; - } - // Get output of resize module - std::shared_ptr ResizeOutData = dvppCommon_->GetResizedImage(); - if (!ResizeOutData) { - MS_LOG(ERROR) << "Decode Data returns NULL"; - return APP_ERR_COMM_INVALID_POINTER; - } - time_util.End(); - // Calculate the time cost of preprocess - std::vector run_time = time_util.GetRunTime(); - const double costMs = run_time[0]; - const double fps = run_time[1]; - MS_LOG(INFO) << "[dvpp (Decode + Resize) Delay] cost: " << costMs << "ms\tfps: " << fps; - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::JPEG_DR_(const RawData &ImageInfo) { - // Decode process - APP_ERROR ret = dvppCommon_->CombineJpegdProcess(ImageInfo, PIXEL_FORMAT_YUV_SEMIPLANAR_420, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process decode, ret = " << ret << "."; - return ret; - } - // Get output of decoded jpeg image - std::shared_ptr decodeOutData = dvppCommon_->GetDecodedImage(); - if (decodeOutData == nullptr) { - MS_LOG(ERROR) << "Decode output buffer is null."; - return APP_ERR_COMM_INVALID_POINTER; - } - // Define output of resize jpeg image - uint32_t pri_h = decodeOutData->heightStride; - uint32_t pri_w = decodeOutData->widthStride; - DvppDataInfo resizeOut; - ret = ResizeConfigFilter(resizeOut, pri_w, pri_h); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to config resize, ret = " << ret << "."; - return ret; - } - // Run resize application function - ret = dvppCommon_->CombineResizeProcess(*decodeOutData, resizeOut, true); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to process resize, ret = " << ret << "."; - return ret; - } - return APP_ERR_OK; -} - -void MDAclProcess::CropConfigFilter(CropRoiConfig &cfg, DvppCropInputInfo &cropinfo, DvppDataInfo &resizeinfo) const { - if (resizeHeight_ != 0) { - cfg.up = (resizeHeight_ - cropHeight_) / 2; - if (cfg.up % 2 != 0) { - cfg.up++; - } - cfg.down = resizeHeight_ - (resizeHeight_ - cropHeight_) / YUV_BGR_SIZE_CONVERT_2; - if (cfg.down % 2 == 0) { - cfg.down--; - } - } else { - cfg.up = (resizeinfo.height - cropHeight_) / 2; - if (cfg.up % 2 != 0) { - cfg.up++; - } - cfg.down = resizeinfo.height - (resizeinfo.height - cropHeight_) / YUV_BGR_SIZE_CONVERT_2; - if (cfg.down % 2 == 0) { - cfg.down--; - } - } - if (resizeWidth_ != 0) { - cfg.left = (resizeWidth_ - cropWidth_) / 2; - if (cfg.left % 2 != 0) { - cfg.left++; - } - cfg.right = resizeWidth_ - (resizeWidth_ - cropWidth_) / 2; - if (cfg.right % 2 == 0) { - cfg.right--; - } - } else { - cfg.left = (resizeinfo.width - cropWidth_) / 2; - if (cfg.left % 2 != 0) { - cfg.left++; - } - cfg.right = resizeinfo.width - (resizeinfo.width - cropWidth_) / 2; - if (cfg.right % 2 == 0) { - cfg.right--; - } - } - cropinfo.roi = cfg; -} - -APP_ERROR MDAclProcess::ResizeConfigFilter(DvppDataInfo &resizeinfo, const uint32_t pri_w_, - const uint32_t pri_h_) const { - if (resizeWidth_ != 0) { - resizeinfo.width = resizeWidth_; - resizeinfo.widthStride = DVPP_ALIGN_UP(resizeWidth_, VPC_STRIDE_WIDTH); - resizeinfo.height = resizeHeight_; - resizeinfo.heightStride = DVPP_ALIGN_UP(resizeHeight_, VPC_STRIDE_HEIGHT); - } else { - if (pri_h_ >= pri_w_) { - resizeinfo.width = resizeHeight_; - resizeinfo.widthStride = DVPP_ALIGN_UP(resizeinfo.width, VPC_STRIDE_WIDTH); - resizeinfo.height = static_cast(resizeHeight_ * pri_h_ / pri_w_); - resizeinfo.heightStride = DVPP_ALIGN_UP(resizeinfo.height, VPC_STRIDE_HEIGHT); - } else { - resizeinfo.width = static_cast(resizeHeight_ * pri_w_ / pri_h_); - resizeinfo.widthStride = DVPP_ALIGN_UP(resizeinfo.width, VPC_STRIDE_WIDTH); - resizeinfo.height = resizeHeight_; - resizeinfo.heightStride = DVPP_ALIGN_UP(resizeinfo.height, VPC_STRIDE_HEIGHT); - } - } - resizeinfo.format = PIXEL_FORMAT_YUV_SEMIPLANAR_420; - return APP_ERR_OK; -} -/* - * @description: Obtain result data of memory - * @param: processed_data is result data info pointer - * @return: Address of data in the memory - */ -std::shared_ptr MDAclProcess::Get_Memory_Data() { return processedInfo_; } - -std::shared_ptr MDAclProcess::Get_Croped_DeviceData() { return dvppCommon_->GetCropedImage(); } - -std::shared_ptr MDAclProcess::Get_Resized_DeviceData() { return dvppCommon_->GetResizedImage(); } - -std::shared_ptr MDAclProcess::Get_Decode_DeviceData() { return dvppCommon_->GetDecodedImage(); } - -APP_ERROR MDAclProcess::SetResizeParas(uint32_t width, uint32_t height) { - resizeWidth_ = width; - resizeHeight_ = height; - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::SetCropParas(uint32_t width, uint32_t height) { - cropWidth_ = width; - cropHeight_ = height; - return APP_ERR_OK; -} - -APP_ERROR MDAclProcess::device_memory_release() { - dvppCommon_->ReleaseDvppBuffer(); - MS_LOG(INFO) << "Device memory release successfully"; - return APP_ERR_OK; -} - -std::vector MDAclProcess::Get_Primary_Shape() { - std::vector pri_shape; - if (dvppCommon_) { - (void)pri_shape.emplace_back(dvppCommon_->GetDecodedImage()->heightStride); - (void)pri_shape.emplace_back(dvppCommon_->GetDecodedImage()->widthStride); - } - return pri_shape; -} diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h deleted file mode 100644 index 585fca048..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_MD_ACL_PROCESS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_MD_ACL_PROCESS_H_ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "acl/acl.h" - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/DvppCommon.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -mode_t SetFileDefaultUmask(); - -class RunTimeUtil { - public: - RunTimeUtil() = default; - ~RunTimeUtil() = default; - inline void Start() { this->start = std::chrono::system_clock::now(); } - inline void End() { this->end = std::chrono::system_clock::now(); } - std::vector GetRunTime(); - - private: - std::chrono::system_clock::time_point start; - std::chrono::system_clock::time_point end; -}; - -class MDAclProcess { - public: - MDAclProcess(uint32_t resizeWidth, uint32_t resizeHeight, uint32_t cropWidth, uint32_t cropHeight, - aclrtContext context, bool is_crop = true, aclrtStream stream = nullptr, - const std::shared_ptr &dvppCommon = nullptr); - - MDAclProcess(uint32_t ParaWidth, uint32_t ParaHeight, aclrtContext context, bool is_crop = false, - aclrtStream stream = nullptr, const std::shared_ptr &dvppCommon = nullptr); - - explicit MDAclProcess(aclrtContext context, bool is_crop = false, aclrtStream stream = nullptr, - const std::shared_ptr &dvppCommon = nullptr); - - ~MDAclProcess() = default; - - // Release all the resource - APP_ERROR Release(); - // Create resource for this sample - APP_ERROR InitResource(); - // Get Ascend Resource core: context and stream which are created when InitResource() - aclrtContext GetContext(); - aclrtContext GetStream(); - - // Process the result - APP_ERROR JPEG_DRC(const RawData &ImageInfo); - APP_ERROR JPEG_DRC(); - // Procss the image without crop - APP_ERROR JPEG_DR(const RawData &ImageInfo); - APP_ERROR JPEG_DR(); - // Process the JPEG image only with decode - APP_ERROR JPEG_D(const RawData &ImageInfo); - APP_ERROR JPEG_D(); - // Process the JPEG image only with resize - APP_ERROR JPEG_R(const DvppDataInfo &ImageInfo); - APP_ERROR JPEG_R(const std::string &last_step); - // Process the JPEG image only with crop - APP_ERROR JPEG_C(const DvppDataInfo &ImageInfo); - APP_ERROR JPEG_C(const std::string &last_step); - // Process the PNG image only with decode - APP_ERROR PNG_D(const RawData &ImageInfo); - APP_ERROR PNG_D(); - // API for access memory - std::shared_ptr Get_Memory_Data(); - // API for access device memory of croped data - std::shared_ptr Get_Croped_DeviceData(); - // API for access device memory of resized data - std::shared_ptr Get_Resized_DeviceData(); - // API for access device memory of decode data - std::shared_ptr Get_Decode_DeviceData(); - - APP_ERROR H2D_Sink(const std::shared_ptr &input, - std::shared_ptr &device_input); - - APP_ERROR D2H_Pop(const std::shared_ptr &device_output, - std::shared_ptr &output); - - // D-chip memory release - APP_ERROR device_memory_release(); - - std::shared_ptr GetDeviceModule(); - - std::vector Get_Primary_Shape(); - - // Set Dvpp parameters - APP_ERROR SetResizeParas(uint32_t width, uint32_t height); - APP_ERROR SetCropParas(uint32_t width, uint32_t height); - - private: - // Crop definition - void CropConfigFilter(CropRoiConfig &cfg, DvppCropInputInfo &cropinfo, DvppDataInfo &resizeinfo) const; - // Resize definition - APP_ERROR ResizeConfigFilter(DvppDataInfo &resizeinfo, uint32_t pri_w_, uint32_t pri_h_) const; - // Initialize DVPP modules used by this sample - APP_ERROR InitModule(); - // Dvpp process with crop - APP_ERROR JPEG_DRC_(const RawData &ImageInfo); - // Dvpp process without crop - APP_ERROR JPEG_DR_(const RawData &ImageInfo); - // Impl of JPEG_D - APP_ERROR JPEG_D_(const RawData &ImageInfo); - APP_ERROR JPEG_D_(); - // Impl of JPEG_R - APP_ERROR JPEG_R_(const DvppDataInfo &ImageInfo); - APP_ERROR JPEG_R_(const std::string &last_step); - // Impl of JPEG_C - APP_ERROR JPEG_C_(const DvppDataInfo &ImageInfo); - APP_ERROR JPEG_C_(const std::string &last_step); - // Impl of PNG_D - APP_ERROR PNG_D_(const RawData &ImageInfo); - APP_ERROR PNG_D_(); - - aclrtContext context_; - aclrtStream stream_; - std::shared_ptr dvppCommon_; // dvpp object - std::shared_ptr processedInfo_; // processed data (On host) - uint32_t resizeWidth_; // dvpp resize width - uint32_t resizeHeight_; // dvpp resize height - uint32_t cropWidth_; // dvpp crop width - uint32_t cropHeight_; // dvpp crop height - bool contain_crop_; // Initialize with crop or not -}; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_MD_ACL_PROCESS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.cc deleted file mode 100644 index ec777dd1e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.cc +++ /dev/null @@ -1,142 +0,0 @@ -/** -* Copyright 2020-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h" - -#include -#include -#include "plugin/ascend/res_manager/symbol_interface/acl_op_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -bool ResourceManager::initFlag_ = true; -std::shared_ptr ResourceManager::ptr_ = nullptr; - -/** - * Check whether the file exists. - * - * @param filePath the file path we want to check - * @return APP_ERR_OK if file exists, error code otherwise - */ -APP_ERROR ExistFile(const std::string &filePath) { - struct stat fileSat = {0}; - char c[PATH_MAX] = {0x00}; - size_t count = filePath.copy(c, PATH_MAX); - if (count != filePath.length()) { - MS_LOG(ERROR) << "Failed to strcpy" << c; - return APP_ERR_COMM_FAILURE; - } - // Get the absolute path of input directory - char path[PATH_MAX] = {0x00}; - if ((strlen(c) >= PATH_MAX) || (realpath(c, path) == nullptr)) { - MS_LOG(ERROR) << "Failed to get canonicalize path"; - return APP_ERR_COMM_EXIST; - } - if (stat(c, &fileSat) == 0 && S_ISREG(fileSat.st_mode)) { - return APP_ERR_OK; - } - return APP_ERR_COMM_FAILURE; -} - -void ResourceManager::Release() { - APP_ERROR ret; - for (size_t i = 0; i < deviceIds_.size(); i++) { - if (contexts_[i] != nullptr) { - ret = CALL_ASCEND_API(aclrtDestroyContext, contexts_[i]); // Destroy context - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to destroy context, ret = " << ret << "."; - return; - } - contexts_[i] = nullptr; - } - ret = CALL_ASCEND_API(aclrtResetDevice, deviceIds_[i]); // Reset device - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to reset device, ret = " << ret << "."; - return; - } - } - - // finalize the acl when the process exit - ret = AclInitAdapter::GetInstance().AclFinalize(); - if (ret != APP_ERR_OK) { - MS_LOG(DEBUG) << "Failed to finalize acl, ret = " << ret << "."; - } - - // release all the members - acl_env_ = nullptr; - deviceIds_.clear(); - deviceIdMap_.clear(); - ptr_ = nullptr; - initFlag_ = true; - contexts_.clear(); - - MS_LOG(INFO) << "Release the resource(s) successfully."; -} - -std::shared_ptr ResourceManager::GetInstance() { - if (ptr_ == nullptr) { - ResourceManager *temp = new ResourceManager(); - ptr_.reset(temp); - } - mindspore::device::ascend::LoadAscendApiSymbols(); - return ptr_; -} - -APP_ERROR ResourceManager::InitResource(ResourceInfo &resourceInfo) { - if (acl_env_ != nullptr) { - MS_LOG(INFO) << "Acl has been initialized, skip."; - return APP_ERR_OK; - } - APP_ERROR ret = APP_ERR_OK; - acl_env_ = AclEnvGuard::GetAclEnv(); - if (acl_env_ == nullptr) { - MS_LOG(ERROR) << "Failed to init acl."; - return APP_ERR_COMM_FAILURE; - } - (void)std::copy(resourceInfo.deviceIds.begin(), resourceInfo.deviceIds.end(), std::back_inserter(deviceIds_)); - MS_LOG(INFO) << "Initialized acl successfully."; - // Open device and create context for each chip, note: it create one context for each chip - for (size_t i = 0; i < deviceIds_.size(); i++) { - deviceIdMap_[deviceIds_[i]] = i; - ret = CALL_ASCEND_API(aclrtSetDevice, deviceIds_[i]); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to open acl device: " << deviceIds_[i]; - return ret; - } - MS_LOG(INFO) << "Open device " << deviceIds_[i] << " successfully."; - aclrtContext context; - ret = CALL_ASCEND_API(aclrtCreateContext, &context, deviceIds_[i]); - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to create acl context, ret = " << ret << "."; - return ret; - } - MS_LOG(INFO) << "Created context for device " << deviceIds_[i] << " successfully"; - contexts_.push_back(context); - } - std::string singleOpPath = resourceInfo.singleOpFolderPath; - if (!singleOpPath.empty()) { - ret = CALL_ASCEND_API(aclopSetModelDir, singleOpPath.c_str()); // Set operator model directory for application - if (ret != APP_ERR_OK) { - MS_LOG(ERROR) << "Failed to aclopSetModelDir, ret = " << ret << "."; - return ret; - } - } - MS_LOG(INFO) << "Init resource successfully."; - ResourceManager::initFlag_ = false; - return APP_ERR_OK; -} - -aclrtContext ResourceManager::GetContext(int deviceId) { return contexts_[deviceIdMap_[deviceId]]; } diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h deleted file mode 100644 index 34a5aec54..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2020-2024.Huawei Technologies Co., Ltd. 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_MANAGER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_MANAGER_H_ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "acl/acl.h" - -#ifndef BUILD_LITE -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h" -#else -#include "mindspore-lite/src/extendrt/kernel/ascend/model/acl_env_guard.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/CommonDataType.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#ifndef BUILD_LITE -using AclEnvGuard = mindspore::AclEnvGuard; -using AclInitAdapter = mindspore::AclInitAdapter; -#else -using AclEnvGuard = mindspore::kernel::acl::AclEnvGuard; -using AclInitAdapter = mindspore::kernel::acl::AclInitAdapter; -#endif - -APP_ERROR ExistFile(const std::string &filePath); - -class ResourceManager { - friend APP_ERROR ExistFile(const std::string &filePath); - - public: - ResourceManager() = default; - - ~ResourceManager() = default; - - // Get the Instance of resource manager - static std::shared_ptr GetInstance(); - - // Init the resource of resource manager - APP_ERROR InitResource(ResourceInfo &resourceInfo); - - aclrtContext GetContext(int deviceId); - - void Release(); - - static bool GetInitStatus() { return initFlag_; } - - private: - static std::shared_ptr ptr_; - static bool initFlag_; - std::vector deviceIds_; - std::vector contexts_; - std::unordered_map deviceIdMap_; // Map of device to index - std::shared_ptr acl_env_; -}; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_MANAGER_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ThreadSafeQueue.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ThreadSafeQueue.h deleted file mode 100644 index f001f2845..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ThreadSafeQueue.h +++ /dev/null @@ -1,131 +0,0 @@ -/** -* Adapted from - * https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/include/ThreadSafeQueue.h - * ============================================================================ - * - * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1 Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2 Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3 Neither the names of the copyright holders nor the names of the - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * ============================================================================ - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_THREAD_SAFE_QUEUE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_THREAD_SAFE_QUEUE_H_ - -#include -#include - -template -class ThreadSafeQueue { - public: - /** - * @brief ThreadSafeQueue constructor - * @param [in] capacity: the queue capacity - */ - explicit ThreadSafeQueue(uint32_t capacity) { - // check the input value: capacity is valid - if (capacity >= kMinQueueCapacity && capacity <= kMaxQueueCapacity) { - queueCapacity = capacity; - } else { // the input value: capacity is invalid, set the default value - queueCapacity = kDefaultQueueCapacity; - } - } - - /** - * @brief ThreadSafeQueue constructor - */ - ThreadSafeQueue() { queueCapacity = kDefaultQueueCapacity; } - - /** - * @brief ThreadSafeQueue destructor - */ - ~ThreadSafeQueue() = default; - - /** - * @brief push data to queue - * @param [in] input_value: the value will push to the queue - * @return true: success to push data; false: fail to push data - */ - bool Push(T input_value) { - std::lock_guard lock(mutex_); - - // check current size is less than capacity - if (queue_.size() < queueCapacity) { - queue_.push(input_value); - return true; - } - - return false; - } - - /** - * @brief pop data from queue - * @return true: success to pop data; false: fail to pop data - */ - T Pop() { - std::lock_guard lock(mutex_); - if (queue_.empty()) { // check the queue is empty - return nullptr; - } - - T tmp_ptr = queue_.front(); - queue_.pop(); - return tmp_ptr; - } - - /** - * @brief check the queue is empty - * @return true: the queue is empty; false: the queue is not empty - */ - bool Empty() { - std::lock_guard lock(mutex_); - return queue_.empty(); - } - - /** - * @brief get the queue size - * @return the queue size - */ - uint32_t Size() { - std::lock_guard lock(mutex_); - return queue_.size(); - } - - void ExtendCapacity(uint32_t newSize) { - queueCapacity = newSize; - kMaxQueueCapacity = newSize > kMaxQueueCapacity ? newSize : kMaxQueueCapacity; - } - - private: - std::queue queue_; // the queue - uint32_t queueCapacity; // queue capacity - mutable std::mutex mutex_; // the mutex value - const uint32_t kMinQueueCapacity = 1; // the minimum queue capacity - const uint32_t kMaxQueueCapacity = 10000; // the maximum queue capacity - const uint32_t kDefaultQueueCapacity = 10; // default queue capacity -}; -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_THREAD_SAFE_QUEUE_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.cc deleted file mode 100644 index b1628bdf5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.cc +++ /dev/null @@ -1,366 +0,0 @@ -/** -* Adapted from -* https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/src/VdecHelper.cpp -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -using namespace std; - -namespace { -const uint32_t kFrameWidthMax = 4096; -const uint32_t kFrameHeightMax = 4096; -} // namespace - -VdecHelper::VdecHelper(int channelId, uint32_t width, uint32_t height, int type, aclvdecCallback callback, - uint32_t outFormat) - : channelId_(channelId), - format_(outFormat), - enType_(type), - frameWidth_(width), - frameHeight_(height), - callback_(callback), - isExit_(false), - isReleased_(false), - isChannelExit_(false) { - alignWidth_ = ALIGN_UP16(frameWidth_); - alignHeight_ = ALIGN_UP2(frameHeight_); - outputPicSize_ = YUV420SP_SIZE(alignWidth_, alignHeight_); - - vdecChannelDesc_ = nullptr; - inputStreamDesc_ = nullptr; - outputPicDesc_ = nullptr; - outputPicBuf_ = nullptr; - - aclError aclRet; - ACLLITE_LOG_INFO("get current context"); - aclRet = CALL_ASCEND_API(aclrtGetCurrentContext, &context_); - if ((aclRet != ACL_SUCCESS) || (context_ == nullptr)) { - ACLLITE_LOG_ERROR("VdecHelper : Get current acl context error:%d", aclRet); - } - - ACLLITE_LOG_INFO("VDEC width %d, height %d", frameWidth_, frameHeight_); -} - -VdecHelper::~VdecHelper() { DestroyResource(); } - -void VdecHelper::DestroyChannel() { - if (isReleased_) { - return; - } - aclError ret; - if (vdecChannelDesc_ != nullptr) { - ret = aclvdecDestroyChannel(vdecChannelDesc_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Vdec destroy channel failed, errorno: %d", ret); - } - ACLLITE_LOG_INFO("Vdec destroy Channel ok"); - ret = aclvdecDestroyChannelDesc(vdecChannelDesc_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Vdec destroy ChannelDesc failed, errorno: %d", ret); - } - ACLLITE_LOG_INFO("Vdec destroy ChannelDesc ok"); - vdecChannelDesc_ = nullptr; - isChannelExit_ = true; - } -} - -void VdecHelper::DestroyResource() { - if (isReleased_) { - return; - } - constexpr auto kMicrosecond = 1000; - while (!isChannelExit_) { - (void)usleep(kMicrosecond); - } - UnsubscribReportThread(); - - // destroy stream - aclError ret; - if (stream_ != nullptr) { - ret = CALL_ASCEND_API(aclrtDestroyStream, stream_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Vdec destroy stream failed"); - } - stream_ = nullptr; - } - isReleased_ = true; -} - -void *VdecHelper::SubscribeReportThreadFunc(void *arg) { - ACLLITE_LOG_INFO("Start vdec subscribe thread..."); - - // Notice: create context for this thread - auto *vdec = reinterpret_cast(arg); - aclrtContext context = vdec->GetContext(); - aclError ret = CALL_ASCEND_API(aclrtSetCurrentContext, context); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Video decoder set context failed, errorno: %d", ret); - } - - while (!vdec->IsExit()) { - // Notice: timeout 1000ms - ret = CALL_ASCEND_API(aclrtProcessReport, 1000); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Video decoder process report failed, errorno: %d", ret); - } - } - - ACLLITE_LOG_INFO("Vdec subscribe thread exit!"); - - return reinterpret_cast(ACLLITE_OK); -} - -void VdecHelper::UnsubscribReportThread() { - if ((subscribeThreadId_ == 0) || (stream_ == nullptr)) { - return; - } - - (void)aclrtUnSubscribeReport(static_cast(subscribeThreadId_), stream_); - // destroy thread - isExit_ = true; - - void *res = nullptr; - int joinThreadErr = pthread_join(subscribeThreadId_, &res); - if (joinThreadErr) { - ACLLITE_LOG_ERROR("Join thread failed, threadId = %lu, err = %d", subscribeThreadId_, joinThreadErr); - } else { - if (reinterpret_cast(res) != 0) { - ACLLITE_LOG_ERROR("thread run failed. ret is %lu.", reinterpret_cast(res)); - } - } - ACLLITE_LOG_INFO("Destroy report thread success."); -} - -AclLiteError VdecHelper::Init() { - ACLLITE_LOG_INFO("Vdec process init start..."); - aclError aclRet = aclrtCreateStream(&stream_); - if (aclRet != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Vdec create stream failed, errorno: %d", aclRet); - return ACLLITE_ERROR_CREATE_STREAM; - } - ACLLITE_LOG_INFO("Vdec create stream ok"); - - int ret = pthread_create(&subscribeThreadId_, nullptr, SubscribeReportThreadFunc, reinterpret_cast(this)); - if (ret) { - ACLLITE_LOG_ERROR("Start vdec subscribe thread failed, return: %d", ret); - return ACLLITE_ERROR_CREATE_THREAD; - } - (void)CALL_ASCEND_API(aclrtSubscribeReport, static_cast(subscribeThreadId_), stream_); - - ret = CreateVdecChannelDesc(); - if (ret != ACLLITE_OK) { - ACLLITE_LOG_ERROR("Create vdec channel failed"); - return ret; - } - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::CreateVdecChannelDesc() { - vdecChannelDesc_ = aclvdecCreateChannelDesc(); - if (vdecChannelDesc_ == nullptr) { - ACLLITE_LOG_ERROR("Create vdec channel desc failed"); - return ACLLITE_ERROR_CREATE_DVPP_CHANNEL_DESC; - } - - // channelId: 0-15 - aclError ret = aclvdecSetChannelDescChannelId(vdecChannelDesc_, channelId_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec channel id to %d failed, errorno:%d", channelId_, ret); - return ACLLITE_ERROR_SET_VDEC_CHANNEL_ID; - } - - ret = aclvdecSetChannelDescThreadId(vdecChannelDesc_, subscribeThreadId_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec channel thread id failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_VDEC_CHANNEL_THREAD_ID; - } - - // callback func - ret = aclvdecSetChannelDescCallback(vdecChannelDesc_, callback_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec channel callback failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_VDEC_CALLBACK; - } - - ret = aclvdecSetChannelDescEnType(vdecChannelDesc_, static_cast(enType_)); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec channel entype failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_VDEC_ENTYPE; - } - - ret = aclvdecSetChannelDescOutPicFormat(vdecChannelDesc_, static_cast(format_)); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec channel pic format failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_VDEC_PIC_FORMAT; - } - - // create vdec channel - ACLLITE_LOG_INFO("Start create vdec channel by desc..."); - ret = aclvdecCreateChannel(vdecChannelDesc_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("fail to create vdec channel"); - return ACLLITE_ERROR_CREATE_VDEC_CHANNEL; - } - ACLLITE_LOG_INFO("Create vdec channel ok"); - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::CreateInputStreamDesc(const std::shared_ptr &frameData) { - inputStreamDesc_ = acldvppCreateStreamDesc(); - if (inputStreamDesc_ == nullptr) { - ACLLITE_LOG_ERROR("Create input stream desc failed"); - return ACLLITE_ERROR_CREATE_STREAM_DESC; - } - - aclError ret; - // to the last data,send an ending signal to dvpp vdec - if (frameData->isFinished) { - ret = acldvppSetStreamDescEos(inputStreamDesc_, 1); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set EOS to input stream desc failed, errorno: %d", ret); - return ACLLITE_ERROR_SET_STREAM_DESC_EOS; - } - return ACLLITE_OK; - } - - ret = acldvppSetStreamDescData(inputStreamDesc_, frameData->data); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set input stream data failed, errorno: %d", ret); - return ACLLITE_ERROR_SET_STREAM_DESC_DATA; - } - - // set size for dvpp stream desc - ret = acldvppSetStreamDescSize(inputStreamDesc_, frameData->size); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set input stream size failed, errorno: %d", ret); - return ACLLITE_ERROR_SET_STREAM_DESC_SIZE; - } - - ret = acldvppSetStreamDescTimestamp(inputStreamDesc_, frameData->frameId); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set input stream timestamp failed, errorno: %d", ret); - return ACLLITE_ERROR; - } - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::CreateOutputPicDesc(size_t size) { - // Malloc output device memory - aclError ret = acldvppMalloc(&outputPicBuf_, size); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR( - "Malloc vdec output buffer failed when create " - "vdec output desc, errorno:%d", - ret); - return ACLLITE_ERROR_MALLOC_DVPP; - } - - outputPicDesc_ = acldvppCreatePicDesc(); - if (outputPicDesc_ == nullptr) { - ACLLITE_LOG_ERROR("Create vdec output pic desc failed"); - return ACLLITE_ERROR_CREATE_PIC_DESC; - } - - ret = acldvppSetPicDescData(outputPicDesc_, outputPicBuf_); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec output pic desc data failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_PIC_DESC_DATA; - } - - ret = acldvppSetPicDescSize(outputPicDesc_, size); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec output pic size failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_PIC_DESC_SIZE; - } - - ret = acldvppSetPicDescFormat(outputPicDesc_, static_cast(format_)); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Set vdec output pic format failed, errorno:%d", ret); - return ACLLITE_ERROR_SET_PIC_DESC_FORMAT; - } - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::Process(const std::shared_ptr &frameData, void *userData) { - // create input desc - AclLiteError atlRet = CreateInputStreamDesc(frameData); - if (atlRet != ACLLITE_OK) { - ACLLITE_LOG_ERROR("Create stream desc failed"); - return atlRet; - } - // create out desc - atlRet = CreateOutputPicDesc(outputPicSize_); - if (atlRet != ACLLITE_OK) { - ACLLITE_LOG_ERROR("Create pic desc failed"); - return atlRet; - } - // send data to dvpp vdec to decode - aclError ret = aclvdecSendFrame(vdecChannelDesc_, inputStreamDesc_, outputPicDesc_, nullptr, userData); - if (ret != ACL_SUCCESS) { - ACLLITE_LOG_ERROR("Send frame to vdec failed, errorno:%d", ret); - return ACLLITE_ERROR_VDEC_SEND_FRAME; - } - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::SetFormat(uint32_t format) { - if ((format != PIXEL_FORMAT_YUV_SEMIPLANAR_420) && (format != PIXEL_FORMAT_YVU_SEMIPLANAR_420)) { - ACLLITE_LOG_ERROR( - "Set video decode output image format to %d failed, " - "only support %d(YUV420SP NV12) and %d(YUV420SP NV21)", - format, (int)PIXEL_FORMAT_YUV_SEMIPLANAR_420, (int)PIXEL_FORMAT_YVU_SEMIPLANAR_420); - return ACLLITE_ERROR_VDEC_FORMAT_INVALID; - } - - format_ = format; - ACLLITE_LOG_INFO("Set video decode output image format to %d ok", format); - - return ACLLITE_OK; -} - -AclLiteError VdecHelper::VideoParamCheck() const { - if ((frameWidth_ == 0) || (frameWidth_ > kFrameWidthMax)) { - ACLLITE_LOG_ERROR("video frame width %d is invalid, the legal range is [0, %d]", frameWidth_, kFrameWidthMax); - return ACLLITE_ERROR_VDEC_INVALID_PARAM; - } - if ((frameHeight_ == 0) || (frameHeight_ > kFrameHeightMax)) { - ACLLITE_LOG_ERROR("video frame height %d is invalid, the legal range is [0, %d]", frameHeight_, kFrameHeightMax); - return ACLLITE_ERROR_VDEC_INVALID_PARAM; - } - if ((format_ != PIXEL_FORMAT_YUV_SEMIPLANAR_420) && (format_ != PIXEL_FORMAT_YVU_SEMIPLANAR_420)) { - ACLLITE_LOG_ERROR( - "video decode image format %d invalid, " - "only support %d(YUV420SP NV12) and %d(YUV420SP NV21)", - format_, (int)PIXEL_FORMAT_YUV_SEMIPLANAR_420, (int)PIXEL_FORMAT_YVU_SEMIPLANAR_420); - return ACLLITE_ERROR_VDEC_INVALID_PARAM; - } - if (enType_ > static_cast(H264_HIGH_LEVEL)) { - ACLLITE_LOG_ERROR("Input video stream format %d invalid", enType_); - return ACLLITE_ERROR_VDEC_INVALID_PARAM; - } - - return ACLLITE_OK; -} diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h deleted file mode 100644 index 3ebf8b840..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h +++ /dev/null @@ -1,90 +0,0 @@ -/** -* Adapted from -* https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/include/VdecHelper.h -* Copyright 2022 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_VDEC_HELPER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_VDEC_HELPER_H_ - -#include -#include -#include - -#include "acl/acl.h" -#include "acl/ops/acl_dvpp.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteError.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteType.h" - -class VdecHelper { - public: - VdecHelper(int channel, uint32_t width, uint32_t height, int type, aclvdecCallback callback, - uint32_t outFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420); - ~VdecHelper(); - - static void *SubscribeReportThreadFunc(void *arg); - - AclLiteError Init(); - void DestroyResource(); - void DestroyChannel(); - - AclLiteError Process(const std::shared_ptr &frameData, void *userData); - AclLiteError SetFormat(uint32_t format); - AclLiteError VideoParamCheck() const; - bool IsExit() const { return isExit_; } - aclrtContext GetContext() { return context_; } - - private: - AclLiteError CreateVdecChannelDesc(); - AclLiteError CreateInputStreamDesc(const std::shared_ptr &frameData); - AclLiteError CreateOutputPicDesc(size_t size); - void UnsubscribReportThread(); - - private: - int channelId_; - - /* 1:YUV420 semi-planner(nv12) - 2:YVU420 semi-planner(nv21) - */ - uint32_t format_; - - /* 0:H265 main level - * 1:H264 baseline level - * 2:H264 main level - * 3:H264 high level - */ - uint32_t enType_; - - uint32_t frameWidth_; - uint32_t frameHeight_; - uint32_t alignWidth_; - uint32_t alignHeight_; - uint32_t outputPicSize_; - void *outputPicBuf_; - aclvdecCallback callback_; - aclrtContext context_{}; - aclrtStream stream_{}; - - aclvdecChannelDesc *vdecChannelDesc_; - acldvppStreamDesc *inputStreamDesc_; - acldvppPicDesc *outputPicDesc_; - - pthread_t subscribeThreadId_{}; - bool isExit_; - bool isReleased_; - bool isChannelExit_; -}; - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_VDEC_HELPER_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.cc deleted file mode 100644 index 10c3c393f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.cc +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h" -#include "src/common/log_adapter.h" -#include "utils/ms_utils.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -namespace mindspore { -std::shared_ptr AclEnvGuard::global_acl_env_ = nullptr; -std::mutex AclEnvGuard::global_acl_env_mutex_; - -AclInitAdapter &AclInitAdapter::GetInstance() { - static AclInitAdapter instance = {}; - return instance; -} - -aclError AclInitAdapter::AclInit(const char *config_file) { - std::lock_guard lock(flag_mutex_); - if (init_flag_) { - return ACL_SUCCESS; - } - - init_flag_ = true; - return CALL_ASCEND_API(aclInit, config_file); -} - -aclError AclInitAdapter::AclFinalize() { - std::lock_guard lock(flag_mutex_); - if (!init_flag_) { - MS_LOG(INFO) << "Acl had been finalized."; - return ACL_SUCCESS; - } - - MS_LOG(INFO) << "Begin to aclFinalize."; - init_flag_ = false; - return CALL_ASCEND_API(aclFinalize); -} - -aclError AclInitAdapter::ForceFinalize() { - std::lock_guard lock(flag_mutex_); - MS_LOG(INFO) << "Begin to force aclFinalize."; - init_flag_ = false; - return CALL_ASCEND_API(aclFinalize); -} - -AclEnvGuard::AclEnvGuard() : errno_(AclInitAdapter::GetInstance().AclInit(nullptr)) { - if (errno_ != ACL_SUCCESS && errno_ != ACL_ERROR_REPEAT_INITIALIZE) { - MS_LOG(ERROR) << "Execute aclInit failed."; - return; - } - MS_LOG(INFO) << "Execute aclInit success."; -} - -AclEnvGuard::~AclEnvGuard() { - TRY_AND_CATCH_WITH_EXCEPTION(errno_ = AclInitAdapter::GetInstance().AclFinalize(), - "AclInitAdapter GetInstance failed"); - if (errno_ != ACL_SUCCESS && errno_ != ACL_ERROR_REPEAT_FINALIZE) { - MS_LOG(ERROR) << "Execute AclFinalize failed."; - } - MS_LOG(INFO) << "Execute AclFinalize success."; -} - -std::shared_ptr AclEnvGuard::GetAclEnv() { - std::lock_guard lock(global_acl_env_mutex_); - std::shared_ptr acl_env = global_acl_env_; - if (acl_env != nullptr) { - MS_LOG(INFO) << "Acl has been initialized, skip."; - } else { - acl_env = std::make_shared(); - aclError ret = acl_env->GetErrno(); - if (ret != ACL_SUCCESS && ret != ACL_ERROR_REPEAT_INITIALIZE) { - MS_LOG(ERROR) << "Execute aclInit failed."; - return nullptr; - } - global_acl_env_ = acl_env; - MS_LOG(INFO) << "Execute aclInit success."; - } - return acl_env; -} -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h deleted file mode 100644 index 66bd7c877..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_env_guard.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_CXX_API_GRAPH_ACL_ACL_ENV_GUARD_H -#define MINDSPORE_CCSRC_CXX_API_GRAPH_ACL_ACL_ENV_GUARD_H - -#include -#include -#include "acl/acl_base.h" - -namespace mindspore { -class __attribute__((visibility("default"))) AclInitAdapter { - public: - static AclInitAdapter &GetInstance(); - aclError AclInit(const char *config_file); - aclError AclFinalize(); - aclError ForceFinalize(); - - private: - AclInitAdapter() : init_flag_(false) {} - ~AclInitAdapter() = default; - - bool init_flag_; - std::mutex flag_mutex_; -}; - -class __attribute__((visibility("default"))) AclEnvGuard { - public: - explicit AclEnvGuard(); - ~AclEnvGuard(); - aclError GetErrno() const { return errno_; } - static std::shared_ptr GetAclEnv(); - - private: - static std::shared_ptr global_acl_env_; - static std::mutex global_acl_env_mutex_; - - aclError errno_; -}; -} // namespace mindspore -#endif // MINDSPORE_CCSRC_CXX_API_GRAPH_ACL_ACL_ENV_GUARD_H diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.cc deleted file mode 100644 index a99f55ff4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.cc +++ /dev/null @@ -1,630 +0,0 @@ -/** -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ResourceManager.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/MDAclProcess.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor_ascend910b.h" -#endif -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -void *PluginCreateDvppVideo(aclrtContext context, uint8_t *data, uint32_t size, uint32_t width, uint32_t height, - uint32_t type, uint32_t out_format, const std::string &output) { - return new DvppVideo(context, data, size, width, height, type, out_format, output); -} - -AclLiteError PluginInitDvppVideo(void *dvpp_video) { - if (dvpp_video == nullptr) { - return ACLLITE_ERROR; - } - - return static_cast(dvpp_video)->Init(); -} - -AclLiteError PluginCloseDvppVideo(void *dvpp_video) { - if (dvpp_video == nullptr) { - return ACLLITE_ERROR; - } - - auto ret = static_cast(dvpp_video)->Close(); - delete static_cast(dvpp_video); - return ret; -} - -AclLiteError PluginDvppVideoDumpFrame(void *dvpp_video) { - if (dvpp_video == nullptr) { - return ACLLITE_ERROR; - } - - return static_cast(dvpp_video)->DumpFrame(); -} - -APP_ERROR PluginInitResource(ResourceInfo &resource_info) { - if (ResourceManager::GetInstance() == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return ResourceManager::GetInstance()->InitResource(resource_info); -} - -aclrtContext PluginGetContext(int device_id) { - if (ResourceManager::GetInstance() == nullptr) { - return nullptr; - } - return ResourceManager::GetInstance()->GetContext(device_id); -} - -void PluginRelease() { - if (ResourceManager::GetInstance() != nullptr) { - return ResourceManager::GetInstance()->Release(); - } -} - -void *PluginCreateAclProcessWithResize(uint32_t resize_width, uint32_t resize_height, uint32_t crop_width, - uint32_t crop_height, aclrtContext context, bool is_crop, aclrtStream stream, - const std::shared_ptr &dvpp_common) { - return new MDAclProcess(resize_width, resize_height, crop_width, crop_height, context, is_crop, stream, dvpp_common); -} - -void *PluginCreateAclProcessWithPara(uint32_t para_width, uint32_t para_height, aclrtContext context, bool is_crop, - aclrtStream stream, const std::shared_ptr &dvpp_common) { - return new MDAclProcess(para_width, para_height, context, is_crop, stream, dvpp_common); -} - -void *PluginCreateAclProcess(aclrtContext context, bool is_crop, aclrtStream stream, - const std::shared_ptr &dvpp_common) { - return new MDAclProcess(context, is_crop, stream, dvpp_common); -} - -void PluginDestroyAclProcess(void *acl_process) { - if (acl_process != nullptr) { - delete static_cast(acl_process); - } -} - -APP_ERROR PluginReleaseAclProcess(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->Release(); -} - -APP_ERROR PluginInitAclProcess(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->InitResource(); -} - -aclrtContext PluginGetContextFromAclProcess(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->GetContext(); -} - -aclrtStream PluginGetStreamFromAclProcess(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->GetStream(); -} - -APP_ERROR PluginJPEG_DRC_WITH_DATA(void *acl_process, const RawData &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_DRC(data); -} - -APP_ERROR PluginJPEG_DR_WITH_DATA(void *acl_process, const RawData &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_DR(data); -} - -APP_ERROR PluginJPEG_D_WITH_DATA(void *acl_process, const RawData &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_D(data); -} - -APP_ERROR PluginJPEG_R_WITH_DATA(void *acl_process, const DvppDataInfo &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_R(data); -} - -APP_ERROR PluginJPEG_C_WITH_DATA(void *acl_process, const DvppDataInfo &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_C(data); -} - -APP_ERROR PluginPNG_D_WITH_DATA(void *acl_process, const RawData &data) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->PNG_D(data); -} - -APP_ERROR PluginJPEG_DRC(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_DRC(); -} - -APP_ERROR PluginJPEG_DR(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_DR(); -} - -APP_ERROR PluginJPEG_D(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_D(); -} - -APP_ERROR PluginJPEG_R(void *acl_process, const std::string &last_step) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_R(last_step); -} - -APP_ERROR PluginJPEG_C(void *acl_process, const std::string &last_step) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->JPEG_C(last_step); -} - -APP_ERROR PluginPNG_D(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->PNG_D(); -} - -void *PluginGetMemoryData(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->Get_Memory_Data().get(); -} - -DvppDataInfo *PluginGetCropedDeviceData(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->Get_Croped_DeviceData().get(); -} - -DvppDataInfo *PluginGetResizedDeviceData(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->Get_Resized_DeviceData().get(); -} - -DvppDataInfo *PluginGetDecodeDeviceData(void *acl_process) { - if (acl_process == nullptr) { - return nullptr; - } - return static_cast(acl_process)->Get_Decode_DeviceData().get(); -} - -APP_ERROR PluginH2D_Sink(void *acl_process, const std::shared_ptr &input, - std::shared_ptr &device_input) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->H2D_Sink(input, device_input); -} - -APP_ERROR PluginD2H_Pop(void *acl_process, const std::shared_ptr &device_output, - std::shared_ptr &output) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->D2H_Pop(device_output, output); -} - -APP_ERROR PluginDeviceMemoryRelease(void *acl_process) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->device_memory_release(); -} - -APP_ERROR PluginSetResizeParas(void *acl_process, uint32_t width, uint32_t height) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->SetResizeParas(width, height); -} - -APP_ERROR PluginSetCropParas(void *acl_process, uint32_t width, uint32_t height) { - if (acl_process == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return static_cast(acl_process)->SetCropParas(width, height); -} - -int PluginaclrtMemcpy(void *dst, size_t dest_max, const void *src, size_t count, int kind) { - return CALL_ASCEND_API(aclrtMemcpy, dst, dest_max, src, count, static_cast(kind)); -} - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -// Ascend910B -APP_ERROR PluginDvppAdjustBrightness(const std::shared_ptr &input, - std::shared_ptr *output, - float factor) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAdjustBrightness(input, output, factor); -} - -APP_ERROR PluginDvppAdjustContrast(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAdjustContrast(input, output, factor); -} - -APP_ERROR PluginDvppAdjustHue(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAdjustHue(input, output, factor); -} - -APP_ERROR PluginDvppAdjustSaturation(const std::shared_ptr &input, - std::shared_ptr *output, - float factor) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAdjustSaturation(input, output, factor); -} - -APP_ERROR PluginDvppAdjustSharpness(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAdjustSharpness(input, output, factor); -} - -APP_ERROR PluginDvppAffine(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &matrix, uint32_t interpolation_mode, uint32_t padding_mode, - const std::vector &fill) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAffine(input, output, matrix, interpolation_mode, padding_mode, fill); -} - -APP_ERROR PluginDvppAutoContrast(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &cutoff, const std::vector &ignore) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppAutoContrast(input, output, cutoff, ignore); -} - -APP_ERROR PluginDvppConvertColor(const std::shared_ptr &input, - std::shared_ptr *output, - mindspore::dataset::ConvertMode convertMode) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppConvertColor(input, output, convertMode); -} - -APP_ERROR PluginDvppCrop(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, - uint32_t left, uint32_t height, uint32_t width) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppCrop(input, output, top, left, height, width); -} - -APP_ERROR PluginDvppDecode(const std::shared_ptr &input, - std::shared_ptr *output) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppDecode(input, output); -} - -APP_ERROR PluginDvppEqualize(const std::shared_ptr &input, - std::shared_ptr *output) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppEqualize(input, output); -} - -APP_ERROR PluginDvppErase(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, - uint32_t left, uint32_t height, uint32_t width, const std::vector &value) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppErase(input, output, top, left, height, width, value); -} - -APP_ERROR PluginDvppGaussianBlur(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &kernel_size, const std::vector &sigma, - uint32_t padding_mode) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppGaussianBlur(input, output, kernel_size, sigma, padding_mode); -} - -APP_ERROR PluginDvppHorizontalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppHorizontalFlip(input, output); -} - -APP_ERROR PluginDvppInvert(const std::shared_ptr &input, - std::shared_ptr *output) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppInvert(input, output); -} - -APP_ERROR PluginDvppNormalize(const std::shared_ptr &input, - std::shared_ptr *output, - std::vector mean, std::vector std, bool is_hwc) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppNormalize(input, output, mean, std, is_hwc); -} - -APP_ERROR PluginDvppPad(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &padding, uint32_t padding_mode, const std::vector &fill) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppPad(input, output, padding, padding_mode, fill); -} - -APP_ERROR PluginDvppPerspective(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, - mindspore::dataset::InterpolationMode interpolation) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppPerspective(input, output, start_points, end_points, interpolation); -} - -APP_ERROR PluginDvppPosterize(const std::shared_ptr &input, - std::shared_ptr *output, uint8_t bits) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppPosterize(input, output, bits); -} - -APP_ERROR PluginDvppResize(const std::shared_ptr &input, - std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, mindspore::dataset::InterpolationMode mode) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppResize(input, output, output_height, output_width, fx, fy, mode); -} - -APP_ERROR PluginDvppResizedCrop(const std::shared_ptr &input, - std::shared_ptr *output, int32_t top, - int32_t left, int32_t height, int32_t width, int32_t output_height, - int32_t output_width, mindspore::dataset::InterpolationMode mode) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppResizedCrop(input, output, top, left, height, width, output_height, output_width, mode); -} - -APP_ERROR PluginDvppRotate(const std::shared_ptr &input, - std::shared_ptr *output, float degrees, - mindspore::dataset::InterpolationMode mode, bool expand, const std::vector ¢er, - const std::vector &fill) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppRotate(input, output, degrees, mode, expand, center, fill); -} - -APP_ERROR PluginDvppSolarize(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector &threshold) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppSolarize(input, output, threshold); -} - -APP_ERROR PluginDvppVerticalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - if (input == nullptr) { - return APP_ERR_ACL_FAILURE; - } - if (output == nullptr) { - return APP_ERR_ACL_FAILURE; - } - return DvppVerticalFlip(input, output); -} - -// acl -APP_ERROR PluginGetSocName(std::string *soc_name) { return mindspore::dataset::GetSocName(soc_name); } - -APP_ERROR PluginCreateAclTensor(const int64_t *view_dims, uint64_t view_dims_num, mindspore::TypeId data_type, - const int64_t *stride, int64_t offset, const int64_t *storage_dims, - uint64_t storage_dims_num, void *tensor_data, bool is_hwc, void **acl_tensor) { - if (view_dims == nullptr) { - MS_LOG(ERROR) << "Input view_dims is null."; - return APP_ERR_ACL_FAILURE; - } - if (stride == nullptr) { - MS_LOG(ERROR) << "Input stride is null."; - return APP_ERR_ACL_FAILURE; - } - if (storage_dims == nullptr) { - MS_LOG(ERROR) << "Input storage_dims is null."; - return APP_ERR_ACL_FAILURE; - } - if (tensor_data == nullptr) { - MS_LOG(ERROR) << "Input tensor_data is null."; - return APP_ERR_ACL_FAILURE; - } - if (acl_tensor == nullptr) { - MS_LOG(ERROR) << "Input acl_tensor is null."; - return APP_ERR_ACL_FAILURE; - } - - return mindspore::dataset::CreateAclTensor(view_dims, view_dims_num, data_type, stride, offset, storage_dims, - storage_dims_num, tensor_data, is_hwc, acl_tensor); -} - -APP_ERROR PluginDestroyTensor(void *tensor) { - if (tensor == nullptr) { - MS_LOG(ERROR) << "Input tensor is null."; - return APP_ERR_ACL_FAILURE; - } - - return mindspore::dataset::DestroyTensor(tensor); -} - -APP_ERROR PluginDestroyFloatArray(void *float_array) { - if (float_array == nullptr) { - MS_LOG(ERROR) << "Input float_array is null."; - return APP_ERR_ACL_FAILURE; - } - - return mindspore::dataset::DestroyFloatArray(float_array); -} - -APP_ERROR PluginDestroyIntArray(void *int_array) { - if (int_array == nullptr) { - MS_LOG(ERROR) << "Input int_array is null."; - return APP_ERR_ACL_FAILURE; - } - - return mindspore::dataset::DestroyIntArray(int_array); -} -#endif diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h deleted file mode 100644 index bfcb83e7e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/acl_plugin.h +++ /dev/null @@ -1,179 +0,0 @@ -/** -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_PLUGIN_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_PLUGIN_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/core/device_tensor_ascend910b.h" -#endif -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h" -#include "utils/dlopen_macro.h" - -class DvppCommon; -struct DvppDataInfo; - -PLUGIN_METHOD(CreateDvppVideo, void *, void *, uint8_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, - const std::string &); -PLUGIN_METHOD(InitDvppVideo, int, void *); -PLUGIN_METHOD(CloseDvppVideo, int, void *); -PLUGIN_METHOD(DvppVideoDumpFrame, int, void *); - -PLUGIN_METHOD(InitResource, int, ResourceInfo &); -PLUGIN_METHOD(GetContext, void *, int); -PLUGIN_METHOD(Release, void); - -PLUGIN_METHOD(CreateAclProcessWithResize, void *, uint32_t, uint32_t, uint32_t, uint32_t, void *, bool, void *, - const std::shared_ptr &); -PLUGIN_METHOD(CreateAclProcessWithPara, void *, uint32_t, uint32_t, void *, bool, void *, - const std::shared_ptr &); -PLUGIN_METHOD(CreateAclProcess, void *, void *, bool, void *, const std::shared_ptr &); -PLUGIN_METHOD(DestroyAclProcess, void, void *); -PLUGIN_METHOD(ReleaseAclProcess, int, void *); -PLUGIN_METHOD(InitAclProcess, int, void *); -PLUGIN_METHOD(GetContextFromAclProcess, void *, void *); -PLUGIN_METHOD(GetStreamFromAclProcess, void *, void *); -PLUGIN_METHOD(JPEG_DRC_WITH_DATA, int, void *, const RawData &); -PLUGIN_METHOD(JPEG_DR_WITH_DATA, int, void *, const RawData &); -PLUGIN_METHOD(JPEG_D_WITH_DATA, int, void *, const RawData &); -PLUGIN_METHOD(JPEG_R_WITH_DATA, int, void *, const DvppDataInfo &); -PLUGIN_METHOD(JPEG_C_WITH_DATA, int, void *, const DvppDataInfo &); -PLUGIN_METHOD(PNG_D_WITH_DATA, int, void *, const RawData &); -PLUGIN_METHOD(JPEG_DRC, int, void *); -PLUGIN_METHOD(JPEG_DR, int, void *); -PLUGIN_METHOD(JPEG_D, int, void *); -PLUGIN_METHOD(JPEG_R, int, void *, const std::string &); -PLUGIN_METHOD(JPEG_C, int, void *, const std::string &); -PLUGIN_METHOD(PNG_D, int, void *); -PLUGIN_METHOD(GetMemoryData, void *, void *); -PLUGIN_METHOD(GetCropedDeviceData, DvppDataInfo *, void *); -PLUGIN_METHOD(GetResizedDeviceData, DvppDataInfo *, void *); -PLUGIN_METHOD(GetDecodeDeviceData, DvppDataInfo *, void *); -PLUGIN_METHOD(H2D_Sink, int, void *, const std::shared_ptr &, - std::shared_ptr &); -PLUGIN_METHOD(D2H_Pop, int, void *, const std::shared_ptr &, - std::shared_ptr &); -PLUGIN_METHOD(DeviceMemoryRelease, int, void *); -PLUGIN_METHOD(SetResizeParas, int, void *, uint32_t, uint32_t); -PLUGIN_METHOD(SetCropParas, int, void *, uint32_t, uint32_t); - -ORIGIN_METHOD(aclrtMallocHost, int, void **, size_t); -PLUGIN_METHOD(aclrtMemcpy, int, void *, size_t, const void *, size_t, int); -ORIGIN_METHOD(aclrtFreeHost, int, void *); - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -// Ascend910B -PLUGIN_METHOD(DvppAdjustBrightness, int, const std::shared_ptr &, - std::shared_ptr *, float); - -PLUGIN_METHOD(DvppAdjustContrast, int, const std::shared_ptr &, - std::shared_ptr *, float); - -PLUGIN_METHOD(DvppAdjustHue, int, const std::shared_ptr &, - std::shared_ptr *, float); - -PLUGIN_METHOD(DvppAdjustSaturation, int, const std::shared_ptr &, - std::shared_ptr *, float); - -PLUGIN_METHOD(DvppAdjustSharpness, int, const std::shared_ptr &, - std::shared_ptr *, float); - -PLUGIN_METHOD(DvppAffine, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector &, uint32_t, - uint32_t, const std::vector &); - -PLUGIN_METHOD(DvppAutoContrast, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector &, - const std::vector &); - -PLUGIN_METHOD(DvppConvertColor, int, const std::shared_ptr &, - std::shared_ptr *, mindspore::dataset::ConvertMode); - -PLUGIN_METHOD(DvppCrop, int, const std::shared_ptr &, - std::shared_ptr *, uint32_t, uint32_t, uint32_t, uint32_t); - -PLUGIN_METHOD(DvppDecode, int, const std::shared_ptr &, - std::shared_ptr *); - -PLUGIN_METHOD(DvppEqualize, int, const std::shared_ptr &, - std::shared_ptr *); - -PLUGIN_METHOD(DvppErase, int, const std::shared_ptr &, - std::shared_ptr *, uint32_t, uint32_t, uint32_t, uint32_t, - const std::vector &); - -PLUGIN_METHOD(DvppGaussianBlur, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector &, - const std::vector &, uint32_t); - -PLUGIN_METHOD(DvppHorizontalFlip, int, const std::shared_ptr &, - std::shared_ptr *); - -PLUGIN_METHOD(DvppInvert, int, const std::shared_ptr &, - std::shared_ptr *); - -PLUGIN_METHOD(DvppNormalize, int, const std::shared_ptr &, - std::shared_ptr *, std::vector, std::vector, - bool); - -PLUGIN_METHOD(DvppPad, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector &, uint32_t, - const std::vector &); - -PLUGIN_METHOD(DvppPerspective, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector> &, - const std::vector> &, mindspore::dataset::InterpolationMode); - -PLUGIN_METHOD(DvppPosterize, int, const std::shared_ptr &, - std::shared_ptr *, uint8_t); - -PLUGIN_METHOD(DvppResize, int, const std::shared_ptr &, - std::shared_ptr *, int32_t, int32_t, double, double, - mindspore::dataset::InterpolationMode); - -PLUGIN_METHOD(DvppResizedCrop, int, const std::shared_ptr &, - std::shared_ptr *, int32_t, int32_t, int32_t, int32_t, - int32_t, int32_t, mindspore::dataset::InterpolationMode); - -PLUGIN_METHOD(DvppRotate, int, const std::shared_ptr &, - std::shared_ptr *, float, - mindspore::dataset::InterpolationMode, bool, const std::vector &, const std::vector &); - -PLUGIN_METHOD(DvppSolarize, int, const std::shared_ptr &, - std::shared_ptr *, const std::vector &); - -PLUGIN_METHOD(DvppVerticalFlip, int, const std::shared_ptr &, - std::shared_ptr *); -// acl -PLUGIN_METHOD(GetSocName, int, std::string *); - -PLUGIN_METHOD(CreateAclTensor, int, const int64_t *, uint64_t, mindspore::TypeId, const int64_t *, int64_t, - const int64_t *, uint64_t, void *, bool, void **); - -PLUGIN_METHOD(DestroyTensor, int, void *); - -PLUGIN_METHOD(DestroyFloatArray, int, void *); - -PLUGIN_METHOD(DestroyIntArray, int, void *); -#endif - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_ACL_PLUGIN_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc deleted file mode 100644 index adaf1891b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.cc +++ /dev/null @@ -1,2840 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utils/file_utils.h" -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/core/type_id.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/invert_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/posterize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/solarize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_symbol.h" - -#include "acldvppop/acldvpp_adjust_brightness.h" -#include "acldvppop/acldvpp_adjust_contrast.h" -#include "acldvppop/acldvpp_adjust_hue.h" -#include "acldvppop/acldvpp_adjust_saturation.h" -#include "acldvppop/acldvpp_auto_contrast.h" -#include "acldvppop/acldvpp_adjust_sharpness.h" -#include "acldvppop/acldvpp_convert_color.h" -#include "acldvppop/acldvpp_crop.h" -#include "acldvppop/acldvpp_crop_and_resize.h" -#include "acldvppop/acldvpp_decode_jpeg.h" -#include "acldvppop/acldvpp_equalize.h" -#include "acldvppop/acldvpp_erase.h" -#include "acldvppop/acldvpp_gaussian_blur.h" -#include "acldvppop/acldvpp_horizontal_flip.h" -#include "acldvppop/acldvpp_invert.h" -#include "acldvppop/acldvpp_normalize.h" -#include "acldvppop/acldvpp_pad.h" -#include "acldvppop/acldvpp_posterize.h" -#include "acldvppop/acldvpp_resize.h" -#include "acldvppop/acldvpp_rotate.h" -#include "acldvppop/acldvpp_solarize.h" -#include "acldvppop/acldvpp_vertical_flip.h" -#include "acldvppop/acldvpp_warp_affine.h" -#include "acldvppop/acldvpp_warp_perspective.h" -#include "plugin/ascend/res_manager/stream_manager/ascend_stream_manager.h" - -namespace mindspore { -namespace dataset { -APP_ERROR DvppAdjustBrightness(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - MS_LOG(DEBUG) << "Begin execute adjust brightness."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppAdjustBrightnessGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), factor, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustBrightnessGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - // call DVPP step3 - ret = acldvppAdjustBrightness( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAdjustBrightness( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustBrightness failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_BRIGHTNESS_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAdjustContrast(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - MS_LOG(WARNING) << "Begin execute adjust contrast."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // the channel should be equal to 3 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3."; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppAdjustContrastGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), factor, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustContrastGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - // call DVPP step3 - ret = acldvppAdjustContrast( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAdjustContrast( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustContrast failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_CONTRAST_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAdjustHue(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - MS_LOG(WARNING) << "Begin execute adjust hue."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[3] != 3 && input->GetShape().AsVector()[3] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppAdjustHueGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), factor, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustHueGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - // call DVPP step3 - ret = acldvppAdjustHue( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAdjustHue( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustHue failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_HUE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAdjustSaturation(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - MS_LOG(WARNING) << "Begin execute adjust saturation."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[3] != 3 && input->GetShape().AsVector()[3] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppAdjustSaturationGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), factor, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustSaturationGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - // call DVPP step3 - ret = acldvppAdjustSaturation( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAdjustSaturation( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustSaturation failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_SATURATION_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAdjustSharpness(const std::shared_ptr &input, - std::shared_ptr *output, float factor) { - MS_LOG(WARNING) << "Begin execute adjust sharpness."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kChannelIndexNHWC && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppAdjustSharpnessGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), factor, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustSharpnessGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - // call DVPP step3 - ret = acldvppAdjustSharpness( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAdjustSharpness( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAdjustSharpness failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ADJUST_SHARPNESS_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAffine(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &matrix, - uint32_t interpolation_mode, uint32_t padding_mode, const std::vector &fill) { - MS_LOG(DEBUG) << "Begin execute dvpp affine."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // create the output shape and type - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create matrix vector - aclFloatArray *acl_matrix = aclCreateFloatArray(matrix.data(), matrix.size()); - if (acl_matrix == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // create fill vector - aclFloatArray *acl_fill = aclCreateFloatArray(fill.data(), fill.size()); - if (acl_fill == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_matrix))) { - MS_LOG(ERROR) << "Add float array [acl_matrix] to the input failed"; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_fill))) { - MS_LOG(ERROR) << "Add float array [acl_fill] to the input failed"; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppWarpAffineGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), acl_matrix, interpolation_mode, padding_mode, acl_fill, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppWarpAffineGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - // call DVPP step3 - ret = acldvppWarpAffine( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_AFFINE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppWarpAffine( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppWarpAffine failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_AFFINE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppAutoContrast(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &cutoff, - const std::vector &ignore) { - MS_LOG(DEBUG) << "Begin execute dvpp autocontrast."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kChannelIndexNHWC && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // create the output shape and type - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create ignore vector - std::vector ignore_cast{ignore.begin(), ignore.end()}; - aclIntArray *acl_ignore = aclCreateIntArray(ignore_cast.data(), ignore_cast.size()); - if (acl_ignore == nullptr) { - MS_LOG(ERROR) << "Call aclCreateIntArray failed."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // create cutoff vector - aclFloatArray *acl_cutoff = aclCreateFloatArray(cutoff.data(), cutoff.size()); - if (acl_cutoff == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenIntArrayMemory(reinterpret_cast(acl_ignore))) { - MS_LOG(ERROR) << "Add int array [acl_ignore] to the input failed"; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_cutoff))) { - MS_LOG(ERROR) << "Add float array [acl_cutoff] to the input failed"; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppAutoContrastGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), acl_cutoff, acl_ignore, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAutoContrastGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // call DVPP step3 - ret = acldvppAutoContrast( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppAutoContrast( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppAutoContrast failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR GetDVPPConvertMode(ConvertMode convertMode, acldvppConvertMode *dvpp_mode) { - switch (convertMode) { - case ConvertMode::COLOR_BGR2BGRA: // COLOR_BGR2BGRA=COLOR_RGB2RGBA - *dvpp_mode = acldvppConvertMode::COLOR_BGR2BGRA; // dvpp alpha channel COLOR_BGR2BGRA/COLOR_RGB2RGBA - break; - case ConvertMode::COLOR_BGRA2BGR: // COLOR_BGRA2BGR=COLOR_RGBA2RGB - *dvpp_mode = acldvppConvertMode::COLOR_BGRA2BGR; // dvpp alpha channel COLOR_BGRA2BGR/COLOR_RGBA2RGB - break; - case ConvertMode::COLOR_BGR2RGBA: // COLOR_BGR2RGBA=COLOR_RGB2BGRA - *dvpp_mode = acldvppConvertMode::COLOR_BGR2RGBA; // dvpp COLOR_BGR2RGBA/COLOR_RGB2BGRA - break; - case ConvertMode::COLOR_RGBA2BGR: // COLOR_RGBA2BGR=COLOR_BGRA2RGB - *dvpp_mode = acldvppConvertMode::COLOR_RGBA2BGR; // dvpp COLOR_RGBA2BGR/COLOR_BGRA2RGB - break; - case ConvertMode::COLOR_BGR2RGB: // COLOR_BGR2RGB=COLOR_RGB2BGR - *dvpp_mode = acldvppConvertMode::COLOR_BGR2RGB; // dvpp COLOR_BGR2RGB/COLOR_RGB2BGR - break; - case ConvertMode::COLOR_BGRA2RGBA: // COLOR_BGRA2RGBA=COLOR_RGBA2BGRA - *dvpp_mode = acldvppConvertMode::COLOR_BGRA2RGBA; // dvpp COLOR_BGRA2RGBA/COLOR_RGBA2BGRA - break; - case ConvertMode::COLOR_BGR2GRAY: - *dvpp_mode = acldvppConvertMode::COLOR_BGR2GRAY; // dvpp COLOR_BGR2GRAY - break; - case ConvertMode::COLOR_RGB2GRAY: - *dvpp_mode = acldvppConvertMode::COLOR_RGB2GRAY; // dvpp COLOR_RGB2GRAY - break; - case ConvertMode::COLOR_GRAY2BGR: // COLOR_GRAY2BGR=COLOR_GRAY2RGB - *dvpp_mode = acldvppConvertMode::COLOR_GRAY2BGR; // dvpp COLOR_GRAY2BGR/COLOR_GRAY2RGB - break; - case ConvertMode::COLOR_GRAY2BGRA: // COLOR_GRAY2BGRA=COLOR_GRAY2RGBA - *dvpp_mode = acldvppConvertMode::COLOR_GRAY2BGRA; // dvpp COLOR_GRAY2BGRA/COLOR_GRAY2RGBA - break; - case ConvertMode::COLOR_BGRA2GRAY: - *dvpp_mode = acldvppConvertMode::COLOR_BGRA2GRAY; // dvpp COLOR_BGRA2GRAY - break; - case ConvertMode::COLOR_RGBA2GRAY: - *dvpp_mode = acldvppConvertMode::COLOR_RGBA2GRAY; // dvpp COLOR_RGBA2GRAY - break; - default: - MS_LOG(ERROR) << "The current ConvertMode is not supported by DVPP. It is " + - std::to_string(static_cast(convertMode)); - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - return APP_ERR_OK; -} - -APP_ERROR DvppConvertColor(const std::shared_ptr &input, - std::shared_ptr *output, ConvertMode convertMode) { - MS_LOG(DEBUG) << "Begin execute dvpp convertcolor."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - // the input should be NHWC - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - if (input->GetShape().AsVector()[kChannelIndexNHWC] != 1 && - input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMaxImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 1 or 3 or 4."; // C == 1 or 3 or 4 - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC."; // N == 1 - return APP_ERR_DVPP_AUTO_CONTRAST_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - // create the output shape and type - std::vector node; - std::vector one_channels = {ConvertMode::COLOR_BGR2GRAY, ConvertMode::COLOR_RGB2GRAY, - ConvertMode::COLOR_BGRA2GRAY, ConvertMode::COLOR_RGBA2GRAY}; - std::vector three_channels = { - ConvertMode::COLOR_BGRA2BGR, ConvertMode::COLOR_RGBA2RGB, ConvertMode::COLOR_RGBA2BGR, ConvertMode::COLOR_BGRA2RGB, - ConvertMode::COLOR_BGR2RGB, ConvertMode::COLOR_RGB2BGR, ConvertMode::COLOR_GRAY2BGR, ConvertMode::COLOR_GRAY2RGB}; - std::vector four_channels = {ConvertMode::COLOR_BGR2BGRA, ConvertMode::COLOR_RGB2RGBA, - ConvertMode::COLOR_BGR2RGBA, ConvertMode::COLOR_RGB2BGRA, - ConvertMode::COLOR_BGRA2RGBA, ConvertMode::COLOR_RGBA2BGRA, - ConvertMode::COLOR_GRAY2BGRA, ConvertMode::COLOR_GRAY2RGBA}; - if (std::find(three_channels.begin(), three_channels.end(), convertMode) != three_channels.end()) { - node = {input->GetShape().AsVector()[0], input->GetShape().AsVector()[kHeightIndexNHWC], - input->GetShape().AsVector()[kWidthIndexNHWC], kDefaultImageChannel}; - } else if (std::find(four_channels.begin(), four_channels.end(), convertMode) != four_channels.end()) { - node = {input->GetShape().AsVector()[0], input->GetShape().AsVector()[kHeightIndexNHWC], - input->GetShape().AsVector()[kWidthIndexNHWC], kMaxImageChannel}; - } else if (std::find(one_channels.begin(), one_channels.end(), convertMode) != one_channels.end()) { - node = {input->GetShape().AsVector()[0], input->GetShape().AsVector()[kHeightIndexNHWC], - input->GetShape().AsVector()[kWidthIndexNHWC], kMinImageChannel}; - } else { - MS_LOG(ERROR) << "The mode of image channel conversion must be in ConvertMode, which mainly includes " - "conversion between RGB, BGR, GRAY, RGBA etc."; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - TensorShape shape = TensorShape(node); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - std::vector channels = {1, 3, 4}; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true, channels) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - if (input->GetType() != DataType::DE_UINT8 && - (convertMode == ConvertMode::COLOR_BGR2BGRA && convertMode == ConvertMode::COLOR_BGRA2BGR && - convertMode == ConvertMode::COLOR_BGR2RGBA && convertMode == ConvertMode::COLOR_RGBA2BGR)) { - std::string err_msg = - "The conversion mode of alpha channel [COLOR_BGR2BGRA, COLOR_RGB2RGBA, COLOR_BGRA2BGR, COLOR_RGBA2RGB, " - "COLOR_BGR2RGBA, COLOR_RGB2BGRA, COLOR_RGBA2BGR, COLOR_BGRA2RGB] only " - "supports uint8 type input"; - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - // convert ConvertMode mode to DVPP mode - acldvppConvertMode dvpp_convert_mode; - GetDVPPConvertMode(convertMode, &dvpp_convert_mode); - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppConvertColorGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), dvpp_convert_mode, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppConvertColorGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - MS_LOG(ERROR) << "Call acldvppConvertColorGetWorkspaceSize failed, error msg: " - << CALL_ASCEND_API(aclGetRecentErrMsg); - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - // call DVPP step3 - ret = acldvppConvertColor( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppConvertColor( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppConvertColor failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_CONVERT_COLOR_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppCrop(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width) { - MS_LOG(DEBUG) << "Begin execute dvpp crop."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_CROP_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_CROP_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_CROP_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_CROP_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_CROP_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape{static_cast(input->GetShape()[0]), height, width, - static_cast(input->GetShape()[kChannelIndexHWC + 1])}; - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_CROP_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppCropGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), top, left, height, - width, reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppCropGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_CROP_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_CROP_FAIL; - } - - // call DVPP step3 - ret = acldvppCrop( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_CROP_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppCrop( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppCrop failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_CROP_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppErase(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width, const std::vector &value) { - MS_LOG(DEBUG) << "Begin execute dvpp erase."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ERASE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ERASE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ERASE_FAIL; - } - - if (input->GetType() == DataType::DE_FLOAT32) { - for (const float &val : value) { - if (val > 1.) { - MS_LOG(ERROR) << "When The input data is float32, the range of value should be [0, 1]"; - return APP_ERR_DVPP_ERASE_FAIL; - } - } - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create fill vector - if (input->GetShape().AsVector()[kChannelIndexNHWC] != value.size()) { - MS_LOG(ERROR) << "The length of value should be the same as the value of channel"; - return APP_ERR_DVPP_ERASE_FAIL; - } - aclFloatArray *acl_value = aclCreateFloatArray(value.data(), value.size()); - if (acl_value == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_value))) { - MS_LOG(ERROR) << "Add float array [acl_value] to the input failed"; - return APP_ERR_DVPP_ERASE_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - - uint32_t image_h = input->GetShape().AsVector()[kHeightIndexNHWC]; - uint32_t image_w = input->GetShape().AsVector()[kWidthIndexNHWC]; - uint32_t h_start = top; - uint32_t w_start = left; - h_start = (h_start < 0) ? 0 : h_start; - w_start = (w_start < 0) ? 0 : w_start; - - uint32_t max_width = (w_start + width > image_w) ? static_cast(image_w) : w_start + width; - uint32_t max_height = (h_start + height > image_h) ? static_cast(image_h) : h_start + height; - uint32_t true_width = max_width - w_start; - uint32_t true_height = max_height - h_start; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppEraseGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), top, left, true_height, true_width, acl_value, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppEraseGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ERASE_FAIL; - } - - // call DVPP step3 - ret = acldvppErase( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ERASE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppErase( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppErase failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ERASE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppDecode(const std::shared_ptr &input, - std::shared_ptr *output) { - MS_LOG(DEBUG) << "Begin execute dvpp decode."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - - // the output DeviceTensorAscend910B had been created in npu_map_job.cc, - // because we need get image height and width from the JPEG header - std::shared_ptr device_tensor = *output; - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppDecodeJpegGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), 3, true, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppDecodeJpegGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - - // call DVPP step3 - ret = acldvppDecodeJpeg( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppDecodeJpeg( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppDecodeJpeg failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_JPEG_DECODE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppEqualize(const std::shared_ptr &input, - std::shared_ptr *output) { - MS_LOG(DEBUG) << "Begin execute dvpp equalize."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kChannelIndexNHWC && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // the type is uint8 - if (input->GetType() != DataType::DE_UINT8) { - MS_LOG(ERROR) << "The input data is not uint8"; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // create the output shape and type - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppEqualizeGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppEqualizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - // call DVPP step3 - ret = acldvppEqualize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppEqualize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppEqualize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_EQUALIZE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppGaussianBlur(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &kernel_size, - const std::vector &sigma, uint32_t padding_mode) { - MS_LOG(DEBUG) << "Begin execute dvpp GaussianBlur."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // create the output shape and type - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create matrix vector - aclIntArray *acl_kernel_size = aclCreateIntArray(kernel_size.data(), kernel_size.size()); - if (acl_kernel_size == nullptr) { - MS_LOG(ERROR) << "Call aclCreateIntArray failed."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // create fill vector - aclFloatArray *acl_sigma = aclCreateFloatArray(sigma.data(), sigma.size()); - if (acl_sigma == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenIntArrayMemory(reinterpret_cast(acl_kernel_size))) { - MS_LOG(ERROR) << "Add float array [acl_kernel_size] to the input failed"; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_sigma))) { - MS_LOG(ERROR) << "Add float array [acl_sigma] to the input failed"; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppGaussianBlurGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), acl_kernel_size, acl_sigma, padding_mode, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppGaussianBlurGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - // call DVPP step3 - ret = acldvppGaussianBlur( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppGaussianBlur( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppGaussianBlur failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_GAUSSIAN_BLUR_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppHorizontalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - MS_LOG(DEBUG) << "Begin execute dvpp horizontal flip."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppHorizontalFlipGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppHorizontalFlipGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - // call DVPP step3 - ret = acldvppHorizontalFlip( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppHorizontalFlip( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppHorizontalFlip failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_HORIZONTAL_FLIP_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppInvert(const std::shared_ptr &input, - std::shared_ptr *output) { - MS_LOG(DEBUG) << "Begin execute dvpp invert."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_INVERT_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_INVERT_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_INVERT_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_INVERT_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8) { - MS_LOG(ERROR) << "The input data is not uint8"; - return APP_ERR_DVPP_INVERT_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_INVERT_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppInvertGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppInvertGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_INVERT_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_INVERT_FAIL; - } - - // call DVPP step3 - ret = acldvppInvert( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_INVERT_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppInvert( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppInvert failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_INVERT_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppNormalize(const std::shared_ptr &input, - std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc) { - MS_LOG(DEBUG) << "Begin execute dvpp normalize."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - if (!is_hwc) { - if (input->GetShape().AsVector()[1] != kDefaultImageChannel && input->GetShape().AsVector()[1] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; // C == 3 or 1 - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // the channel should be equal to the size of mean - if (mean.size() != std.size() || std.size() != input->GetShape().AsVector()[1]) { - MS_LOG(ERROR) << "The channel is not equal to the size of mean or std."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - } else { - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; // C == 3 or 1 - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // the channel should be equal to the size of mean - if (mean.size() != std.size() || std.size() != input->GetShape().AsVector()[kChannelIndexNHWC]) { - MS_LOG(ERROR) << "The channel is not equal to the size of mean or std."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type(DataType::DE_FLOAT32); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, is_hwc) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // create aclFloatArray for mean - aclFloatArray *acl_mean = aclCreateFloatArray(mean.data(), mean.size()); - if (acl_mean == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // create aclFloatArray for std - aclFloatArray *acl_std = aclCreateFloatArray(std.data(), std.size()); - if (acl_std == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_mean))) { - MS_LOG(ERROR) << "Add float array [acl_mean] to the input failed"; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_std))) { - MS_LOG(ERROR) << "Add float array [acl_std] to the input failed"; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppNormalizeGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), acl_mean, - acl_std, reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call aclvisionNormalizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - // call DVPP step3 - ret = acldvppNormalize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppNormalize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppNormalize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_NORMALIZE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppPad(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &padding, uint32_t padding_mode, const std::vector &fill) { - MS_LOG(DEBUG) << "Begin execute dvpp Pad."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_PAD_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_PAD_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_PAD_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_PAD_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_PAD_FAIL; - } - - // create the output shape and type - TensorShape input_shape = input->GetShape(); - int32_t left = padding[0]; - int32_t top = padding[1]; - int32_t right = padding[2]; - int32_t bottom = padding[3]; - TensorShape output_shape = - TensorShape(std::vector{input_shape[0], input_shape[kHeightIndexNHWC] + top + bottom, - input_shape[kWidthIndexNHWC] + left + right, input_shape[kChannelIndexNHWC]}); - DataType type = input->GetType(); - - // create pad vector - aclIntArray *acl_padding = aclCreateIntArray(padding.data(), padding.size()); - if (acl_padding == nullptr) { - MS_LOG(ERROR) << "Call aclCreateIntArray failed."; - return APP_ERR_DVPP_PAD_FAIL; - } - - // create fill vector - aclFloatArray *acl_fill = aclCreateFloatArray(fill.data(), fill.size()); - if (acl_fill == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_PAD_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenIntArrayMemory(reinterpret_cast(acl_padding))) { - MS_LOG(ERROR) << "Add int array [acl_padding] to the input failed"; - return APP_ERR_DVPP_PAD_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(acl_fill))) { - MS_LOG(ERROR) << "Add float array [acl_fill] to the input failed"; - return APP_ERR_DVPP_PAD_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(output_shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_PAD_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppPadGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), acl_padding, padding_mode, acl_fill, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppPadGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_PAD_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_PAD_FAIL; - } - - // call DVPP step3 - ret = acldvppPad( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_PAD_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppPad( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppPad failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_PAD_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppPerspective(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation) { - MS_LOG(DEBUG) << "Begin execute dvpp Perspective."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != kMinImageChannel) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create transform matrix - // Get Point - const int kListSize = 4; - cv::Point2f cv_src_point[kListSize]; - cv::Point2f cv_dst_point[kListSize]; - for (int i = 0; i < kListSize; i++) { - cv_src_point[i] = cv::Point2f(static_cast(start_points[i][0]), static_cast(start_points[i][1])); - cv_dst_point[i] = cv::Point2f(static_cast(end_points[i][0]), static_cast(end_points[i][1])); - } - - // Get transform matrix by cv::getPerspectiveTransform function - cv::Mat M = cv::getPerspectiveTransform(cv_src_point, cv_dst_point, cv::DECOMP_LU); - cv::Mat input_matrix; - cv::invert(M, input_matrix); - const int kMatrixSize = 3; - std::vector transform_matrix; - for (int i = 0; i < kMatrixSize; i++) { - double *data = input_matrix.ptr(i); - for (int j = 0; j < kMatrixSize; j++) { - transform_matrix.push_back(static_cast(data[j])); - } - } - - aclFloatArray *matrix = aclCreateFloatArray(transform_matrix.data(), transform_matrix.size()); - if (matrix == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // get pad data - std::vector fill_data = {0.0, 0.0, 0.0}; - auto *fill = aclCreateFloatArray(fill_data.data(), fill_data.size()); - if (fill == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(matrix))) { - MS_LOG(ERROR) << "Add float array [matrix] to the input failed"; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(fill))) { - MS_LOG(ERROR) << "Add float array [fill] to the input failed"; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // convert InterpolationMode mode to DVPP mode - auto dvpp_interpolation_mode = GetDVPPInterpolationMode(interpolation); - if (dvpp_interpolation_mode == kInvalidInterpolationMode) { - std::string err_msg = "The current InterpolationMode is not supported by DVPP. It is " + - std::to_string(static_cast(interpolation)); - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppWarpPerspectiveGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), matrix, dvpp_interpolation_mode, 0, fill, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppWarpPerspectiveGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - // call DVPP step3 - ret = acldvppWarpPerspective( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppWarpPerspective( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppWarpPerspective failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_PERSPECTIVE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppPosterize(const std::shared_ptr &input, - std::shared_ptr *output, uint8_t bits) { - MS_LOG(DEBUG) << "Begin execute dvpp posterize flip."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[3] != 3 && input->GetShape().AsVector()[3] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8) { - MS_LOG(ERROR) << "The input data is not uint8."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppPosterizeGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), static_cast(bits), - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppPosterizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - // call DVPP step3 - ret = acldvppPosterize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppPosterize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppPosterize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_POSTERIZE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppResize(const std::shared_ptr &input, - std::shared_ptr *output, int32_t output_height, int32_t output_width, - double fx, double fy, InterpolationMode mode) { - MS_LOG(DEBUG) << "Begin execute dvpp resize."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // the input should be HWC - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_RESIZE_FAIL; - } - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; // C == 3 or 1 - return APP_ERR_DVPP_RESIZE_FAIL; - } - - if (output_height == 0 || output_width == 0) { - std::string err_msg = "DvppResize: the input value of 'resize' is invalid, width or height is zero."; - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // create the output shape and type, it's HWC - TensorShape shape{static_cast(input->GetShape()[0]), output_height, output_width, - static_cast(input->GetShape()[kChannelIndexHWC + 1])}; - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // convert InterpolationMode mode to DVPP mode - auto dvpp_interpolation_mode = GetDVPPInterpolationMode(mode); - if (dvpp_interpolation_mode == kInvalidInterpolationMode) { - std::string err_msg = - "The current InterpolationMode is not supported by DVPP. It is " + std::to_string(static_cast(mode)); - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppResizeGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), dvpp_interpolation_mode, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppResizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - // call DVPP step3 - ret = acldvppResize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_RESIZE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppResize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call aclvisionResize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_RESIZE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppResizedCrop(const std::shared_ptr &input, - std::shared_ptr *output, int32_t top, int32_t left, int32_t height, - int32_t width, int32_t output_height, int32_t output_width, InterpolationMode mode) { - MS_LOG(DEBUG) << "Begin execute dvpp crop and resize."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // the input should be HWC - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; // C == 3 or 1 - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - const uint32_t kResizeShapeLimits = 1000; - // resize image too large or too small, 1000 is arbitrarily chosen here to prevent open cv from segmentation fault - if ((std::numeric_limits::max() / kResizeShapeLimits) <= input->GetShape().AsVector()[kHeightIndexNHWC]) { - MS_LOG(ERROR) << "DvppResizedCrop: in_image rows out of bounds."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - if ((std::numeric_limits::max() / kResizeShapeLimits) <= input->GetShape().AsVector()[kWidthIndexNHWC]) { - MS_LOG(ERROR) << "DvppResizedCrop: in_image cols out of bounds."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - if (output_height > input->GetShape().AsVector()[1] * kResizeShapeLimits || - output_width > input->GetShape().AsVector()[kWidthIndexNHWC] * kResizeShapeLimits) { - std::string err_msg = - "DvppResizedCrop: the resizing width or height is too big, it's 1000 times bigger than the original image, got " - "output " - "height: " + - std::to_string(output_height) + ", width: " + std::to_string(output_width) + - ", and original image size:" + std::to_string(input->GetShape().AsVector()[1]) + ", " + - std::to_string(input->GetShape().AsVector()[kWidthIndexNHWC]); - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - if (output_height == 0 || output_width == 0) { - std::string err_msg = "DvppResizedCrop: the input value of 'resize' is invalid, width or height is zero."; - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // create the output shape and type, it's HWC - TensorShape shape{static_cast(input->GetShape()[0]), output_height, output_width, - static_cast(input->GetShape()[kChannelIndexHWC + 1])}; - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // convert InterpolationMode mode to DVPP mode - auto dvpp_interpolation_mode = GetDVPPInterpolationMode(mode); - if (dvpp_interpolation_mode == kInvalidInterpolationMode) { - std::string err_msg = - "The current InterpolationMode is not supported by DVPP. It is " + std::to_string(static_cast(mode)); - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // call DVPP step1 - std::vector size_data = {output_height, output_width}; - aclIntArray *size = aclCreateIntArray(size_data.data(), size_data.size()); - if (size == nullptr) { - MS_LOG(ERROR) << "Call aclCreateIntArray failed."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenIntArrayMemory(reinterpret_cast(size))) { - MS_LOG(ERROR) << "Add int array [size] to the input failed"; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppCropAndResizeGetWorkspaceSize( - reinterpret_cast(input->GetDeviceTensor()), top, left, height, width, size, dvpp_interpolation_mode, - reinterpret_cast(device_tensor->GetDeviceTensor()), &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppCropAndResizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - // call DVPP step3 - ret = acldvppCropAndResize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppCropAndResize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppCropAndResize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_RESIZED_CROP_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppSolarize(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &threshold) { - MS_LOG(DEBUG) << "Begin execute solarize."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[kChannelIndexNHWC] != kDefaultImageChannel && - input->GetShape().AsVector()[kChannelIndexNHWC] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8) { - MS_LOG(ERROR) << "The input data is not uint8"; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create float array - aclFloatArray *dvpp_threshold = aclCreateFloatArray(threshold.data(), threshold.size()); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - auto ret = acldvppSolarizeGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), dvpp_threshold, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppSolarizeGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - // call DVPP step3 - ret = acldvppSolarize( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppSolarize( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppSolarize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_SOLARIZE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppVerticalFlip(const std::shared_ptr &input, - std::shared_ptr *output) { - MS_LOG(DEBUG) << "Begin execute dvpp vertical flip."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[3] != 3 && input->GetShape().AsVector()[3] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - DataType type = input->GetType(); - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - aclOpExecutor *executor; - // decode output C is 3 - // don't recovery truncate - auto ret = acldvppVerticalFlipGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppVerticalFlipGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - // call DVPP step3 - ret = acldvppVerticalFlip( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppVerticalFlip( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppVerticalFlip failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_VERTICAL_FLIP_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -APP_ERROR DvppRotate(const std::shared_ptr &input, - std::shared_ptr *output, float degrees, InterpolationMode mode, - bool expand, const std::vector ¢er, const std::vector &fill) { - MS_LOG(DEBUG) << "Begin execute dvpp rotate."; - if (input == nullptr || output == nullptr) { - MS_LOG(ERROR) << "The input or output is nullptr."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // the input should be 1HWC or 1CHW - if (input->GetShape().Rank() != kNHWCImageRank) { - MS_LOG(ERROR) << "The input data's dims is not 4."; // NHWC - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // the channel should be equal to 3 or 1 - if (input->GetShape().AsVector()[3] != 3 && input->GetShape().AsVector()[3] != 1) { - MS_LOG(ERROR) << "The input data's channel is not 3 or 1."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - if (input->GetShape().AsVector()[0] != 1) { - MS_LOG(ERROR) << "The input data is not 1HWC or 1CHW."; // N == 1 - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // the type is uint8 / float - if (input->GetType() != DataType::DE_UINT8 && input->GetType() != DataType::DE_FLOAT32) { - MS_LOG(ERROR) << "The input data is not uint8 or float32"; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - std::vector center_data; - if (center.empty()) { - constexpr float kHalf = 0.5; - auto input_h = (static_cast(input->GetShape().AsVector()[kHeightIndexNHWC]) - 1.0F) * kHalf; - auto input_w = (static_cast(input->GetShape().AsVector()[kWidthIndexNHWC]) - 1.0F) * kHalf; - center_data = {static_cast(input_w), static_cast(input_h)}; - } else { - center_data = {static_cast(center[0]), static_cast(center[1])}; - } - - // create the output shape and type, it's 1HWC or 1CHW - TensorShape shape = input->GetShape(); - if (expand) { - double radian = degrees * M_PI / 180.0; - double precision = std::pow(10, 15); // 10的15次用于保留15位小数 - std::vector matrix = { - std::round(std::cos(radian) * precision) / precision, std::round(std::sin(radian) * precision) / precision, 0.0, - std::round(-std::sin(radian) * precision) / precision, std::round(std::cos(radian) * precision) / precision, 0.0, - }; - std::pair rotnCenter = { - input->GetShape().AsVector()[kWidthIndexNHWC] / 2.0, - input->GetShape().AsVector()[kHeightIndexNHWC] / 2.0 // 除2,求中心点 - }; - auto tmp = std::make_pair( - matrix[0] * (-rotnCenter.first) + matrix[1] * (-rotnCenter.second) + matrix[2], // 0, 1, 2代表2x3矩阵的第一行元素 - matrix[3] * (-rotnCenter.first) + matrix[4] * (-rotnCenter.second) + - matrix[5]); // 3, 4, 5代表2x3矩阵的第二行元素 - matrix[2] = tmp.first; // 下标2是x轴的平移量 - matrix[5] = tmp.second; // 下标5是y轴的平移量 - - matrix[2] += rotnCenter.first; // 下标2是x轴的平移量 - matrix[5] += rotnCenter.second; // 下标5是y轴的平移量 - - std::vector> points = { - {0, 0}, - {input->GetShape().AsVector()[kWidthIndexNHWC], 0}, - {input->GetShape().AsVector()[kWidthIndexNHWC], input->GetShape().AsVector()[kHeightIndexNHWC]}, - {0, input->GetShape().AsVector()[kHeightIndexNHWC]}, - }; - auto f = [&matrix](std::pair &p) { - p = std::make_pair(matrix[0] * (p.first) + matrix[1] * (p.second) + matrix[2], // 0, 1, 2代表2x3矩阵的第一行元素 - matrix[3] * (p.first) + matrix[4] * (p.second) + matrix[5]); // 3, 4, 5代表2x3矩阵的第二行元素 - return p; - }; - std::transform(points.begin(), points.end(), points.begin(), f); - - auto xComp = [](auto &p0, auto &p1) { return p0.first < p1.first; }; - auto xMax = std::max_element(points.cbegin(), points.cend(), xComp); - auto xMin = std::min_element(points.cbegin(), points.cend(), xComp); - - auto yComp = [](auto &p0, auto &p1) { return p0.second < p1.second; }; - auto yMax = std::max_element(points.cbegin(), points.cend(), yComp); - auto yMin = std::min_element(points.cbegin(), points.cend(), yComp); - - auto weight_out = std::ceil(xMax->first) - std::floor(xMin->first); - auto height_out = std::ceil(yMax->second) - std::floor(yMin->second); - shape = TensorShape({input->GetShape().AsVector()[0], static_cast(height_out), - static_cast(weight_out), input->GetShape().AsVector()[kChannelIndexNHWC]}); - } - DataType type = input->GetType(); - - // convert InterpolationMode mode to DVPP mode - auto dvpp_interpolation_mode = GetDVPPRotateMode(mode); - if (dvpp_interpolation_mode == kInvalidRotateMode) { - std::string err_msg = - "The current InterpolationMode is not supported by DVPP. It is " + std::to_string(static_cast(mode)); - MS_LOG(ERROR) << err_msg; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // convert to the dvpp type - aclIntArray *dvpp_center = aclCreateIntArray(center_data.data(), center_data.size()); - aclFloatArray *dvpp_fill = aclCreateFloatArray(fill.data(), fill.size()); - - if (dvpp_center == nullptr) { - MS_LOG(ERROR) << "Call aclCreateIntArray failed."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - if (dvpp_fill == nullptr) { - MS_LOG(ERROR) << "Call aclCreateFloatArray failed."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // the memory will be released when the map / executor is finished - if (!input->AddMaintenIntArrayMemory(reinterpret_cast(dvpp_center))) { - MS_LOG(ERROR) << "Add int array [dvpp_center] to the input failed"; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - if (!input->AddMaintenFloatArrayMemory(reinterpret_cast(dvpp_fill))) { - MS_LOG(ERROR) << "Add float array [dvpp_fill] to the input failed"; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // create output DeviceTensorAscend910B - std::shared_ptr device_tensor = nullptr; - if (DeviceTensorAscend910B::CreateDeviceTensor(shape, type, input->GetDeviceContext(), input->GetStreamID(), - &device_tensor, true) != Status::OK()) { - MS_LOG(ERROR) << "Create output device tensor failed."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // call DVPP step1 - uint64_t workspace_size = 0; - uint32_t paddingMode = 0; - aclOpExecutor *executor; - auto ret = acldvppRotateGetWorkspaceSize(reinterpret_cast(input->GetDeviceTensor()), degrees, - dvpp_interpolation_mode, expand, dvpp_center, paddingMode, dvpp_fill, - reinterpret_cast(device_tensor->GetDeviceTensor()), - &workspace_size, &executor); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppRotateGetWorkspaceSize failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // call DVPP step2 - void *workspace_addr = nullptr; - if (workspace_size > 0) { - // create new device address for data copy - workspace_addr = input->GetDeviceContext()->device_res_manager_->AllocateMemory(workspace_size); - if (workspace_addr == nullptr) { - MS_LOG(ERROR) << "Allocate dynamic workspace memory failed"; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - // call DVPP step3 - ret = acldvppRotate( - workspace_addr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - - // use the input to hold the workspace and release it when the executor / npu_map_job finish - if (!input->AddWorkSpace(workspace_addr)) { - MS_LOG(ERROR) << "Add workspace to the input failed"; - return APP_ERR_DVPP_ROTATE_FAIL; - } - } else { - // call DVPP step3 - ret = acldvppRotate( - nullptr, workspace_size, executor, - static_cast(input->GetDeviceContext()->device_res_manager_->GetStream(input->GetStreamID()))); - } - - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Call acldvppRotate failed, error code: " + std::to_string(ret) + "."; - return APP_ERR_DVPP_ROTATE_FAIL; - } - - *output = std::move(device_tensor); // currently the data is still in device - return APP_ERR_OK; -} - -// acl -APP_ERROR GetSocName(std::string *soc_name) { - const char *soc_name_c = aclrtGetSocName(); - if (soc_name_c == nullptr) { - *soc_name = ""; - } - *soc_name = std::string(soc_name_c); - return APP_ERR_OK; -} - -APP_ERROR CreateAclTensor(const int64_t *view_dims, uint64_t view_dims_num, mindspore::TypeId data_type, - const int64_t *stride, int64_t offset, const int64_t *storage_dims, uint64_t storage_dims_num, - void *tensor_data, bool is_hwc, void **acl_tensor) { - if (view_dims == nullptr) { - MS_LOG(ERROR) << "Input view_dims is null."; - return APP_ERR_COMM_FAILURE; - } - if (stride == nullptr) { - MS_LOG(ERROR) << "Input stride is null."; - return APP_ERR_COMM_FAILURE; - } - if (storage_dims == nullptr) { - MS_LOG(ERROR) << "Input storage_dims is null."; - return APP_ERR_COMM_FAILURE; - } - if (tensor_data == nullptr) { - MS_LOG(ERROR) << "Input tensor_data is null."; - return APP_ERR_COMM_FAILURE; - } - if (acl_tensor == nullptr) { - MS_LOG(ERROR) << "Input acl_tensor is null."; - return APP_ERR_COMM_FAILURE; - } - - aclDataType acl_data_type = aclDataType::ACL_DT_UNDEFINED; - - switch (data_type) { - case kNumberTypeBool: - acl_data_type = aclDataType::ACL_BOOL; - break; - case kNumberTypeInt8: - acl_data_type = aclDataType::ACL_INT8; - break; - case kNumberTypeUInt8: - acl_data_type = aclDataType::ACL_UINT8; - break; - case kNumberTypeInt16: - acl_data_type = aclDataType::ACL_INT16; - break; - case kNumberTypeUInt16: - acl_data_type = aclDataType::ACL_UINT16; - break; - case kNumberTypeInt32: - acl_data_type = aclDataType::ACL_INT32; - break; - case kNumberTypeUInt32: - acl_data_type = aclDataType::ACL_UINT32; - break; - case kNumberTypeInt64: - acl_data_type = aclDataType::ACL_INT64; - break; - case kNumberTypeUInt64: - acl_data_type = aclDataType::ACL_UINT64; - break; - case kNumberTypeFloat16: - acl_data_type = aclDataType::ACL_FLOAT16; - break; - case kNumberTypeBFloat16: - acl_data_type = aclDataType::ACL_BF16; - break; - case kNumberTypeFloat32: - acl_data_type = aclDataType::ACL_FLOAT; - break; - case kNumberTypeFloat64: - acl_data_type = aclDataType::ACL_DOUBLE; - break; - case kObjectTypeString: - acl_data_type = aclDataType::ACL_STRING; - break; - default: - acl_data_type = aclDataType::ACL_DT_UNDEFINED; - break; - } - - if (acl_data_type == aclDataType::ACL_DT_UNDEFINED) { - MS_LOG(ERROR) << "Invalid data type: " << data_type << ", which couldn't be converted to aclDataType."; - return APP_ERR_COMM_FAILURE; - } - - if (is_hwc) { - *acl_tensor = reinterpret_cast(aclCreateTensor(view_dims, view_dims_num, acl_data_type, stride, offset, - aclFormat::ACL_FORMAT_NHWC, storage_dims, storage_dims_num, - tensor_data)); - } else { - *acl_tensor = reinterpret_cast(aclCreateTensor(view_dims, view_dims_num, acl_data_type, stride, offset, - aclFormat::ACL_FORMAT_NCHW, storage_dims, storage_dims_num, - tensor_data)); - } - return APP_ERR_OK; -} - -APP_ERROR DestroyTensor(void *tensor) { - if (aclDestroyTensor(reinterpret_cast(tensor)) != OK) { - MS_LOG(ERROR) << "Call aclDestroyTensor failed."; - return APP_ERR_DESTORY_TENSOR; - } - return APP_ERR_OK; -} - -APP_ERROR DestroyFloatArray(void *float_array) { - if (aclDestroyFloatArray(reinterpret_cast(float_array)) != OK) { - MS_LOG(ERROR) << "Call aclDestroyFloatArray failed."; - return APP_ERR_DESTORY_FLOAT_ARRAY; - } - return APP_ERR_OK; -} - -APP_ERROR DestroyIntArray(void *int_array) { - if (aclDestroyIntArray(reinterpret_cast(int_array)) != OK) { - MS_LOG(ERROR) << "Call aclDestroyIntArray failed."; - return APP_ERR_DESTORY_INT_ARRAY; - } - return APP_ERR_OK; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h deleted file mode 100644 index 7e242228f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_image_utils.h +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright 2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_IMAGE_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_IMAGE_UTILS_H_ - -#include - -#include -#include -#include -#include -#include -#include -#include -#if defined(_WIN32) || defined(_WIN64) -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#elif __APPLE__ -#include -#include -#endif -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ErrorCode.h" - -#include "acldvppop/acldvpp_base.h" - -namespace mindspore { -namespace dataset { -const int kInvalidInterpolationMode = 100; -const int kInvalidPaddingMode = 101; -const int kInvalidRotateMode = 102; -const int kInvalidConvertMode = 103; - -APP_ERROR GetDVPPConvertMode(ConvertMode convertMode, acldvppConvertMode *dvpp_mode); - -/// \brief Convert ConvertMode to dvpp mode -inline int GetDVPPConvertMode(ConvertMode convertMode) { - switch (convertMode) { - case ConvertMode::COLOR_BGR2BGRA: // COLOR_BGR2BGRA=COLOR_RGB2RGBA - return acldvppConvertMode::COLOR_BGR2BGRA; // dvpp alpha channel COLOR_BGR2BGRA/COLOR_RGB2RGBA - case ConvertMode::COLOR_BGRA2BGR: // COLOR_BGRA2BGR=COLOR_RGBA2RGB - return acldvppConvertMode::COLOR_BGRA2BGR; // dvpp alpha channel COLOR_BGRA2BGR/COLOR_RGBA2RGB - case ConvertMode::COLOR_BGR2RGBA: // COLOR_BGR2RGBA=COLOR_RGB2BGRA - return acldvppConvertMode::COLOR_BGR2RGBA; // dvpp COLOR_BGR2RGBA/COLOR_RGB2BGRA - case ConvertMode::COLOR_RGBA2BGR: // COLOR_RGBA2BGR=COLOR_BGRA2RGB - return acldvppConvertMode::COLOR_RGBA2BGR; // dvpp COLOR_RGBA2BGR/COLOR_BGRA2RGB - case ConvertMode::COLOR_BGR2RGB: // COLOR_BGR2RGB=COLOR_RGB2BGR - return acldvppConvertMode::COLOR_BGR2RGB; // dvpp COLOR_BGR2RGB/COLOR_RGB2BGR - case ConvertMode::COLOR_BGRA2RGBA: // COLOR_BGRA2RGBA=COLOR_RGBA2BGRA - return acldvppConvertMode::COLOR_BGRA2RGBA; // dvpp COLOR_BGRA2RGBA/COLOR_RGBA2BGRA - case ConvertMode::COLOR_BGR2GRAY: - return acldvppConvertMode::COLOR_BGR2GRAY; // dvpp COLOR_BGR2GRAY - case ConvertMode::COLOR_RGB2GRAY: - return acldvppConvertMode::COLOR_RGB2GRAY; // dvpp COLOR_RGB2GRAY - case ConvertMode::COLOR_GRAY2BGR: // COLOR_GRAY2BGR=COLOR_GRAY2RGB - return acldvppConvertMode::COLOR_GRAY2BGR; // dvpp COLOR_GRAY2BGR/COLOR_GRAY2RGB - case ConvertMode::COLOR_GRAY2BGRA: // COLOR_GRAY2BGRA=COLOR_GRAY2RGBA - return acldvppConvertMode::COLOR_GRAY2BGRA; // dvpp COLOR_GRAY2BGRA/COLOR_GRAY2RGBA - case ConvertMode::COLOR_BGRA2GRAY: - return acldvppConvertMode::COLOR_BGRA2GRAY; // dvpp COLOR_BGRA2GRAY - case ConvertMode::COLOR_RGBA2GRAY: - return acldvppConvertMode::COLOR_RGBA2GRAY; // dvpp COLOR_RGBA2GRAY - default: - return kInvalidConvertMode; - } -} - -/// \brief Convert InterpolationMode to dvpp mode -inline int GetDVPPInterpolationMode(InterpolationMode mode) { - switch (mode) { - case InterpolationMode::kLinear: - return 0; // dvpp BILINEAR - case InterpolationMode::kCubic: - return 2; // dvpp BICUBIC - case InterpolationMode::kArea: - return kInvalidInterpolationMode; - case InterpolationMode::kNearestNeighbour: - return 1; // dvpp NEAREST - default: - return kInvalidInterpolationMode; - } -} - -/// \brief Convert Padding BorderType to dvpp mode -inline uint32_t GetDVPPPaddingMode(BorderType type) { - switch (type) { - case BorderType::kConstant: - return 0; // dvpp Constant - case BorderType::kEdge: - return 1; // dvpp Edge - case BorderType::kReflect: - return 2; // dvpp Reflect - case BorderType::kSymmetric: - return 3; // dvpp Symmetric - default: - return kInvalidPaddingMode; - } -} - -/// \brief Convert Rotate InterpolationMode to dvpp mode -inline uint32_t GetDVPPRotateMode(InterpolationMode mode) { - switch (mode) { - case InterpolationMode::kLinear: - return 0; // dvpp BILINEAR - case InterpolationMode::kNearestNeighbour: - return 1; // dvpp NEAREST - default: - return kInvalidRotateMode; - } -} - -inline Status CheckDvppLimit(int64_t input_h, int64_t input_w, int64_t h_lb, int64_t w_lb, int64_t h_ub, int64_t w_ub, - const std::string &op_name, const std::string ¶m_name = "input") { - if ((input_h < h_lb || input_h > h_ub) || (input_w < w_lb || input_w > w_ub)) { - auto error = op_name + ": the " + param_name + " shape should be from [" + std::to_string(h_lb) + ", " + - std::to_string(w_lb) + "] to [" + std::to_string(h_ub) + ", " + std::to_string(w_ub) + "], but got [" + - std::to_string(input_h) + ", " + std::to_string(input_w) + "]."; - RETURN_STATUS_UNEXPECTED(error); - } - return Status::OK(); -} - -/// \brief Returns image with adjusting brightness. -/// \param input: Tensor of shape format. -/// \param output: Augmented image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: brightness factor. -APP_ERROR DvppAdjustBrightness(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - -/// \brief Returns image with adjusting contrast. -/// \param input: Tensor of shape format. -/// \param output: Augmented image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: contrast factor. -APP_ERROR DvppAdjustContrast(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - -/// \brief Returns image with adjusting hue. -/// \param input: Tensor of shape format. -/// \param output: Augmented image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: hue factor. -APP_ERROR DvppAdjustHue(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - -/// \brief Returns image with adjusting saturation. -/// \param input: Tensor of shape format. -/// \param output: Augmented image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: saturation factor. -APP_ERROR DvppAdjustSaturation(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - -/// \brief Returns image with adjusting sharpness. -/// \param input: Tensor of shape format. -/// \param output: Augmented image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: sharpness factor. -APP_ERROR DvppAdjustSharpness(const std::shared_ptr &input, - std::shared_ptr *output, float factor); - -/// \brief Returns transformed image with affine matrix. -/// \param input: Tensor of shape format. -/// \param output: Transformed image Tensor (type DE_FLOAT32 or DE_UINT8). -/// \param matrix: affine matrix. -/// \param interpolation_mode: the mode of interpolation. -/// \param padding_mode: the mode of padding. -/// \param fill: fill value for color channel. -APP_ERROR DvppAffine(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &matrix, - uint32_t interpolation_mode, uint32_t padding_mode, const std::vector &fill); - -/// \brief Returns image with contrast maximized. -/// \param input: Tensor of shape format. -/// \param output: Transformed image Tensor (type DE_FLOAT32 or DE_UINT8). -/// \param cutoff: cutoff percentage of how many pixels are to be removed from the high and low ends of the histogram. -/// \param ignore: pixel values to be ignored in the algorithm. -APP_ERROR DvppAutoContrast(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &cutoff, - const std::vector &ignore); - -/// \brief Returns Convertcolor image. -/// \param input: Tensor of shape , c support [1, 3, 4], N only support 1. -/// \param output: Transformed image Tensor (type DE_FLOAT32 or DE_UINT8), c = [1, 3, 4]. -/// \param convertMode: the ConvertMode mode. -APP_ERROR DvppConvertColor(const std::shared_ptr &input, - std::shared_ptr *output, ConvertMode convertMode); - -/// \brief Returns croped image. -/// \param input: Tensor of shape format. -/// \param output: Croped image Tensor (type DE_FLOAT32 or DE_UINT8). -/// \param top: the vertical starting coordinate. -/// \param left: the horizontal starting coordinate. -/// \param height: the height of the crop box. -/// \param width: the width of the crop box. -APP_ERROR DvppCrop(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width); - -/// \brief Returns Decoded image. -/// Supported images: JPEG JPG -/// \param input: input containing the not decoded image 1D bytes. -/// \param output: Decoded image Tensor of shape and type DE_UINT8. Pixel order is RGB. -APP_ERROR DvppDecode(const std::shared_ptr &input, - std::shared_ptr *output); - -/// \brief Returns equalized image. -/// \param input: Tensor of shape , c == 1 or c == 3. -/// \param output: Equalized image Tensor (type DE_UINT8). -APP_ERROR DvppEqualize(const std::shared_ptr &input, - std::shared_ptr *output); -/// \brief Returns Erased image. -/// \param input: Tensor of shape format. -/// \param output: Erased image Tensor (type DE_FLOAT32 or DE_UINT8). -/// \param top: top of the cropped box. -/// \param left: left of the cropped box. -/// \param height: height of the cropped box. -/// \param width: width of the cropped box. -/// \param value: fill value for erase -APP_ERROR DvppErase(const std::shared_ptr &input, - std::shared_ptr *output, uint32_t top, uint32_t left, uint32_t height, - uint32_t width, const std::vector &value); - -/// \brief Blur input image with the specified Gaussian kernel. -/// \param input: input containing the not decoded image 1D bytes. -/// \param output: Blurred image Tensor (type DE_FLOAT32 or DE_UINT8). -/// \param kernel_size: The size of the Gaussian kernel. -/// \param sigma: The standard deviation of the Gaussian kernel. -/// \param padding_mode: The method of padding. -APP_ERROR DvppGaussianBlur(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &kernel_size, - const std::vector &sigma, uint32_t padding_mode); - -/// \brief Returns horizontal flip image -/// \param input: Tensor of shape , c == 1 or c == 3 -/// \param output: Flipped image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8) -APP_ERROR DvppHorizontalFlip(const std::shared_ptr &input, - std::shared_ptr *output); - -/// \brief Returns invert image -/// \param input: Tensor of shape , c == 1 or c == 3 -/// \param output: Invert image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8) -APP_ERROR DvppInvert(const std::shared_ptr &input, - std::shared_ptr *output); - -/// \brief Returns Normalized image. -/// \param input: Tensor of shape in RGB order. -/// \param output: Normalized image Tensor of same input shape and type DE_FLOAT32. -/// \param mean: Tensor of shape <3> and type DE_FLOAT32 which are mean of each channel in RGB order. -/// \param std: Tensor of shape <3> and type DE_FLOAT32 which are std of each channel in RGB order. -/// \param is_hwc: Check if input is HWC/CHW format. -APP_ERROR DvppNormalize(const std::shared_ptr &input, - std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc); - -/// \brief Returns Padded image. -/// \param input: Tensor of shape format. -/// \param output: Padded image (type DE_FLOAT32 or DE_UINT8). -/// \param padding The number of pixels to pad each border of the image [left, top, right, bottom]. -/// \param[in] padding_mode The method of padding. -/// \param[in] fill The pixel intensity of the borders. -APP_ERROR DvppPad(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &padding, uint32_t padding_mode, const std::vector &fill); - -/// \brief Returns Perspective image. -/// \param input: Tensor of shape format. -/// \param output: Transformed image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param start_points List containing four lists of two integers corresponding to four -/// corners [top-left, top-right, bottom-right, bottom-left] of the original image. -/// \param[in] end_points List containing four lists of two integers corresponding to four -/// corners [top-left, top-right, bottom-right, bottom-left] of the transformed image. -/// \param[in] interpolation Method of interpolation, support linear and nearest-neighbor interpolation. -APP_ERROR DvppPerspective(const std::shared_ptr &input, - std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, - InterpolationMode interpolation = InterpolationMode::kLinear); - -/// \brief Returns Padded image. -/// \param input: Tensor of shape format. -/// \param output: Padded image (type DE_FLOAT32 or DE_UINT8). -APP_ERROR DvppPosterize(const std::shared_ptr &input, - std::shared_ptr *output, uint8_t bits); - -/// \brief Returns Resized image. -/// \param input: Tensor of shape , c == 1 or c == 3 -/// \param output: Resized image of shape and same type as input. -/// \param output_height: height of output. -/// \param output_width: width of output. -/// \param fx: horizontal scale. -/// \param fy: vertical scale. -/// \param InterpolationMode: the interpolation mode. -APP_ERROR DvppResize(const std::shared_ptr &input, - std::shared_ptr *output, int32_t output_height, int32_t output_width, - double fx = 0.0, double fy = 0.0, InterpolationMode mode = InterpolationMode::kLinear); - -/// \brief Returns Crop and Resized image. -/// \param input: Tensor of shape , c == 1 or c == 3. -/// \param output: Resized image of shape and same type as input. -/// \param top: horizontal start point. -/// \param left: vertical start point. -/// \param height: height of the cropped ROI. -/// \param width: width of the cropped ROI. -/// \param output_height: height of output. -/// \param output_width: width of output. -/// \param mode: the interpolation mode. -APP_ERROR DvppResizedCrop(const std::shared_ptr &input, - std::shared_ptr *output, int32_t top, int32_t left, int32_t height, - int32_t width, int32_t output_height, int32_t output_width, InterpolationMode mode); - -/// \brief Returns rotate image. -/// \param input: Tensor of shape , c == 1 or c == 3 -/// \param output: Rotate image Tensor (type DE_FLOAT32 or DE_UINT8). -APP_ERROR DvppRotate(const std::shared_ptr &input, - std::shared_ptr *output, float degrees, InterpolationMode mode, - bool expand, const std::vector ¢er, const std::vector &fill); - -/// \brief Returns image with solarize. -/// \param input: Tensor of shape format. -/// \param output: solarize image Tensor of same input shape (type DE_FLOAT32 or DE_UINT8). -/// \param factor: saturation factor. -APP_ERROR DvppSolarize(const std::shared_ptr &input, - std::shared_ptr *output, const std::vector &threshold); - -/// \brief Returns vertical flip image. -/// \param input: Tensor of shape , c == 1 or c == 3 -/// \param output: Flipped image Tensor of same input shape (type DE_FLOAT32 and DE_UINT8) -APP_ERROR DvppVerticalFlip(const std::shared_ptr &input, - std::shared_ptr *output); - -APP_ERROR GetSocName(std::string *soc_name); - -APP_ERROR CreateAclTensor(const int64_t *view_dims, uint64_t view_dims_num, mindspore::TypeId data_type, - const int64_t *stride, int64_t offset, const int64_t *storage_dims, uint64_t storage_dims_num, - void *tensor_data, bool is_hwc, void **acl_tensor); - -APP_ERROR DestroyTensor(void *tensor); - -APP_ERROR DestroyFloatArray(void *float_array); - -APP_ERROR DestroyIntArray(void *int_array); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_IMAGE_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.cc b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.cc deleted file mode 100644 index d81913d99..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.cc +++ /dev/null @@ -1,701 +0,0 @@ -/** - * Adapted from - * https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/src/VideoCapture.cpp - * ============================================================================ - * - * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1 Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2 Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3 Neither the names of the copyright holders nor the names of the - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * ============================================================================ - */ -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h" - -#include -#include - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/AclLiteUtils.h" -#include "src/common/log_adapter.h" -#include "plugin/ascend/res_manager/symbol_interface/acl_rt_symbol.h" -#include "plugin/ascend/res_manager/symbol_interface/symbol_utils.h" - -namespace { -const int64_t kUsec = 1000000; -const uint32_t kDecodeFrameQueueSize = 256; -const int kDecodeQueueOpWait = 10000; // decode wait 10ms/frame -const int kFrameEnQueueRetryTimes = 1000; // max wait time for the frame to enter in queue -const int kQueueOpRetryTimes = 1000; -const int kOutputJamWait = 10000; -const int kInvalidTpye = -1; -const int kWaitDecodeFinishInterval = 1000; - -const uint32_t DVPP_VIDEO_H264 = 0; -const uint32_t DVPP_VIDEO_H265 = 1; - -ChannelIdGenerator channelIdGenerator; -} // namespace - -FrameExtarct::FrameExtarct(uint8_t *data, uint32_t size, uint32_t width, uint32_t height, uint32_t type) - : data_(data), size_(size), frameWidth_(width), frameHeight_(height) { - isFinished_ = false; - isStop_ = false; - videoType_ = (type == 0) ? DVPP_VIDEO_H265 : DVPP_VIDEO_H264; -} - -void FrameExtarct::ExtractFrameH264(const uint8_t *buf_ptr, int *size_ptr) { - if (buf_ptr == nullptr || size_ptr == nullptr || *size_ptr <= 0) { - return; - } - bool isFindStart = false; - bool isFindEnd = false; - int size = *size_ptr; - int i = 0; - for (; i < size - 8; i++) { - if (FindStartH264(buf_ptr, i)) { - isFindStart = true; - i += 8; - break; - } - } - - for (; i < size - 8; i++) { - if (FindEndH264(buf_ptr, i)) { - isFindEnd = true; - break; - } - } - - if (i > 0) { - *size_ptr = i; - } - - if (!isFindStart) { - MS_LOG(ERROR) << "Channel can not find H265 start code, please check input video coding protocol is H264."; - return; - } - if (!isFindEnd) { - *size_ptr = i + 8; - } -} - -void FrameExtarct::ExtractFrameH265(const uint8_t *buf_ptr, int *size_ptr) { - if (buf_ptr == nullptr || size_ptr == nullptr || *size_ptr <= 0) { - return; - } - bool isFindStart = false; - bool isFindEnd = false; - int i = 0; - for (; i < *size_ptr - 6; i++) { - if (FindStartH265(buf_ptr, i)) { - isFindStart = true; - i += 6; - break; - } - } - - for (; i < *size_ptr - 6; i++) { - if (FindEndH265(buf_ptr, i)) { - isFindEnd = true; - break; - } - } - if (i > 0) { - *size_ptr = i; - } - - if (!isFindStart) { - MS_LOG(ERROR) << "Channel can not find H265 start code, please check input video coding protocol is H265."; - return; - } - if (!isFindEnd) { - *size_ptr = i + 6; - } -} - -void FrameExtarct::Decode(FrameProcessCallBack callback, void *callbackParam) { - MS_LOG(INFO) << "Start extarct frame from video..."; - - int32_t usedBytes = 0; - uint32_t count = 0; - bool processOk = true; - - while (!isStop_ && processOk) { - uint8_t *bufPointer; - bufPointer = data_ + usedBytes; - int32_t readlen = size_ - usedBytes; - if (readlen <= 0) { - break; - } - - if (videoType_ == DVPP_VIDEO_H264) { // H264 - ExtractFrameH264(bufPointer, &readlen); - } else if (videoType_ == DVPP_VIDEO_H265) { // H265 - ExtractFrameH265(bufPointer, &readlen); - } - int ret = callback(callbackParam, bufPointer, readlen); - if (ret != 0) { - processOk = false; - } - count++; - usedBytes = usedBytes + readlen; - } - // Frame count - - isFinished_ = true; - MS_LOG(INFO) << "FrameExtarct decoder finished, frame count: " << count << "."; -} - -DvppVideo::DvppVideo(aclrtContext context, uint8_t *data, uint32_t size, uint32_t width, uint32_t height, uint32_t type, - uint32_t out_format, const std::string &output) - : data_(data), - size_(size), - frameWidth_(width), - frameHeight_(height), - format_(out_format), - output_(output), - isStop_(false), - isReleased_(false), - isJam_(false), - status_(DecodeStatus::DECODE_UNINIT), - context_(context), - channelId_(INVALID_CHANNEL_ID), - streamFormat_(type), - frameId_(0), - finFrameCnt_(0), - lastDecodeTime_(0), - frameExtarct_(nullptr), - dvppVdec_(nullptr), - frameImageQueue_(kDecodeFrameQueueSize) {} - -DvppVideo::~DvppVideo() { DestroyResource(); } - -void DvppVideo::DestroyResource() { - if (isReleased_) { - return; - } - // 1. stop ffmpeg - isStop_ = true; - frameExtarct_->StopDecode(); - while ((status_ >= DecodeStatus::DECODE_START) && (status_ < DecodeStatus::DECODE_FRAME_EXTRACT_FINISHED)) { - (void)usleep(kWaitDecodeFinishInterval); - } - // 2. delete ffmpeg decoder - delete frameExtarct_; - frameExtarct_ = nullptr; - - // 3. release dvpp vdec - dvppVdec_->DestroyResource(); - - // 4. release image memory in decode output queue - do { - std::shared_ptr frame = FrameImageOutQueue(true); - if (frame == nullptr) { - break; - } - - if (frame->data != nullptr) { - auto ret = acldvppFree(frame->data.get()); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "acldvppFree failed, errorno: " << ret; - } - frame->data = nullptr; - } - } while (true); - // 5. release channel id - channelIdGenerator.ReleaseChannelId(channelId_); - - isReleased_ = true; -} - -AclLiteError DvppVideo::InitResource() { - aclError aclRet; - // use current thread context default - if (context_ == nullptr) { - aclRet = CALL_ASCEND_API(aclrtGetCurrentContext, &context_); - if ((aclRet != ACL_SUCCESS) || (context_ == nullptr)) { - MS_LOG(ERROR) << "Get current acl context error: " << aclRet; - return ACLLITE_ERROR_GET_ACL_CONTEXT; - } - } - // Get current run mode - aclRet = CALL_ASCEND_API(aclrtGetRunMode, &runMode_); - if (aclRet != ACL_SUCCESS) { - MS_LOG(ERROR) << "acl get run mode failed"; - return ACLLITE_ERROR_GET_RUM_MODE; - } - - return ACLLITE_OK; -} - -AclLiteError DvppVideo::InitVdecDecoder() { - // Generate a unique channel id for video decoder - channelId_ = channelIdGenerator.GenerateChannelId(); - if (channelId_ == INVALID_CHANNEL_ID) { - MS_LOG(ERROR) << "Decoder number excessive " << VIDEO_CHANNEL_MAX; - return ACLLITE_ERROR_TOO_MANY_VIDEO_DECODERS; - } - - // Create dvpp vdec to decode h26x data - dvppVdec_ = new VdecHelper(channelId_, frameWidth_, frameHeight_, streamFormat_, DvppVideo::DvppVdecCallback); - - AclLiteError ret = dvppVdec_->SetFormat(format_); - if (ret != ACLLITE_OK) { - MS_LOG(ERROR) << "Dvpp vdec set out format failed"; - } - ret = dvppVdec_->Init(); - if (ret != ACLLITE_OK) { - MS_LOG(ERROR) << "Dvpp vdec init failed"; - } - - ret = this->dvppVdec_->VideoParamCheck(); - if (ret != ACLLITE_OK) { - this->SetStatus(DecodeStatus::DECODE_ERROR); - MS_LOG(ERROR) << "Dvpp vdec check param failed " << ret; - return ret; - } - - return ret; -} - -AclLiteError DvppVideo::InitFrameExtractor() { - // Create ffmpeg decoder to parse video stream to h26x frame data - frameExtarct_ = new FrameExtarct(data_, size_, frameWidth_, frameHeight_, streamFormat_); - return ACLLITE_OK; -} - -AclLiteError DvppVideo::Init() { - // Open video stream, if open failed before, return error directly - if (status_ == DecodeStatus::DECODE_ERROR) { - return ACLLITE_ERROR_OPEN_VIDEO_UNREADY; - } - // If open ok already - if (status_ != DecodeStatus::DECODE_UNINIT) { - return ACLLITE_OK; - } - // Init acl resource - AclLiteError ret = InitResource(); - if (ret != ACLLITE_OK) { - this->SetStatus(DecodeStatus::DECODE_ERROR); - MS_LOG(ERROR) << "Dvpp video init resource failed " << ret; - return ret; - } - // Init ffmpeg decoder - ret = InitFrameExtractor(); - if (ret != ACLLITE_OK) { - this->SetStatus(DecodeStatus::DECODE_ERROR); - MS_LOG(ERROR) << "Dvpp video init FrameExtractor failed " << ret; - return ret; - } - // Init dvpp vdec decoder - ret = InitVdecDecoder(); - if (ret != ACLLITE_OK) { - this->SetStatus(DecodeStatus::DECODE_ERROR); - MS_LOG(ERROR) << "Dvpp video init Vdec failed " << ret; - return ret; - } - // Set init ok - this->SetStatus(DecodeStatus::DECODE_READY); - MS_LOG(INFO) << "Dvpp video init ok"; - - return ACLLITE_OK; -} - -// dvpp vdec callback -void DvppVideo::DvppVdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userData) { - auto *decoder = reinterpret_cast(userData); - // Get decoded image parameters - std::shared_ptr image = std::make_shared(); - image->format = acldvppGetPicDescFormat(output); - image->width = acldvppGetPicDescWidth(output); - image->height = acldvppGetPicDescHeight(output); - image->alignWidth = acldvppGetPicDescWidthStride(output); - image->alignHeight = acldvppGetPicDescHeightStride(output); - image->size = acldvppGetPicDescSize(output); - - void *vdecOutBufferDev = acldvppGetPicDescData(output); - image->data = SHARED_PTR_DVPP_BUF(vdecOutBufferDev); - - // Put the decoded image to queue for read - decoder->ProcessDecodedImage(image); - // Release resource - aclError ret = acldvppDestroyPicDesc(output); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Dvpp vdec destroy pic desc failed " << ret; - } - - if (input != nullptr) { - void *inputBuf = acldvppGetStreamDescData(input); - if (inputBuf != nullptr) { - ret = acldvppFree(inputBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "acldvppFree failed, errorno: " << ret; - } - } - - ret = acldvppDestroyStreamDesc(input); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Dvpp vdec destroy input stream failed " << ret; - } - } -} - -void DvppVideo::ProcessDecodedImage(std::shared_ptr frameData) { - finFrameCnt_++; - if (YUV420SP_SIZE(frameData->width, frameData->height) != frameData->size) { - MS_LOG(ERROR) << "Invalid decoded frame parameter, width " << frameData->width << ", height " << frameData->height - << ", size " << frameData->size << ", buffer " << static_cast(frameData->data.get()); - return; - } - - auto ret = FrameImageEnQueue(frameData); - if (ret != ACLLITE_OK) { - MS_LOG(ERROR) << "FrameImageEnQueue failed, errorno: " << ret; - } - - if ((status_ == DecodeStatus::DECODE_FRAME_EXTRACT_FINISHED) && (finFrameCnt_ >= frameId_)) { - MS_LOG(INFO) << "Last frame decoded by dvpp, change status to " << DecodeStatus::DECODE_DVPP_FINISHED; - this->SetStatus(DecodeStatus::DECODE_DVPP_FINISHED); - } -} - -AclLiteError DvppVideo::FrameImageEnQueue(const std::shared_ptr &frameData) { - for (int count = 0; count < kFrameEnQueueRetryTimes; count++) { - if (frameImageQueue_.Push(frameData)) { - return ACLLITE_OK; - } - (void)usleep(kDecodeQueueOpWait); - } - MS_LOG(ERROR) << "Video lost decoded image for queue full"; - - return ACLLITE_ERROR_VDEC_QUEUE_FULL; -} - -// start decoder -void DvppVideo::StartFrameDecoder() { - if (status_ == DecodeStatus::DECODE_READY) { - decodeThread_ = std::thread(FrameDecodeThreadFunction, reinterpret_cast(this)); - decodeThread_.detach(); - - status_ = DecodeStatus::DECODE_START; - } -} - -// ffmpeg decoder entry -void DvppVideo::FrameDecodeThreadFunction(void *decoderSelf) { - if (decoderSelf == nullptr) { - return; - } - auto *thisPtr = reinterpret_cast(decoderSelf); - - aclError aclRet = thisPtr->SetAclContext(); - if (aclRet != ACL_SUCCESS) { - MS_LOG(ERROR) << "Set frame decoder context failed, errorno: " << aclRet; - return; - } - // start decode until complete - thisPtr->DecodeH26xFrame(); - if (thisPtr->IsStop()) { - thisPtr->SetStatus(DecodeStatus::DECODE_FINISHED); - return; - } - thisPtr->SetStatus(DecodeStatus::DECODE_FRAME_EXTRACT_FINISHED); - // when ffmpeg decode finish, send eos to vdec - std::shared_ptr videoFrame = std::make_shared(); - videoFrame->isFinished = true; - videoFrame->data = nullptr; - videoFrame->size = 0; - auto ret = thisPtr->dvppVdec_->Process(videoFrame, decoderSelf); - if (ret != ACLLITE_OK) { - MS_LOG(ERROR) << "DvppVdec processing failed, errorno: " << ret; - } - - thisPtr->dvppVdec_->DestroyChannel(); - while ((thisPtr->GetStatus() != DecodeStatus::DECODE_DVPP_FINISHED) && !thisPtr->IsStop()) { - (void)usleep(kWaitDecodeFinishInterval); - } -} - -// callback of ffmpeg decode frame -AclLiteError DvppVideo::FrameDecodeCallback(void *decoder, void *frameData, int frameSize) { - if ((frameData == nullptr) || (frameSize == 0)) { - MS_LOG(ERROR) << "Frame data is null"; - return ACLLITE_ERROR_H26X_FRAME; - } - - // copy data to dvpp memory - if (decoder == nullptr) { - MS_LOG(ERROR) << "Decoder is nullptr"; - return ACLLITE_ERROR_H26X_FRAME; - } - auto *videoDecoder = reinterpret_cast(decoder); - - void *buffer = CopyDataToDevice(frameData, frameSize, videoDecoder->runMode_, MemoryType::MEMORY_DVPP); - if (buffer == nullptr) { - MS_LOG(ERROR) << "Copy frame h26x data to dvpp failed"; - return ACLLITE_ERROR_COPY_DATA; - } - - std::shared_ptr videoFrame = std::make_shared(); - videoDecoder->frameId_++; - videoFrame->frameId = videoDecoder->frameId_; - videoFrame->data = buffer; - videoFrame->size = frameSize; - // decode data by dvpp vdec - AclLiteError ret = videoDecoder->dvppVdec_->Process(videoFrame, decoder); - if (ret != ACLLITE_OK) { - MS_LOG(ERROR) << "Dvpp vdec process " << videoDecoder->frameId_ << "th frame failed, error: " << ret; - return ret; - } - return ACLLITE_OK; -} - -// read decoded frame -AclLiteError DvppVideo::Read(std::shared_ptr *image_ptr) { - if (image_ptr == nullptr) { - MS_LOG(ERROR) << "image_ptr is nullptr"; - return ACLLITE_ERROR; - } - // return nullptr,if decode fail/finish - if (status_ == DecodeStatus::DECODE_ERROR) { - MS_LOG(ERROR) << "Read failed for decode failed"; - return ACLLITE_ERROR_VIDEO_DECODER_STATUS; - } - - if (status_ == DecodeStatus::DECODE_FINISHED) { - MS_LOG(INFO) << "No frame to read for decode finished"; - return ACLLITE_ERROR_DECODE_FINISH; - } - // start decode if status is ok - if (status_ == DecodeStatus::DECODE_READY) { - StartFrameDecoder(); - (void)usleep(kDecodeQueueOpWait); - } - // read frame from decode queue - bool noWait = (status_ == DecodeStatus::DECODE_DVPP_FINISHED); - std::shared_ptr frame = FrameImageOutQueue(noWait); - if (noWait && (frame == nullptr)) { - SetStatus(DecodeStatus::DECODE_FINISHED); - MS_LOG(INFO) << "No frame to read anymore"; - return ACLLITE_ERROR_DECODE_FINISH; - } - - if (frame == nullptr) { - MS_LOG(ERROR) << "Empty frame image to read"; - return ACLLITE_ERROR_READ_EMPTY; - } - - (*image_ptr)->format = frame->format; - (*image_ptr)->width = frame->width; - (*image_ptr)->height = frame->height; - (*image_ptr)->alignWidth = frame->alignWidth; - (*image_ptr)->alignHeight = frame->alignHeight; - (*image_ptr)->size = frame->size; - (*image_ptr)->data = frame->data; - - return ACLLITE_OK; -} - -std::shared_ptr DvppVideo::FrameImageOutQueue(bool noWait) { - std::shared_ptr image = frameImageQueue_.Pop(); - - if (noWait || (image != nullptr)) { - return image; - } - - for (int count = 0; count < kQueueOpRetryTimes - 1; count++) { - (void)usleep(kDecodeQueueOpWait); - - image = frameImageQueue_.Pop(); - if (image != nullptr) { - return image; - } - } - - return nullptr; -} - -// YUV data write to a file -void DvppVideo::SaveYuvFile(FILE *const fd, const ImageData &frame) { - auto *addr = reinterpret_cast(frame.data.get()); - uint32_t imageSize = frame.width * frame.height * 3 / 2; // Size = width * height * 3 / 2 - uint8_t *outImageBuf = nullptr; - uint32_t outWidthStride = frame.alignWidth; - uint32_t outHeightStride = frame.alignHeight; - - if (runMode_ == ACL_HOST) { - // malloc host memory - AclLiteError ret = CALL_ASCEND_API(aclrtMallocHost, reinterpret_cast(&outImageBuf), imageSize); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Chn " << channelId_ << " malloc host memory " << imageSize << " failed, error code " << ret; - return; - } - } - - if ((frame.width == outWidthStride) && (frame.height == outHeightStride)) { - if (runMode_ == ACL_HOST) { - // copy device data to host - AclLiteError ret = - CALL_ASCEND_API(aclrtMemcpy, outImageBuf, imageSize, addr, imageSize, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Chn " << channelId_ << " Copy aclrtMemcpy " << imageSize - << " from device to host failed, error code " << ret; - ret = CALL_ASCEND_API(aclrtFreeHost, outImageBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "aclrtFreeHost failed, errorno: " << ret; - } - return; - } - - (void)fwrite(outImageBuf, 1, imageSize, fd); - ret = CALL_ASCEND_API(aclrtFreeHost, outImageBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "aclrtFreeHost failed, errorno: " << ret; - return; - } - } else { - (void)fwrite(addr, imageSize, 1, fd); - } - } else { - if (runMode_ == ACL_HOST) { - if (outImageBuf == nullptr) { - return; - } - // Copy valid Y data - for (uint32_t i = 0; i < frame.height; i++) { - AclLiteError ret = CALL_ASCEND_API(aclrtMemcpy, outImageBuf + i * frame.width, frame.width, - addr + i * outWidthStride, frame.width, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Chn " << channelId_ << " Copy aclrtMemcpy " << imageSize - << " from device to host failed, error code " << ret; - ret = CALL_ASCEND_API(aclrtFreeHost, outImageBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "aclrtFreeHost failed, errorno: " << ret; - } - return; - } - } - // Copy valid UV data - for (uint32_t i = 0; i < frame.height / 2; i++) { - AclLiteError ret = CALL_ASCEND_API(aclrtMemcpy, outImageBuf + i * frame.width + frame.width * frame.height, - frame.width, addr + i * outWidthStride + outWidthStride * outHeightStride, - frame.width, ACL_MEMCPY_DEVICE_TO_HOST); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Chn " << channelId_ << " Copy aclrtMemcpy " << imageSize - << " from device to host failed, error code " << ret; - ret = CALL_ASCEND_API(aclrtFreeHost, outImageBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "aclrtFreeHost failed, errorno: " << ret; - } - return; - } - } - - (void)fwrite(outImageBuf, 1, imageSize, fd); - aclError ret = CALL_ASCEND_API(aclrtFreeHost, outImageBuf); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "aclrtFreeHost failed, errorno: " << ret; - } - } else { - // Crop the invalid data, then write the valid data to a file - outImageBuf = reinterpret_cast(malloc(imageSize)); - if (outImageBuf == nullptr) { - MS_LOG(ERROR) << "Chn " << channelId_ << " Malloc failed"; - return; - } - // Copy valid Y data - for (uint32_t i = 0; i < frame.height; i++) { - int status = memcpy_s(outImageBuf + i * frame.width, frame.width, addr + i * outWidthStride, frame.width); - if (status != EOK) { - MS_LOG(ERROR) << "[Internal ERROR] memcpy failed."; - free(outImageBuf); - return; - } - } - // Copy valid UV data - for (uint32_t i = 0; i < frame.height / 2; i++) { - int status = memcpy_s(outImageBuf + i * frame.width + frame.width * frame.height, frame.width, - addr + i * outWidthStride + outWidthStride * outHeightStride, frame.width); - if (status != EOK) { - MS_LOG(ERROR) << "[Internal ERROR] memcpy failed."; - free(outImageBuf); - return; - } - } - - (void)fwrite(outImageBuf, 1, imageSize, fd); - free(outImageBuf); - } - } -} - -AclLiteError DvppVideo::DumpFrame() { - auto frame = std::make_shared(); - int frameCnt = 0; - while (true) { - AclLiteError ret = Read(&frame); - if (ret != ACLLITE_OK) { - if (ret == ACLLITE_ERROR_DECODE_FINISH) { - MS_LOG(INFO) << "Dump all " << frameCnt << " frames to " << output_; - return ACLLITE_OK; - } else { - MS_LOG(ERROR) << "Dump " << frameCnt << "td frame failed"; - return ret; - } - } - frameCnt++; - std::string full_path = output_ + "/" + "frame_" + std::to_string(frameCnt) + ".yuv"; - MS_LOG(INFO) << "Dump the " << frameCnt << "th frame to " << full_path; - FILE *outFileFp = fopen(full_path.c_str(), "wb+"); - SaveYuvFile(outFileFp, *frame); - (void)fflush(outFileFp); - (void)fclose(outFileFp); - } -} - -AclLiteError DvppVideo::SetAclContext() { - if (context_ == nullptr) { - MS_LOG(ERROR) << "Video decoder context is null"; - return ACLLITE_ERROR_SET_ACL_CONTEXT; - } - - aclError ret = CALL_ASCEND_API(aclrtSetCurrentContext, context_); - if (ret != ACL_SUCCESS) { - MS_LOG(ERROR) << "Video decoder set context failed, error: " << ret; - return ACLLITE_ERROR_SET_ACL_CONTEXT; - } - - return ACLLITE_OK; -} - -AclLiteError DvppVideo::Close() { - DestroyResource(); - return ACLLITE_OK; -} diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h deleted file mode 100644 index 3c9ed3172..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/dvpp_video.h +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Adapted from - * https://gitee.com/ascend/samples/blob/master/cplusplus/common/acllite/include/VideoCapture.h - * ============================================================================ - * - * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1 Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2 Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3 Neither the names of the copyright holders nor the names of the - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * ============================================================================ - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_VIDEO_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_VIDEO_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/ThreadSafeQueue.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/VdecHelper.h" - -constexpr int INVALID_CHANNEL_ID = -1; -constexpr int INVALID_STREAM_FORMAT = -1; -constexpr int VIDEO_CHANNEL_MAX = 23; -constexpr int THIRD_ELEMENT_INDEX = 2; -constexpr int FOURTH_ELEMENT_INDEX = 3; -constexpr int FIFTH_ELEMENT_INDEX = 4; -constexpr int SIXTH_ELEMENT_INDEX = 5; -constexpr int EIGHTH_ELEMENT_INDEX = 7; - -using FrameProcessCallBack = int (*)(void *callback_param, void *frame_data, int frame_size); - -enum class DecodeStatus { - DECODE_ERROR = -1, - DECODE_UNINIT = 0, - DECODE_READY = 1, - DECODE_START = 2, - DECODE_FRAME_EXTRACT_FINISHED = 3, - DECODE_DVPP_FINISHED = 4, - DECODE_FINISHED = 5 -}; - -class ChannelIdGenerator { - public: - ChannelIdGenerator() noexcept { - for (int i = 0; i < VIDEO_CHANNEL_MAX; i++) { - channelId_[i] = INVALID_CHANNEL_ID; - } - } - ~ChannelIdGenerator() = default; - - int GenerateChannelId() { - std::lock_guard lock(mutex_lock_); - for (int i = 0; i < VIDEO_CHANNEL_MAX; i++) { - if (channelId_[i] == INVALID_CHANNEL_ID) { - channelId_[i] = i; - return i; - } - } - - return INVALID_CHANNEL_ID; - } - - void ReleaseChannelId(int channelId) { - std::lock_guard lock(mutex_lock_); - if ((channelId >= 0) && (channelId < VIDEO_CHANNEL_MAX)) { - channelId_[channelId] = INVALID_CHANNEL_ID; - } - } - - private: - int channelId_[VIDEO_CHANNEL_MAX]{}; - mutable std::mutex mutex_lock_; -}; - -class FrameExtarct { - public: - FrameExtarct(uint8_t *data, uint32_t size, uint32_t width, uint32_t height, uint32_t type); - ~FrameExtarct() = default; - void Decode(FrameProcessCallBack callback, void *callbackParam); - void ExtractFrameH264(const uint8_t *buf_ptr, int *size_ptr); - void ExtractFrameH265(const uint8_t *buf_ptr, int *size_ptr); - int IsFinished() const { return isFinished_; } - void StopDecode() { isStop_ = true; } - - private: - inline bool FindStartH264(const uint8_t *buf, int idx) { - int32_t tmp = buf[idx + FOURTH_ELEMENT_INDEX] & 0x1F; - // Find 00 00 01 - return (buf[idx] == 0) && (buf[idx + 1] == 0) && (buf[idx + THIRD_ELEMENT_INDEX] == 1) && - (((tmp == 0x5 || tmp == 0x1) && ((buf[idx + FIFTH_ELEMENT_INDEX] & 0x80) == 0x80)) || - (tmp == 0x14 && (buf[idx + EIGHTH_ELEMENT_INDEX] & 0x80) == 0x80)); - } - - inline bool FindEndH264(const uint8_t *buf, int idx) { - // Find 00 00 01 - int32_t tmp = buf[idx + FOURTH_ELEMENT_INDEX] & 0x1F; - return (buf[idx] == 0) && (buf[idx + 1] == 0) && (buf[idx + THIRD_ELEMENT_INDEX] == 1) && - ((tmp == 0xF) || (tmp == 0x7) || (tmp == 0x8) || (tmp == 0x6) || - ((tmp == 0x5 || tmp == 1) && ((buf[idx + FIFTH_ELEMENT_INDEX] & 0x80) == 0x80)) || - (tmp == 0x14 && (buf[idx + EIGHTH_ELEMENT_INDEX] & 0x80) == 0x80)); - } - - inline bool FindStartH265(const uint8_t *buf, int idx) { - uint32_t tmp = (buf[idx + FOURTH_ELEMENT_INDEX] & 0x7EU) >> 1; - // Find 00 00 01 - return (buf[idx + 0] == 0) && (buf[idx + 1] == 0) && (buf[idx + THIRD_ELEMENT_INDEX] == 1) && (tmp <= 0x15U) && - ((buf[idx + SIXTH_ELEMENT_INDEX] & 0x80) == 0x80); - } - - inline bool FindEndH265(const uint8_t *buf, int idx) { - uint32_t tmp = (buf[idx + FOURTH_ELEMENT_INDEX] & 0x7EU) >> 1; - // Find 00 00 01 - return ((buf[idx + 0] == 0) && (buf[idx + 1] == 0) && (buf[idx + THIRD_ELEMENT_INDEX] == 1) && - ((tmp == 0x20U) || (tmp == 0x21U) || (tmp == 0x22U) || (tmp == 0x27U) || (tmp == 0x28U) || - ((tmp <= 0x15U) && (buf[idx + SIXTH_ELEMENT_INDEX] & 0x80) == 0x80))); - } - - private: - uint8_t *data_; - uint32_t size_; - - uint32_t frameWidth_; - uint32_t frameHeight_; - int videoType_; - - bool isFinished_; - bool isStop_; -}; - -class DvppVideo { - public: - /** - * @brief DvppVideo constructor - */ - DvppVideo(aclrtContext context, uint8_t *data, uint32_t size, uint32_t width, uint32_t height, uint32_t type, - uint32_t out_format, const std::string &output); - - /** - * @brief DvppVideo destructor - */ - ~DvppVideo(); - - static void FrameDecodeThreadFunction(void *decoderSelf); - static AclLiteError FrameDecodeCallback(void *context, void *frameData, int frameSize); - static void DvppVdecCallback(acldvppStreamDesc *input, acldvppPicDesc *output, void *userdata); - - void ProcessDecodedImage(std::shared_ptr frameData); - void DecodeH26xFrame() { frameExtarct_->Decode(&DvppVideo::FrameDecodeCallback, reinterpret_cast(this)); } - - AclLiteError Init(); - void SetStatus(DecodeStatus status) { status_ = status; } - DecodeStatus GetStatus() { return status_; } - - AclLiteError Read(std::shared_ptr *image_ptr); - - AclLiteError DumpFrame(); - - AclLiteError SetAclContext(); - AclLiteError Close(); - - void DestroyResource(); - bool IsStop() const { return isStop_; } - bool IsJam() const { return isJam_; } - - private: - AclLiteError InitResource(); - AclLiteError InitVdecDecoder(); - AclLiteError InitFrameExtractor(); - void StartFrameDecoder(); - AclLiteError FrameImageEnQueue(const std::shared_ptr &frameData); - std::shared_ptr FrameImageOutQueue(bool noWait = false); - - void SaveYuvFile(FILE *fd, const ImageData &frame); - - private: - uint8_t *data_; - uint32_t size_; - - uint32_t frameWidth_; - uint32_t frameHeight_; - - /* 1:YUV420 semi-planner(nv12) - 2:YVU420 semi-planner(nv21) - */ - uint32_t format_; - std::string output_; - - bool isStop_; - bool isReleased_; - bool isJam_; - DecodeStatus status_; - aclrtContext context_; - aclrtRunMode runMode_; - int channelId_; - int streamFormat_; - uint32_t frameId_; - uint32_t finFrameCnt_; - int64_t lastDecodeTime_; - std::thread decodeThread_; - FrameExtarct *frameExtarct_; - VdecHelper *dvppVdec_; - ThreadSafeQueue> frameImageQueue_; -}; -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_DVPP_VIDEO_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h b/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h deleted file mode 100644 index e0813062b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/dvpp/utils/resouce_info.h +++ /dev/null @@ -1,58 +0,0 @@ -/** -* Copyright 2022-2023 Huawei Technologies Co., Ltd -* -* 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_INFO_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_INFO_H_ - -#include -#include -#include -#include -#include - -// Description of data in device -struct RawData { - size_t lenOfByte; // Size of memory, bytes - void *data; // Pointer of data -}; - -enum ModelLoadMethod { - LOAD_FROM_FILE = 0, // Loading from file, memory of model and weights are managed by ACL - LOAD_FROM_MEM, // Loading from memory, memory of model and weights are managed by ACL - LOAD_FROM_FILE_WITH_MEM, // Loading from file, memory of model and weight are managed by user - LOAD_FROM_MEM_WITH_MEM // Loading from memory, memory of model and weight are managed by user -}; - -struct ModelInfo { - std::string modelName; - std::string modelPath; // Path of om model file - size_t modelFileSize; // Size of om model file - std::shared_ptr modelFilePtr; // Smart pointer of model file data - uint32_t modelWidth; // Input width of model - uint32_t modelHeight; // Input height of model - ModelLoadMethod method; // Loading method of model -}; - -// Device resource info, such as model infos, etc -struct DeviceResInfo { - std::vector modelInfos; -}; - -struct ResourceInfo { - std::set deviceIds; - std::string singleOpFolderPath; - std::unordered_map deviceResInfos; // map -}; -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_DVPP_UTILS_RESOURCE_INFO_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/equalize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/equalize_op.cc deleted file mode 100644 index 723bab0f4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/equalize_op.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/equalize_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -// only supports RGB images -Status EqualizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Equalize(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/equalize_op.h b/mindspore-lite/minddata/dataset/kernels/image/equalize_op.h deleted file mode 100644 index 5802bb4c5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/equalize_op.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EQUALIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EQUALIZE_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class EqualizeOp : public TensorOp { - public: - EqualizeOp() = default; - - ~EqualizeOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kEqualizeOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EQUALIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/erase_op.cc b/mindspore-lite/minddata/dataset/kernels/image/erase_op.cc deleted file mode 100644 index cf7755a6e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/erase_op.cc +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - - * 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. -*/ -#include "mindspore-lite/minddata/dataset/kernels/image/erase_op.h" - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// constructor -EraseOp::EraseOp(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value, - bool inplace) - : top_(top), left_(left), height_(height), width_(width), value_(value), inplace_(inplace) {} - -Status EraseOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageDtype("Erase", input->type())); - if (input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("Erase: input tensor is not in shape of , but got rank: " + - std::to_string(input->Rank())); - } - int num_channels = input->shape()[2]; - if (num_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("Erase: channel of input image should be 3, but got: " + std::to_string(num_channels)); - } - return Erase(input, output, top_, left_, height_, width_, value_, inplace_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/erase_op.h b/mindspore-lite/minddata/dataset/kernels/image/erase_op.h deleted file mode 100644 index 7f634c1f2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/erase_op.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ERASE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ERASE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class EraseOp : public TensorOp { - public: - EraseOp(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value, - bool inplace = false); - - ~EraseOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kEraseOp; } - - private: - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - std::vector value_; - bool inplace_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ERASE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/exif_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/exif_utils.cc deleted file mode 100644 index ac971d66e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/exif_utils.cc +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/exif_utils.h" - -#include -#include - -#define UNKNOW_ORIENTATION 0 - -namespace mindspore { -namespace dataset { -template -T parse_bytes(const uint8_t *buf, bool intel_align); - -template <> -uint8_t parse_bytes(const uint8_t *buf, bool intel_align) { - if (buf == nullptr) { - return 0; - } - return *buf; -} - -template <> -uint16_t parse_bytes(const uint8_t *buf, bool intel_align) { - if (buf == nullptr) { - return 0; - } - - uint16_t res = 0; - if (intel_align) { - res = (static_cast(buf[1]) << 8) | buf[0]; - } else { - res = (static_cast(buf[0]) << 8) | buf[1]; - } - return res; -} - -template <> -uint32_t parse_bytes(const uint8_t *buf, bool intel_align) { - if (buf == nullptr) { - return 0; - } - - uint32_t res = 0; - if (intel_align) { - res = (static_cast(buf[3]) << 24) | (static_cast(buf[2]) << 16) | - (static_cast(buf[1]) << 8) | buf[0]; - } else { - res = (static_cast(buf[0]) << 24) | (static_cast(buf[1]) << 16) | - (static_cast(buf[2]) << 8) | buf[3]; - } - return res; -} - -int parseExif(const uint8_t *buf, uint32_t len) { - constexpr uint32_t kMinLength = 6; - if (buf == nullptr || len < kMinLength) { - return UNKNOW_ORIENTATION; - } - - if (!std::equal(buf, buf + 6, "Exif\0\0")) { - return UNKNOW_ORIENTATION; - } - uint32_t offset = 6; - if (offset + 8 > len) { - return UNKNOW_ORIENTATION; - } - bool intel_align = true; - if (buf[offset] == 'I' && buf[offset + 1] == 'I') { - intel_align = true; - } else if (buf[offset] == 'M' && buf[offset + 1] == 'M') { - intel_align = false; - } else { - return UNKNOW_ORIENTATION; - } - - offset += 2; - if (parse_bytes(buf + offset, intel_align) != 0x2a) { - return UNKNOW_ORIENTATION; - } - offset += 2; - uint32_t first_ifd_offset = parse_bytes(buf + offset, intel_align); - offset += first_ifd_offset - 4; - if (offset >= len || offset + 2 > len) { - return UNKNOW_ORIENTATION; - } - - int num_entries = parse_bytes(buf + offset, intel_align); - if (offset + 6 + 12 * num_entries > len) { - return UNKNOW_ORIENTATION; - } - offset += 2; - while (num_entries > 0) { - uint16_t tag = parse_bytes(buf + offset, intel_align); - if (tag == 0x112) { - uint16_t format = parse_bytes(buf + offset + 2, intel_align); - uint32_t length = parse_bytes(buf + offset + 4, intel_align); - if (format == 3 && length) { - uint16_t orient = parse_bytes(buf + offset + 8, intel_align); - return static_cast(orient); - } - } - offset += 12; - num_entries--; - } - return UNKNOW_ORIENTATION; -} - -int ExifInfo::parseOrientation(const unsigned char *data, unsigned len) { - constexpr int64_t len_size = 4; - constexpr int64_t len_min = 2; - constexpr int64_t offset_factor = 4; - constexpr int64_t section_length_min = 16; - if (data == nullptr || len < len_size) { - return UNKNOW_ORIENTATION; - } - - if (data[0] != 0xFF || data[1] != 0xD8) { - return UNKNOW_ORIENTATION; - } - - while (len > len_min) { - if (data[len - 1] == 0xD9 && data[len - 2] == 0xFF) break; - len--; - } - if (len <= len_min) { - return UNKNOW_ORIENTATION; - } - - unsigned int offset = 0; - for (; offset < len - 1; offset++) { - if (data[offset] == 0xFF && data[offset + 1] == 0xE1) break; - } - if (offset + offset_factor > len) { - return UNKNOW_ORIENTATION; - } - offset += 2; - uint16_t section_length = parse_bytes(data + offset, false); - if (offset + section_length > len || section_length < section_length_min) { - return UNKNOW_ORIENTATION; - } - offset += 2; - - return parseExif(data + offset, len - offset); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/exif_utils.h b/mindspore-lite/minddata/dataset/kernels/image/exif_utils.h deleted file mode 100644 index 7a3584752..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/exif_utils.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EXIF_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EXIF_H_ - -namespace mindspore { -namespace dataset { - -class ExifInfo { - public: - int parseOrientation(const unsigned char *data, unsigned len); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_EXIF_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.cc b/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.cc deleted file mode 100644 index 0d37747dc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.cc +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status GaussianBlurOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageRank("GaussianBlur", input->Rank())); - return GaussianBlur(input, output, kernel_x_, kernel_y_, sigma_x_, sigma_y_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h b/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h deleted file mode 100644 index 01ba7600d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_GAUSSIAN_BLUR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_GAUSSIAN_BLUR_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class GaussianBlurOp : public TensorOp { - public: - /// \brief Constructor to GaussianBlur Op - /// \param[in] kernel_x - Gaussian kernel size of width - /// \param[in] kernel_y - Gaussian kernel size of height - /// \param[in] sigma_x - Gaussian kernel standard deviation of width - /// \param[in] sigma_y - Gaussian kernel standard deviation of height - GaussianBlurOp(int32_t kernel_x, int32_t kernel_y, float sigma_x, float sigma_y) - : kernel_x_(kernel_x), kernel_y_(kernel_y), sigma_x_(sigma_x), sigma_y_(sigma_y) {} - - ~GaussianBlurOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kGaussianBlurOp; } - - void Print(std::ostream &out) const override { - out << Name() << " kernel_size: (" << kernel_x_ << ", " << kernel_y_ << "), sigma: (" << sigma_x_ << ", " - << sigma_y_ << ")"; - } - - protected: - int32_t kernel_x_; - int32_t kernel_y_; - float sigma_x_; - float sigma_y_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_GAUSSIAN_BLUR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.cc b/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.cc deleted file mode 100644 index 784a37dc1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.cc +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status HorizontalFlipOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "HorizontalFlip", {1, 2, 3, 4, 5, 6, 10, 11, 12})); - dsize_t rank = input->shape().Rank(); - if (rank <= kDefaultImageRank) { - RETURN_IF_NOT_OK(HorizontalFlip(input, output)); - } else { - // reshape input to nhwc - auto input_shape = input->shape(); - dsize_t num_batch = input->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and horizental flip N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (auto &image : input_vector_hwc) { - std::shared_ptr flip; - RETURN_IF_NOT_OK(HorizontalFlip(image, &flip)); - output_vector_hwc.push_back(flip); - } - - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, output)); - RETURN_IF_NOT_OK((*output)->Reshape(input_shape)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h b/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h deleted file mode 100644 index e34e0bee0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HORIZONTAL_FLIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HORIZONTAL_FLIP_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class HorizontalFlipOp : public TensorOp { - public: - HorizontalFlipOp() {} - - ~HorizontalFlipOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kHorizontalFlipOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HORIZONTAL_FLIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.cc b/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.cc deleted file mode 100644 index a9e329909..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.cc +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status HwcToChwOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return HwcToChw(input, output); -} - -Status HwcToChwOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "HWC2CHW: inputs cannot be empty."); - TensorShape image_shape = inputs[0]; - constexpr auto kDefaultImageRank = 3; - if (image_shape.Rank() == kDefaultImageRank) { - (void)outputs.emplace_back(TensorShape{image_shape[2], image_shape[0], image_shape[1]}); - } - CHECK_FAIL_RETURN_UNEXPECTED( - !outputs.empty(), - "HWC2CHW: invalid input shape, expected 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h b/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h deleted file mode 100644 index 9bfedf1ac..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HWC_TO_CHW_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HWC_TO_CHW_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class HwcToChwOp : public TensorOp { - public: - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kHwcToChwOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_HWC_TO_CHW_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/image_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/image_utils.cc deleted file mode 100644 index f8a76f908..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/image_utils.cc +++ /dev/null @@ -1,2867 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/invert_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/posterize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h" -#include "utils/file_utils.h" -#include "utils/ms_utils.h" - -const int32_t MAX_INT_PRECISION = 16777216; // float int precision is 16777216 -const int32_t DOUBLING_FACTOR = 2; // used as multiplier with MAX_INT_PRECISION -const int32_t DEFAULT_NUM_HEIGHT = 1; -const int32_t DEFAULT_NUM_WIDTH = 1; - -namespace mindspore { -namespace dataset { -int GetCVInterpolationMode(InterpolationMode mode) { - switch (mode) { - case InterpolationMode::kLinear: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - case InterpolationMode::kCubic: - return static_cast(cv::InterpolationFlags::INTER_CUBIC); - case InterpolationMode::kArea: - return static_cast(cv::InterpolationFlags::INTER_AREA); - case InterpolationMode::kNearestNeighbour: - return static_cast(cv::InterpolationFlags::INTER_NEAREST); - default: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - } -} - -int GetCVBorderType(BorderType type) { - switch (type) { - case BorderType::kConstant: - return static_cast(cv::BorderTypes::BORDER_CONSTANT); - case BorderType::kEdge: - return static_cast(cv::BorderTypes::BORDER_REPLICATE); - case BorderType::kReflect: - return static_cast(cv::BorderTypes::BORDER_REFLECT101); - case BorderType::kSymmetric: - return static_cast(cv::BorderTypes::BORDER_REFLECT); - default: - return static_cast(cv::BorderTypes::BORDER_CONSTANT); - } -} - -Status GetConvertShape(ConvertMode convert_mode, const std::shared_ptr &input_cv, - std::vector *node) { - RETURN_UNEXPECTED_IF_NULL(node); - std::vector one_channels = {ConvertMode::COLOR_BGR2GRAY, ConvertMode::COLOR_RGB2GRAY, - ConvertMode::COLOR_BGRA2GRAY, ConvertMode::COLOR_RGBA2GRAY}; - std::vector three_channels = { - ConvertMode::COLOR_BGRA2BGR, ConvertMode::COLOR_RGBA2RGB, ConvertMode::COLOR_RGBA2BGR, ConvertMode::COLOR_BGRA2RGB, - ConvertMode::COLOR_BGR2RGB, ConvertMode::COLOR_RGB2BGR, ConvertMode::COLOR_GRAY2BGR, ConvertMode::COLOR_GRAY2RGB}; - std::vector four_channels = {ConvertMode::COLOR_BGR2BGRA, ConvertMode::COLOR_RGB2RGBA, - ConvertMode::COLOR_BGR2RGBA, ConvertMode::COLOR_RGB2BGRA, - ConvertMode::COLOR_BGRA2RGBA, ConvertMode::COLOR_RGBA2BGRA, - ConvertMode::COLOR_GRAY2BGRA, ConvertMode::COLOR_GRAY2RGBA}; - if (std::find(three_channels.begin(), three_channels.end(), convert_mode) != three_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1], 3}; - } else if (std::find(four_channels.begin(), four_channels.end(), convert_mode) != four_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1], 4}; - } else if (std::find(one_channels.begin(), one_channels.end(), convert_mode) != one_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1]}; - } else { - RETURN_STATUS_UNEXPECTED( - "The mode of image channel conversion must be in ConvertMode, which mainly includes " - "conversion between RGB, BGR, GRAY, RGBA etc."); - } - return Status::OK(); -} - -Status ImageNumChannels(const std::shared_ptr &image, dsize_t *channels) { - RETURN_UNEXPECTED_IF_NULL(channels); - if (image->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED( - "GetImageNumChannels: invalid parameter, image should have at least two dimensions, but got: " + - std::to_string(image->Rank())); - } else if (image->Rank() == kMinImageRank) { - *channels = 1; - } else { - *channels = image->shape()[-1]; - } - return Status::OK(); -} - -Status ImageSize(const std::shared_ptr &image, std::vector *size) { - RETURN_UNEXPECTED_IF_NULL(size); - *size = std::vector(kMinImageRank); - if (image->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED("GetImageSize: invalid parameter, image should have at least two dimensions, but got: " + - std::to_string(image->Rank())); - } else if (image->Rank() == kMinImageRank) { - (*size)[0] = image->shape()[0]; - (*size)[1] = image->shape()[1]; - } else { - const int32_t kHeightIndexFromBack = -3; - const int32_t kWidthIndexFromBack = -2; - (*size)[0] = image->shape()[kHeightIndexFromBack]; - (*size)[1] = image->shape()[kWidthIndexFromBack]; - } - return Status::OK(); -} - -Status ValidateImage(const std::shared_ptr &image, const std::string &op_name, - const std::set &valid_dtype, const std::set &valid_rank, - const std::set &valid_channel) { - // Validate image dtype - if (!valid_dtype.empty()) { - auto dtype = image->type(); - if (valid_dtype.find(dtype.value()) == valid_dtype.end()) { - std::string err_msg = op_name + ": the data type of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in type of " + DataTypeSetToString(valid_dtype); - err_msg += ". But got type " + dtype.ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - // Validate image rank - auto rank = image->Rank(); - if (!valid_rank.empty()) { - if (valid_rank.find(rank) == valid_rank.end()) { - std::string err_msg = op_name + ": the dimension of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in dimension of " + NumberSetToString(valid_rank); - if (valid_rank == std::set({kMinImageRank, kDefaultImageRank})) { - err_msg += ", in shape of or "; - } else if (valid_rank == std::set({kMinImageRank})) { - err_msg += ", in shape of "; - } else if (valid_rank == std::set({kDefaultImageRank})) { - err_msg += ", in shape of "; - } - err_msg += ". But got dimension " + std::to_string(rank) + "."; - if (rank == 1) { - err_msg += " You may need to perform Decode first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - if (rank < kMinImageRank) { - std::string err_msg = - op_name + ": the image tensor should have at least two dimensions. You may need to perform Decode first."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - // Validate image channel - if (!valid_channel.empty()) { - dsize_t channel = 1; - RETURN_IF_NOT_OK(ImageNumChannels(image, &channel)); - if (valid_channel.find(channel) == valid_channel.end()) { - std::string err_msg = op_name + ": the channel of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in channel of " + NumberSetToString(valid_channel); - err_msg += ". But got channel " + std::to_string(channel) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} - -Status ValidateImageDtype(const std::string &op_name, DataType dtype) { - uint8_t type = dtype.AsCVType(); - if (type == kCVInvalidType) { - std::string type_name = "unknown"; - if (dtype.value() < DataType::NUM_OF_TYPES) { - type_name = std::string(DataType::kTypeInfo[dtype.value()].name_); - } - std::string err_msg = op_name + ": Cannot convert [" + type_name + "] to OpenCV type." + - " Currently unsupported data type: [uint32, int64, uint64, string]"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status ValidateImageRank(const std::string &op_name, int32_t rank) { - if (rank != kMinImageRank && rank != kDefaultImageRank) { - std::string err_msg = - op_name + ": input tensor is not in shape of or , but got rank: " + std::to_string(rank); - if (rank == 1) { - err_msg = err_msg + ". You may need to perform Decode first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -bool CheckTensorShape(const std::shared_ptr &tensor, const int &channel) { - if (tensor == nullptr) { - return false; - } - bool rc = false; - if (tensor->shape().Size() <= channel) { - return false; - } - if (tensor->Rank() != kDefaultImageRank || - (tensor->shape()[channel] != 1 && tensor->shape()[channel] != kDefaultImageChannel)) { - rc = true; - } - return rc; -} - -Status Flip(std::shared_ptr input, std::shared_ptr *output, int flip_code) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Flip: load image failed."); - } - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - - try { - cv::flip(input_cv->mat(), output_cv->mat(), flip_code); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Flip: " + std::string(e.what())); - } - return Status::OK(); -} - -Status HorizontalFlip(std::shared_ptr input, std::shared_ptr *output) { - return Flip(std::move(input), output, 1); -} - -Status VerticalFlip(std::shared_ptr input, std::shared_ptr *output) { - return Flip(std::move(input), output, 0); -} - -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Resize: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Resize", input_cv->Rank())); - - cv::Mat in_image = input_cv->mat(); - const uint32_t kResizeShapeLimits = 1000; - // resize image too large or too small, 1000 is arbitrarily chosen here to prevent open cv from segmentation fault - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.rows, - "Resize: in_image rows out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.cols, - "Resize: in_image cols out of bounds."); - if (output_height > in_image.rows * kResizeShapeLimits || output_width > in_image.cols * kResizeShapeLimits) { - RETURN_STATUS_ERROR( - StatusCode::kMDShapeMisMatch, - "Resize: the resizing width or height is too big, it's 1000 times bigger than the original image, got output " - "height: " + - std::to_string(output_height) + ", width: " + std::to_string(output_width) + - ", and original image size:" + std::to_string(in_image.rows) + ", " + std::to_string(in_image.cols)); - } - if (output_height == 0 || output_width == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDShapeMisMatch, - "Resize: the input value of 'resize' is invalid, width or height is zero."); - } - - if (mode == InterpolationMode::kCubicPil) { - if (input_cv->shape().Size() != kDefaultImageChannel || - input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("Resize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + - input_cv->shape().ToString()); - } - - LiteMat imIn, imOut; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({output_height, output_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - imOut.Init(output_width, output_height, static_cast(input_cv->shape()[kChannelIndexHWC]), - reinterpret_cast(buffer), LDataType::UINT8); - imIn.Init(static_cast(input_cv->shape()[1]), static_cast(input_cv->shape()[0]), - static_cast(input_cv->shape()[kChannelIndexHWC]), input_cv->mat().data, LDataType::UINT8); - if (ResizeCubic(imIn, imOut, output_width, output_height) == false) { - RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); - } - *output = output_tensor; - return Status::OK(); - } - try { - TensorShape shape{output_height, output_width}; - if (input_cv->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - shape = shape.AppendDim(num_channels); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); - - auto cv_mode = GetCVInterpolationMode(mode); - cv::resize(in_image, output_cv->mat(), cv::Size(output_width, output_height), fx, fy, cv_mode); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Resize: " + std::string(e.what())); - } -} - -const unsigned char kJpegMagic[] = "\xFF\xD8\xFF"; -constexpr dsize_t kJpegMagicLen = 3; -const unsigned char kPngMagic[] = "\x89\x50\x4E\x47"; -constexpr dsize_t kPngMagicLen = 4; - -bool IsNonEmptyJPEG(const std::shared_ptr &input) { - if (input->type() == DataType::DE_BYTES) { - uint32_t len = 0; - if (input->GetStringLength(&len) != Status::OK()) { - MS_LOG(ERROR) << "Get string length from bytes field failed."; - return false; - } - return len > kJpegMagicLen && memcmp(input->GetStringsBuffer(), kJpegMagic, kJpegMagicLen) == 0; - } - return input->SizeInBytes() > kJpegMagicLen && memcmp(input->GetMutableBuffer(), kJpegMagic, kJpegMagicLen) == 0; -} - -bool IsNonEmptyPNG(const std::shared_ptr &input) { - if (input->type() == DataType::DE_BYTES) { - uint32_t len = 0; - if (input->GetStringLength(&len) != Status::OK()) { - MS_LOG(ERROR) << "Get string length from bytes field failed."; - return false; - } - return len > kPngMagicLen && memcmp(input->GetStringsBuffer(), kPngMagic, kPngMagicLen) == 0; - } - return input->SizeInBytes() > kPngMagicLen && memcmp(input->GetMutableBuffer(), kPngMagic, kPngMagicLen) == 0; -} - -Status Decode(const std::shared_ptr &input, std::shared_ptr *output) { - RETURN_IF_NOT_OK(CheckUnsupportedImage(input)); - - if (input->type() == DataType::DE_BYTES && input->shape().NumOfElements() != 1) { - RETURN_STATUS_UNEXPECTED("Decode: couldn't decode bytes field with multi dims."); - } - - Status ret; - if (IsNonEmptyJPEG(input)) { - ret = JpegCropAndDecode(input, output); - } else { - ret = DecodeCv(input, output); - } - - // decode failed and dump it - if (ret != Status::OK()) { - return DumpImageAndAppendStatus(input, ret); - } - return ret; -} - -Status DecodeCv(const std::shared_ptr &input, std::shared_ptr *output) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: load image failed."); - } - try { - cv::Mat img_mat = cv::imdecode(input_cv->mat(), cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION); - if (img_mat.data == nullptr) { - std::string err = "Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err); - } - cv::cvtColor(img_mat, img_mat, static_cast(cv::COLOR_BGR2RGB)); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(img_mat, 3, &output_cv)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Decode: " + std::string(e.what())); - } -} - -static void JpegInitSource(j_decompress_ptr cinfo) {} - -static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) { - if (cinfo->src->bytes_in_buffer == 0) { - // Under ARM platform raise runtime_error may cause core problem, - // so we catch runtime_error and just return FALSE. - try { - ERREXIT(cinfo, JERR_INPUT_EMPTY); - } catch (std::runtime_error &e) { - return FALSE; - } - return FALSE; - } - return TRUE; -} - -static void JpegTermSource(j_decompress_ptr cinfo) {} - -static void JpegSkipInputData(j_decompress_ptr cinfo, int64_t jump) { - if (jump < 0) { - return; - } - if (static_cast(jump) > cinfo->src->bytes_in_buffer) { - cinfo->src->bytes_in_buffer = 0; - return; - } else { - cinfo->src->bytes_in_buffer -= jump; - cinfo->src->next_input_byte += jump; - } -} - -void JpegSetSource(j_decompress_ptr cinfo, const void *data, int64_t datasize) { - cinfo->src = static_cast( - (*cinfo->mem->alloc_small)(reinterpret_cast(cinfo), JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr))); - cinfo->src->init_source = JpegInitSource; - cinfo->src->fill_input_buffer = JpegFillInputBuffer; -#if defined(_WIN32) || defined(_WIN64) || defined(ENABLE_ARM32) || defined(__APPLE__) - cinfo->src->skip_input_data = reinterpret_cast(JpegSkipInputData); -#else - cinfo->src->skip_input_data = JpegSkipInputData; -#endif - cinfo->src->resync_to_restart = jpeg_resync_to_restart; - cinfo->src->term_source = JpegTermSource; - cinfo->src->bytes_in_buffer = datasize; - cinfo->src->next_input_byte = static_cast(data); -} - -thread_local std::vector jpeg_status; - -Status CheckJpegExit(jpeg_decompress_struct *cinfo) { - if (!jpeg_status.empty()) { - jpeg_destroy_decompress(cinfo); - Status s = jpeg_status[0]; - jpeg_status.clear(); - return s; - } - return Status::OK(); -} - -static Status JpegReadScanlines(jpeg_decompress_struct *const cinfo, int max_scanlines_to_read, JSAMPLE *buffer, - int buffer_size, int crop_w, int crop_w_aligned, int offset, int stride) { - // scanlines will be read to this buffer first, must have the number - // of components equal to the number of components in the image - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / cinfo->output_components) > crop_w_aligned, - "JpegReadScanlines: multiplication out of bounds."); - int64_t scanline_size = crop_w_aligned * cinfo->output_components; - std::vector scanline(scanline_size); - JSAMPLE *scanline_ptr = &scanline[0]; - while (cinfo->output_scanline < static_cast(max_scanlines_to_read)) { - int num_lines_read = 0; - try { - num_lines_read = static_cast(jpeg_read_scanlines(cinfo, &scanline_ptr, 1)); - RETURN_IF_NOT_OK(CheckJpegExit(cinfo)); - } catch (std::runtime_error &e) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: image decode failed."); - } - if (cinfo->out_color_space == JCS_CMYK && num_lines_read > 0) { - for (int i = 0; i < crop_w; ++i) { - const int cmyk_pixel = 4 * i + offset; - const int c = scanline_ptr[cmyk_pixel]; - const int m = scanline_ptr[cmyk_pixel + 1]; - const int y = scanline_ptr[cmyk_pixel + 2]; - const int k = scanline_ptr[cmyk_pixel + 3]; - int r, g, b; - if (cinfo->saw_Adobe_marker) { - r = (k * c) / kMaxBitValue; - g = (k * m) / kMaxBitValue; - b = (k * y) / kMaxBitValue; - } else { - r = (kMaxBitValue - c) * (kMaxBitValue - k) / kMaxBitValue; - g = (kMaxBitValue - m) * (kMaxBitValue - k) / kMaxBitValue; - b = (kMaxBitValue - y) * (kMaxBitValue - k) / kMaxBitValue; - } - buffer[kDefaultImageChannel * i + kRIndex] = r; - buffer[kDefaultImageChannel * i + kGIndex] = g; - buffer[kDefaultImageChannel * i + kBIndex] = b; - } - } else if (num_lines_read > 0) { - int copy_status = memcpy_s(buffer, buffer_size, scanline_ptr + offset, stride); - if (copy_status != 0) { - jpeg_destroy_decompress(cinfo); - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: memcpy failed."); - } - } else { - jpeg_destroy_decompress(cinfo); - std::string err_msg = "[Internal ERROR] Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - buffer += stride; - buffer_size = buffer_size - stride; - } - return Status::OK(); -} - -static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { - switch (cinfo->num_components) { - case 1: - // we want to output 3 components if it's grayscale - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 3: - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 4: - // Need to manually convert to RGB - cinfo->out_color_space = JCS_CMYK; - return Status::OK(); - default: - jpeg_destroy_decompress(cinfo); - std::string err_msg = "[Internal ERROR] Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } -} - -void JpegErrorExitCustom(j_common_ptr cinfo) { - char jpeg_error_msg[JMSG_LENGTH_MAX]; - (*(cinfo->err->format_message))(cinfo, jpeg_error_msg); - // we encounter core dump when execute jpeg_start_decompress at arm platform, - // so we collect Status instead of throwing exception. - jpeg_status.emplace_back( - STATUS_ERROR(StatusCode::kMDUnexpectedError, "Error raised by libjpeg: " + std::string(jpeg_error_msg))); -} - -Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int crop_x, int crop_y, - int crop_w, int crop_h) { - struct jpeg_decompress_struct cinfo {}; - auto DestroyDecompressAndReturnError = [&cinfo](const std::string &err) { - jpeg_destroy_decompress(&cinfo); - RETURN_STATUS_UNEXPECTED(err); - }; - struct JpegErrorManagerCustom jerr {}; - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = JpegErrorExitCustom; - try { - jpeg_create_decompress(&cinfo); - if (input->type() == DataType::DE_BYTES) { - uint32_t len = 0; - RETURN_IF_NOT_OK(input->GetStringLength(&len)); - JpegSetSource(&cinfo, input->GetStringsBuffer(), len); - } else { - JpegSetSource(&cinfo, input->GetMutableBuffer(), input->SizeInBytes()); - } - (void)jpeg_read_header(&cinfo, TRUE); - RETURN_IF_NOT_OK(JpegSetColorSpace(&cinfo)); - jpeg_calc_output_dimensions(&cinfo); - RETURN_IF_NOT_OK(CheckJpegExit(&cinfo)); - } catch (std::runtime_error &e) { - return DestroyDecompressAndReturnError(e.what()); - } - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_w) > crop_x, - "JpegCropAndDecode: addition(crop x and crop width) out of bounds, got crop x:" + - std::to_string(crop_x) + ", and crop width:" + std::to_string(crop_w)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_h) > crop_y, - "JpegCropAndDecode: addition(crop y and crop height) out of bounds, got crop y:" + - std::to_string(crop_y) + ", and crop height:" + std::to_string(crop_h)); - if (crop_x == 0 && crop_y == 0 && crop_w == 0 && crop_h == 0) { - crop_w = static_cast(cinfo.output_width); - crop_h = static_cast(cinfo.output_height); - } else if (crop_w == 0 || static_cast(crop_w + crop_x) > cinfo.output_width || crop_h == 0 || - static_cast(crop_h + crop_y) > cinfo.output_height) { - return DestroyDecompressAndReturnError( - "Crop: invalid crop size, corresponding crop value equal to 0 or too big, got crop width: " + - std::to_string(crop_w) + ", crop height:" + std::to_string(crop_h) + - ", and crop x coordinate:" + std::to_string(crop_x) + ", crop y coordinate:" + std::to_string(crop_y)); - } - const int mcu_size = cinfo.min_DCT_scaled_size; - CHECK_FAIL_RETURN_UNEXPECTED(mcu_size != 0, "JpegCropAndDecode: divisor mcu_size is zero."); - unsigned int crop_x_aligned = (crop_x / mcu_size) * mcu_size; - unsigned int crop_w_aligned = crop_w + crop_x - crop_x_aligned; - try { - bool status = jpeg_start_decompress(&cinfo); - CHECK_FAIL_RETURN_UNEXPECTED(status, "JpegCropAndDecode: fail to decode, jpeg maybe a multi-scan file or broken."); - RETURN_IF_NOT_OK(CheckJpegExit(&cinfo)); - jpeg_crop_scanline(&cinfo, &crop_x_aligned, &crop_w_aligned); - RETURN_IF_NOT_OK(CheckJpegExit(&cinfo)); - } catch (std::runtime_error &e) { - return DestroyDecompressAndReturnError(e.what()); - } - JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); - // three number of output components, always convert to RGB and output - constexpr int kOutNumComponents = 3; - TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); - const int buffer_size = static_cast(output_tensor->SizeInBytes()); - JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, - "JpegCropAndDecode: addition out of bounds."); - const int max_scanlines_to_read = static_cast(skipped_scanlines) + crop_h; - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, - "JpegCropAndDecode: multiplication out of bounds."); - const int stride = crop_w * kOutNumComponents; - // offset is calculated for scanlines read from the image, therefore - // has the same number of components as the image - int minius_value = crop_x - static_cast(crop_x_aligned); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / minius_value) > cinfo.output_components, - "JpegCropAndDecode: multiplication out of bounds."); - const int offset = minius_value * cinfo.output_components; - RETURN_IF_NOT_OK( - JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); - *output = output_tensor; - jpeg_destroy_decompress(&cinfo); - return Status::OK(); -} - -Status Rescale(const std::shared_ptr &input, std::shared_ptr *output, float rescale, float shift) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rescale: load image failed."); - } - cv::Mat input_image = input_cv->mat(); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), DataType(DataType::DE_FLOAT32), &output_cv)); - try { - input_image.convertTo(output_cv->mat(), CV_32F, rescale, shift); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Rescale: " + std::string(e.what())); - } - return Status::OK(); -} - -Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Crop: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Crop", input_cv->Rank())); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - y) > h, - "Crop: addition(x and height) out of bounds, got height:" + std::to_string(h) + - ", and coordinate y:" + std::to_string(y)); - // account for integer overflow - if (y < 0 || (y + h) > input_cv->shape()[0] || (y + h) < 0) { - RETURN_STATUS_UNEXPECTED( - "Crop: invalid y coordinate value for crop, y coordinate value exceeds the boundary of the image, got y: " + - std::to_string(y)); - } - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x) > w, "Crop: addition out of bounds."); - // account for integer overflow - if (x < 0 || (x + w) > input_cv->shape()[1] || (x + w) < 0) { - RETURN_STATUS_UNEXPECTED( - "Crop: invalid x coordinate value for crop, " - "x coordinate value exceeds the boundary of the image, got x: " + - std::to_string(x)); - } - try { - TensorShape shape{h, w}; - if (input_cv->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - shape = shape.AppendDim(num_channels); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); - cv::Rect roi(x, y, w, h); - (input_cv->mat())(roi).copyTo(output_cv->mat()); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Crop: " + std::string(e.what())); - } -} - -Status ConvertColor(const std::shared_ptr &input, std::shared_ptr *output, ConvertMode convert_mode) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - RETURN_IF_NOT_OK(ValidateImageRank("ConvertColor", input_cv->Rank())); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] ConvertColor: load image failed."); - } - if (input_cv->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - if (num_channels != kMinImageChannel && num_channels != kDefaultImageChannel && - num_channels != kMaxImageChannel) { - RETURN_STATUS_UNEXPECTED("ConvertColor: number of channels of image should be 1, 3, 4, but got:" + - std::to_string(num_channels)); - } - } - std::vector node; - RETURN_IF_NOT_OK(GetConvertShape(convert_mode, input_cv, &node)); - if (node.empty()) { - RETURN_STATUS_UNEXPECTED( - "ConvertColor: convert mode must be in ConvertMode, which mainly includes conversion " - "between RGB, BGR, GRAY, RGBA etc."); - } - TensorShape out_shape = TensorShape(node); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(convert_mode)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("ConvertColor: " + std::string(e.what())); - } -} - -Status HwcToChw(const std::shared_ptr &input, std::shared_ptr *output) { - try { - if (input->Rank() == kMinImageRank) { - // If input tensor is 2D, we assume we have hw dimensions - *output = input; - return Status::OK(); - } - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] HWC2CHW: load image failed."); - } - if (input_cv->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("HWC2CHW: image shape should be or , but got rank: " + - std::to_string(input_cv->Rank())); - } - cv::Mat output_img; - - int height = static_cast(input_cv->shape()[0]); - int width = static_cast(input_cv->shape()[1]); - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(TensorShape{num_channels, height, width}, input_cv->type(), &output_cv)); - - for (int i = 0; i < num_channels; ++i) { - cv::Mat mat; - RETURN_IF_NOT_OK(output_cv->MatAtIndex({i}, &mat)); - cv::extractChannel(input_cv->mat(), mat, i); - } - *output = std::move(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("HWC2CHW: " + std::string(e.what())); - } -} - -Status MaskWithTensor(const std::shared_ptr &sub_mat, std::shared_ptr *input, int x, int y, - int crop_width, int crop_height, ImageFormat image_format) { - constexpr int64_t input_shape = 2; - if (image_format == ImageFormat::HWC) { - if (CheckTensorShape(*input, input_shape)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "input shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - if (CheckTensorShape(sub_mat, input_shape)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "sub_mat shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - int number_of_channels = static_cast((*input)->shape()[kChannelIndexHWC]); - for (int i = 0; i < crop_width; i++) { - for (int j = 0; j < crop_height; j++) { - for (int c = 0; c < number_of_channels; c++) { - RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {j, i, c}, {y + j, x + i, c})); - } - } - } - } else if (image_format == ImageFormat::CHW) { - if (CheckTensorShape(*input, 0)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "input shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - if (CheckTensorShape(sub_mat, 0)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "sub_mat shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - int number_of_channels = static_cast((*input)->shape()[0]); - for (int i = 0; i < crop_width; i++) { - for (int j = 0; j < crop_height; j++) { - for (int c = 0; c < number_of_channels; c++) { - RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {c, j, i}, {c, y + j, x + i})); - } - } - } - } else if (image_format == ImageFormat::HW) { - if ((*input)->Rank() != kMinImageRank) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "input shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - if (sub_mat->Rank() != kMinImageRank) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "sub_mat shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - for (int i = 0; i < crop_width; i++) { - for (int j = 0; j < crop_height; j++) { - RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {j, i}, {y + j, x + i})); - } - } - } else { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "image format must be , , or , got shape:" + - (*input)->shape().ToString()); - } - return Status::OK(); -} - -Status CopyTensorValue(const std::shared_ptr &source_tensor, std::shared_ptr *dest_tensor, - const std::vector &source_indx, const std::vector &dest_indx) { - if (source_tensor->type() != (*dest_tensor)->type()) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: CopyTensorValue failed: " - "source and destination tensor must have the same type."); - } - if (source_tensor->type() == DataType::DE_UINT8) { - uint8_t pixel_value = 0; - RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); - RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); - } else if (source_tensor->type() == DataType::DE_FLOAT32) { - float pixel_value = 0; - RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); - RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); - } else { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: CopyTensorValue failed: " - "Tensor type is not supported. Tensor type must be float32 or uint8."); - } - return Status::OK(); -} - -Status SwapRedAndBlue(std::shared_ptr input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "SwapRedBlue", {3, 5, 11})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); - CHECK_FAIL_RETURN_UNEXPECTED( - input_cv->shape().Size() > kChannelIndexHWC, - "SwapRedAndBlue: rank of input data should be greater than:" + std::to_string(kChannelIndexHWC) + - ", but got:" + std::to_string(input_cv->shape().Size())); - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - if (input_cv->shape().Size() != kDefaultImageRank || num_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("SwapRedBlue: image shape should be in format, but got:" + - input_cv->shape().ToString()); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_BGR2RGB)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("SwapRedBlue: " + std::string(e.what())); - } -} - -Status CropAndResize(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, - int crop_height, int crop_width, int target_height, int target_width, InterpolationMode mode) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] CropAndResize: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("CropAndResize", input_cv->Rank())); - // image too large or too small, 1000 is arbitrary here to prevent opencv from segmentation fault - const uint32_t kCropShapeLimits = 1000; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_height, - "CropAndResize: crop_height out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_width, - "CropAndResize: crop_width out of bounds."); - if (crop_height == 0 || crop_width == 0 || target_height == 0 || target_height > crop_height * kCropShapeLimits || - target_width == 0 || target_width > crop_width * kCropShapeLimits) { - std::string err_msg = - "CropAndResize: the resizing width or height 1) is too big, it's up to " + std::to_string(kCropShapeLimits) + - " times the original image; 2) can not be 0. Detail info is: crop_height: " + std::to_string(crop_height) + - ", crop_width: " + std::to_string(crop_width) + ", target_height: " + std::to_string(target_height) + - ", target_width: " + std::to_string(target_width); - RETURN_STATUS_UNEXPECTED(err_msg); - } - cv::Rect roi(x, y, crop_width, crop_height); - auto cv_mode = GetCVInterpolationMode(mode); - cv::Mat cv_in = input_cv->mat(); - - if (mode == InterpolationMode::kCubicPil) { - if (input_cv->shape().Size() != kDefaultImageChannel || - input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED( - "CropAndResize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + - input_cv->shape().ToString()); - } - - cv::Mat input_roi = cv_in(roi); - std::shared_ptr input_image; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_roi, input_cv->Rank(), &input_image)); - LiteMat imIn, imOut; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({target_height, target_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - int input_channel = static_cast(input_cv->shape()[kChannelIndexHWC]); - imOut.Init(target_width, target_height, input_channel, reinterpret_cast(buffer), LDataType::UINT8); - int input_height = static_cast(input_image->shape()[0]); - int input_width = static_cast(input_image->shape()[1]); - imIn.Init(input_width, input_height, input_channel, input_image->mat().data, LDataType::UINT8); - if (ResizeCubic(imIn, imOut, target_width, target_height) == false) { - RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); - } - *output = output_tensor; - return Status::OK(); - } - - TensorShape shape{target_height, target_width}; - if (input_cv->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - shape = shape.AppendDim(num_channels); - } - std::shared_ptr cvt_out; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &cvt_out)); - cv::resize(cv_in(roi), cvt_out->mat(), cv::Size(target_width, target_height), 0, 0, cv_mode); - *output = std::static_pointer_cast(cvt_out); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("CropAndResize: " + std::string(e.what())); - } -} - -Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, std::vector center, - float degree, InterpolationMode interpolation, bool expand, uint8_t fill_r, uint8_t fill_g, - uint8_t fill_b) { - try { - RETURN_IF_NOT_OK(ValidateImageRank("Rotate", input->Rank())); - dsize_t channel = 1; - RETURN_IF_NOT_OK(ImageNumChannels(input, &channel)); - CHECK_FAIL_RETURN_UNEXPECTED(channel <= kMaxImageChannel || interpolation != InterpolationMode::kCubic, - "Rotate: interpolation can not be CUBIC when image channel is greater than 4."); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rotate: load image failed."); - } - - cv::Mat input_img = input_cv->mat(); - if (input_img.cols > (MAX_INT_PRECISION * DOUBLING_FACTOR) || - input_img.rows > (MAX_INT_PRECISION * DOUBLING_FACTOR)) { - RETURN_STATUS_UNEXPECTED("Rotate: image is too large and center is not precise, got image width:" + - std::to_string(input_img.cols) + ", and image height:" + std::to_string(input_img.rows) + - ", both should be small than:" + std::to_string(MAX_INT_PRECISION * DOUBLING_FACTOR)); - } - float fx = 0, fy = 0; - if (center.empty()) { - // default to center of image - fx = (static_cast(input_img.cols) - 1.0F) * kHalf; - fy = (static_cast(input_img.rows) - 1.0F) * kHalf; - } else { - fx = center[0]; - fy = center[1]; - } - cv::Mat output_img; - cv::Scalar fill_color = cv::Scalar(fill_b, fill_g, fill_r); - // maybe don't use uint32 for image dimension here - cv::Point2f pc(fx, fy); - cv::Mat rot = cv::getRotationMatrix2D(pc, degree, 1.0); - std::shared_ptr output_cv; - if (!expand) { - // this case means that the shape doesn't change, size stays the same - // We may not need this memcpy if it is in place. - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - // using inter_nearest to comply with python default - cv::warpAffine(input_img, output_cv->mat(), rot, input_img.size(), GetCVInterpolationMode(interpolation), - cv::BORDER_CONSTANT, fill_color); - } else { - // we resize here since the shape changes - // create a new bounding box with the rotate - cv::Rect2f bbox = cv::RotatedRect(pc, input_img.size(), degree).boundingRect2f(); - rot.at(0, 2) += bbox.width / 2.0 - input_img.cols / 2.0; - rot.at(1, 2) += bbox.height / 2.0 - input_img.rows / 2.0; - // use memcpy and don't compute the new shape since openCV has a rounding problem - cv::warpAffine(input_img, output_img, rot, bbox.size(), GetCVInterpolationMode(interpolation), - cv::BORDER_CONSTANT, fill_color); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_img, input_cv->Rank(), &output_cv)); - RETURN_UNEXPECTED_IF_NULL(output_cv); - } - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); - } - return Status::OK(); -} - -template -void Normalize(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc, bool pad = false) { - // T1 is the type of input tensor, T2 is the type of output tensor - auto itr_out = (*output)->begin(); - auto itr = input->begin(); - auto end = input->end(); - int64_t num_channels; - if (is_hwc) { - num_channels = (*output)->shape()[kChannelIndexHWC]; - while (itr != end) { - for (size_t i = 0; i < num_channels - static_cast(pad); i++) { - *itr_out = static_cast((static_cast(*itr) - mean[i]) / std[i]); - ++itr_out; - ++itr; - } - } - } else { - num_channels = (*output)->shape()[kChannelIndexCHW]; - int64_t height_index = 1; - int64_t width_index = 2; - int64_t channel_len = (*output)->shape()[height_index] * (*output)->shape()[width_index]; - while (itr != end) { - for (size_t i = 0; i < num_channels - static_cast(pad); i++) { - for (int64_t j = 0; j < channel_len; j++) { - *itr_out = static_cast((static_cast(*itr) - mean[i]) / std[i]); - ++itr_out; - ++itr; - } - } - } - } -} - -template -Status Normalize_caller(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector mean_v, const std::vector std_v, bool is_hwc, bool pad) { - switch (static_cast(input->type().value())) { - case DataType::DE_BOOL: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_INT8: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_UINT8: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_INT16: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_UINT16: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_INT32: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_UINT32: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_INT64: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_UINT64: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_FLOAT16: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_FLOAT32: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - case DataType::DE_FLOAT64: - Normalize(input, output, mean_v, std_v, is_hwc, pad); - break; - default: - std::string op_name = (pad) ? "NormalizePad" : "Normalize"; - RETURN_STATUS_UNEXPECTED( - op_name + ": unsupported type, currently supported types include " + - "[bool,int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t,float16,float,double]."); - } - return Status::OK(); -} - -Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc) { - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), DataType(DataType::DE_FLOAT32), output)); - if (input->Rank() == kMinImageRank) { - RETURN_IF_NOT_OK((*output)->ExpandDim(kMinImageRank)); - } - - CHECK_FAIL_RETURN_UNEXPECTED((*output)->Rank() == kDefaultImageRank, - "Normalize: output image rank should be: " + std::to_string(kDefaultImageRank) + - ", but got: " + std::to_string((*output)->Rank())); - CHECK_FAIL_RETURN_UNEXPECTED(std.size() == mean.size(), - "Normalize: mean and std vectors are not of same size, got size of std: " + - std::to_string(std.size()) + ", and mean size: " + std::to_string(mean.size())); - int64_t channel_index; - if (is_hwc) { - channel_index = kChannelIndexHWC; - } else { - channel_index = kChannelIndexCHW; - } - // caller provided 1 mean/std value and there is more than one channel --> duplicate mean/std value - if (mean.size() == 1 && (*output)->shape()[channel_index] != 1) { - for (int64_t i = 0; i < (*output)->shape()[channel_index] - 1; i++) { - mean.push_back(mean[0]); - std.push_back(std[0]); - } - } - CHECK_FAIL_RETURN_UNEXPECTED((*output)->shape()[channel_index] == static_cast(mean.size()), - "Normalize: number of channels does not match the size of mean and std vectors, got " - "channels: " + - std::to_string((*output)->shape()[channel_index]) + - ", size of mean: " + std::to_string(mean.size())); - RETURN_IF_NOT_OK(Normalize_caller(input, output, mean, std, is_hwc, false)); - - if (input->Rank() == kMinImageRank) { - (*output)->Squeeze(); - } - return Status::OK(); -} - -Status NormalizePad(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, - std::vector std, const std::string &dtype, bool is_hwc) { - RETURN_IF_NOT_OK(ValidateImageRank("NormalizePad", input->Rank())); - int64_t channel_index = kChannelIndexCHW; - if (is_hwc) { - channel_index = kChannelIndexHWC; - } - int32_t channels = 1; - if (input->Rank() == kDefaultImageRank) { - channels = static_cast(input->shape()[channel_index]); - } - - if (is_hwc) { - TensorShape new_shape = TensorShape({input->shape()[0], input->shape()[1], channels + 1}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, DataType(dtype), output)); - RETURN_IF_NOT_OK((*output)->Zero()); - } else { - TensorShape new_shape = TensorShape({channels + 1, input->shape()[1], input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, DataType(dtype), output)); - RETURN_IF_NOT_OK((*output)->Zero()); - } - - // caller provided 1 mean/std value and there are more than one channel --> duplicate mean/std value - if (mean.size() == 1 && channels > 1) { - while (mean.size() < channels) { - mean.push_back(mean[0]); - std.push_back(std[0]); - } - } - CHECK_FAIL_RETURN_UNEXPECTED((*output)->shape()[channel_index] == static_cast(mean.size()) + 1, - "NormalizePad: number of channels does not match the size of mean and std vectors, got " - "channels: " + - std::to_string((*output)->shape()[channel_index] - 1) + - ", size of mean: " + std::to_string(mean.size())); - if (dtype == "float16") { - RETURN_IF_NOT_OK(Normalize_caller(input, output, mean, std, is_hwc, true)); - } else { - RETURN_IF_NOT_OK(Normalize_caller(input, output, mean, std, is_hwc, true)); - } - if (input->Rank() == kMinImageRank) { - (*output)->Squeeze(); - } - return Status::OK(); -} - -Status AdjustBrightness(const std::shared_ptr &input, std::shared_ptr *output, float alpha) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "AdjustBrightness", {1, 2, 3, 4, 5, 6, 10, 11, 12}, {2, 3}, {1, 3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustBrightness: load image failed."); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - output_cv->mat() = input_img * alpha; - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AdjustBrightness: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AdjustContrast(const std::shared_ptr &input, std::shared_ptr *output, float alpha) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "AdjustContrast", {3, 5, 11}, {3}, {3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustContrast: load image failed."); - } - cv::Mat gray, output_img; - cv::cvtColor(input_img, gray, CV_RGB2GRAY); - auto mean_img = cv::mean(gray).val[0]; - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - // thread safe: change cv::Mat::zeros to cv::Mat + setTo - output_img = cv::Mat(input_img.rows, input_img.cols, input_img.depth()); - output_img.setTo(cv::Scalar::all(0)); - output_img = output_img + mean_img; - cv::cvtColor(output_img, output_img, CV_GRAY2RGB); - output_img = output_img * (1.0 - alpha) + input_img * alpha; - output_img.copyTo(output_cv->mat()); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AdjustContrast: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AdjustGamma(const std::shared_ptr &input, std::shared_ptr *output, float gamma, float gain) { - try { - int num_channels = 1; - if (input->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED("AdjustGamma: input tensor is not in shape of <...,H,W,C> or , got shape:" + - input->shape().ToString()); - } - if (input->Rank() > 2) { - num_channels = static_cast(input->shape()[-1]); - } - if (num_channels != 1 && num_channels != 3) { - RETURN_STATUS_UNEXPECTED("AdjustGamma: channel of input image should be 1 or 3, but got: " + - std::to_string(num_channels)); - } - if (input->type().IsFloat()) { - for (auto itr = input->begin(); itr != input->end(); itr++) { - *itr = pow((*itr) * gain, gamma); - *itr = std::min(std::max((*itr), 0.0f), 1.0f); - } - *output = input; - } else { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input, output)); - std::shared_ptr input_cv = CVTensor::AsCVTensor(*output); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustGamma: load image failed."); - } - cv::Mat input_img = input_cv->mat(); - uchar LUT[256] = {}; - auto kMaxPixelValueFloat = static_cast(kMaxBitValue); - for (int i = 0; i <= kMaxBitValue; i++) { - float f = static_cast(i) / kMaxPixelValueFloat; - f = pow(f, gamma); - LUT[i] = - static_cast(floor(std::min(f * (kMaxPixelValueFloat + 1.f - 1e-3f) * gain, kMaxPixelValueFloat))); - } - if (input_img.channels() == 1) { - cv::MatIterator_ it = input_img.begin(); - cv::MatIterator_ it_end = input_img.end(); - for (; it != it_end; ++it) { - *it = LUT[(*it)]; - } - } else { - cv::MatIterator_ it = input_img.begin(); - cv::MatIterator_ it_end = input_img.end(); - for (; it != it_end; ++it) { - (*it)[0] = LUT[(*it)[0]]; - (*it)[1] = LUT[(*it)[1]]; - (*it)[2] = LUT[(*it)[2]]; - } - } - *output = std::static_pointer_cast(input_cv); - } - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AdjustGamma: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AdjustSharpness(const std::shared_ptr &input, std::shared_ptr *output, float alpha) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Sharpness: load image failed."); - } - - if (input_cv->Rank() == 1 || input_cv->mat().dims > 2) { - RETURN_STATUS_UNEXPECTED("Sharpness: shape of input is not or , but got rank: " + - std::to_string(input_cv->Rank())); - } - - /// creating a smoothing filter. 1, 1, 1, - /// 1, 5, 1, - /// 1, 1, 1 - - const float filterMid = 5.0; - const float filterSum = 13.0; - cv::Mat filter = cv::Mat(3, 3, CV_32F, cv::Scalar::all(1.0 / filterSum)); - filter.at(1, 1) = filterMid / filterSum; - - /// applying filter on channels - cv::Mat result = cv::Mat(); - cv::filter2D(input_img, result, -1, filter); - - auto height = static_cast(input_cv->shape()[0]); - auto width = static_cast(input_cv->shape()[1]); - - /// restoring the edges - input_img.row(0).copyTo(result.row(0)); - input_img.row(height - 1).copyTo(result.row(height - 1)); - input_img.col(0).copyTo(result.col(0)); - input_img.col(width - 1).copyTo(result.col(width - 1)); - - /// blend based on alpha : (alpha_ *input_img) + ((1.0-alpha_) * result); - cv::addWeighted(input_img, alpha, result, 1.0 - alpha, 0.0, result); - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); - RETURN_UNEXPECTED_IF_NULL(output_cv); - - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Sharpness: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AutoContrast(const std::shared_ptr &input, std::shared_ptr *output, float cutoff, - const std::vector &ignore) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AutoContrast: load image failed."); - } - if (input_cv->Rank() != kDefaultImageRank && input_cv->Rank() != kMinImageRank) { - std::string err_msg = "AutoContrast: image rank should be 2 or 3, but got: " + std::to_string(input_cv->Rank()); - if (input_cv->Rank() == 1) { - err_msg = err_msg + ", may need to do Decode operation first."; - } - RETURN_STATUS_UNEXPECTED("AutoContrast: image rank should be 2 or 3, but got: " + - std::to_string(input_cv->Rank())); - } - // Reshape to extend dimension if rank is 2 for algorithm to work. then reshape output to be of rank 2 like input - auto input_rank = input_cv->Rank(); - if (input_cv->Rank() == kMinImageRank) { - RETURN_IF_NOT_OK(input_cv->ExpandDim(kMinImageRank)); - } - // Get number of channels and image matrix - std::size_t num_of_channels = input_cv->shape()[static_cast(kChannelIndexHWC)]; - if (num_of_channels != kMinImageChannel && num_of_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("AutoContrast: channel of input image should be 1 or 3, but got: " + - std::to_string(num_of_channels)); - } - cv::Mat image = input_cv->mat(); - // Separate the image to channels - std::vector planes(num_of_channels); - cv::split(image, planes); - cv::Mat b_hist, g_hist, r_hist; - // Establish the number of bins and set variables for histogram - int32_t hist_size = 256; - int32_t channels = 0; - float range[] = {0, 256}; - const float *hist_range[] = {range}; - bool uniform = true, accumulate = false; - // Set up lookup table for LUT(Look up table algorithm) - std::vector table; - std::vector image_result; - for (std::size_t layer = 0; layer < planes.size(); layer++) { - // Reset lookup table - table = std::vector{}; - // Calculate Histogram for channel - cv::Mat hist; - cv::calcHist(&planes[layer], 1, &channels, cv::Mat(), hist, 1, &hist_size, hist_range, uniform, accumulate); - hist.convertTo(hist, CV_32SC1); - std::vector hist_vec; - hist.col(0).copyTo(hist_vec); - // Ignore values in ignore - for (const auto &item : ignore) { - hist_vec[item] = 0; - } - int32_t hi = kMaxBitValue; - int32_t lo = 0; - RETURN_IF_NOT_OK(ComputeUpperAndLowerPercentiles(&hist_vec, cutoff, cutoff, &hi, &lo)); - if (hi <= lo) { - for (int32_t i = 0; i < 256; i++) { - table.push_back(i); - } - } else { - const float scale = static_cast(kMaxBitValue) / static_cast(hi - lo); - const float offset = static_cast(-1 * lo) * scale; - for (int32_t i = 0; i < 256; i++) { - auto ix = static_cast(static_cast(i) * scale + offset); - ix = std::max(ix, 0); - ix = std::min(ix, kMaxBitValue); - table.push_back(ix); - } - } - cv::Mat result_layer; - cv::LUT(planes[layer], table, result_layer); - image_result.push_back(result_layer); - } - cv::Mat result; - cv::merge(image_result, result); - result.convertTo(result, input_cv->mat().type()); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); - (*output) = std::static_pointer_cast(output_cv); - if (input_rank == kMinImageRank) { - (*output)->Squeeze(); - } - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AutoContrast: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AdjustSaturation(const std::shared_ptr &input, std::shared_ptr *output, float alpha) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "AdjustSaturation", {3, 5, 11}, {3}, {3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustSaturation: load image failed."); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - cv::Mat output_img = output_cv->mat(); - cv::Mat gray; - cv::cvtColor(input_img, gray, CV_RGB2GRAY); - cv::cvtColor(gray, output_img, CV_GRAY2RGB); - output_img = output_img * (1.0 - alpha) + input_img * alpha; - output_img.copyTo(output_cv->mat()); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AdjustSaturation: " + std::string(e.what())); - } - return Status::OK(); -} - -Status AdjustHue(const std::shared_ptr &input, std::shared_ptr *output, float hue) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "AdjustHue", {3, 11}, {3}, {3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustHue: load image failed."); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - cv::Mat output_img; - cv::cvtColor(input_img, output_img, CV_RGB2HSV_FULL); - for (int x = 0; x < output_img.cols; x++) { - for (int y = 0; y < output_img.rows; y++) { - uint8_t cur1 = output_img.at(cv::Point(x, y))[0]; - uint8_t h_hue = 0; - h_hue = static_cast(hue * kMaxBitValue); - cur1 += h_hue; - output_img.at(cv::Point(x, y))[0] = cur1; - } - } - cv::cvtColor(output_img, output_cv->mat(), CV_HSV2RGB_FULL); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("AdjustHue: " + std::string(e.what())); - } - return Status::OK(); -} - -Status Equalize(const std::shared_ptr &input, std::shared_ptr *output) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Equalize: load image failed."); - } - if (input_cv->Rank() != kDefaultImageRank && input_cv->Rank() != kMinImageRank) { - RETURN_STATUS_UNEXPECTED("Equalize: image rank should be 2 or 3, but got: " + std::to_string(input_cv->Rank())); - } - // For greyscale images, extend dimension if rank is 2 and reshape output to be of rank 2. - auto input_rank = input_cv->Rank(); - if (input_cv->Rank() == kMinImageRank) { - RETURN_IF_NOT_OK(input_cv->ExpandDim(kMinImageRank)); - } - // Get number of channels and image matrix - std::size_t num_of_channels = input_cv->shape()[kChannelIndexHWC]; - if (num_of_channels != kMinImageChannel && num_of_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("Equalize: channel of input image should be 1 or 3, but got: " + - std::to_string(num_of_channels)); - } - cv::Mat image = input_cv->mat(); - // Separate the image to channels - std::vector planes(num_of_channels); - cv::split(image, planes); - // Equalize each channel separately - std::vector image_result; - for (auto &plane : planes) { - cv::Mat channel_result; - cv::equalizeHist(plane, channel_result); - image_result.push_back(channel_result); - } - cv::Mat result; - cv::merge(image_result, result); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); - (*output) = std::static_pointer_cast(output_cv); - if (input_rank == kMinImageRank) { - (*output)->Squeeze(); - } - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Equalize: " + std::string(e.what())); - } - return Status::OK(); -} - -Status Invert(const std::shared_ptr &input, std::shared_ptr *output) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Invert: load image failed."); - } - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - RETURN_UNEXPECTED_IF_NULL(output_cv); - - constexpr auto kMaxPixel = 255.0; - output_cv->mat() = cv::Scalar::all(kMaxPixel) - input_img; - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Invert: " + std::string(e.what())); - } - return Status::OK(); -} - -Status Posterize(const std::shared_ptr &input, std::shared_ptr *output, uint8_t bits) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Posterize: load image failed."); - } - if (input_cv->Rank() != 3 && input_cv->Rank() != 2) { - RETURN_STATUS_UNEXPECTED("Posterize: input image is not in shape of or , but got rank: " + - std::to_string(input_cv->Rank())); - } - uint8_t mask_value = ~(static_cast(1 << (8 - bits)) - 1); - std::vector lut_vector; - for (std::size_t i = 0; i < 256; i++) { - lut_vector.push_back(i & mask_value); - } - cv::Mat in_image = input_cv->mat(); - - cv::Mat output_img; - CHECK_FAIL_RETURN_UNEXPECTED(in_image.depth() == CV_8U || in_image.depth() == CV_8S, - "Posterize: data type of input image should be int8 or uint8, " - "but got " + - input_cv->type().ToString()); - cv::LUT(in_image, lut_vector, output_img); - std::shared_ptr result_tensor; - - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_img, input_cv->Rank(), &result_tensor)); - *output = std::static_pointer_cast(result_tensor); - return Status::OK(); -} - -Status ValidateCutOutImage(const std::shared_ptr &input, bool is_hwc, int32_t box_height, int32_t box_width) { - uint32_t channel_index = is_hwc ? kChannelIndexHWC : kChannelIndexCHW; - uint32_t height_index = is_hwc ? 0 : 1; - uint32_t width_index = is_hwc ? 1 : 2; - std::string right_shape = is_hwc ? "" : ""; - int64_t image_h = input->shape()[height_index]; - int64_t image_w = input->shape()[width_index]; - - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() > channel_index, "CutOut: shape is invalid."); - - if (input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("CutOut: image shape is not " + right_shape + - ", but got rank: " + std::to_string(input->Rank())); - } - - if (box_height > image_h || box_width > image_w) { - RETURN_STATUS_UNEXPECTED( - "CutOut: box size is too large for image erase, got box height: " + std::to_string(box_height) + - "box weight: " + std::to_string(box_width) + ", and image height: " + std::to_string(image_h) + - ", image width: " + std::to_string(image_w)); - } - return Status::OK(); -} - -uchar *GetPtr(const std::shared_ptr &tensor) { - switch (tensor->type().value()) { - case DataType::DE_BOOL: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_INT8: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_UINT8: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_INT16: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_UINT16: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_INT32: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_UINT32: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_INT64: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_UINT64: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_FLOAT16: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_FLOAT32: - return reinterpret_cast(&(*tensor->begin())); - case DataType::DE_FLOAT64: - return reinterpret_cast(&(*tensor->begin())); - default: - return nullptr; - } -} - -Status CutOut(const std::shared_ptr &input, std::shared_ptr *output, int32_t box_height, - int32_t box_width, int32_t num_patches, bool bounded, bool random_color, std::mt19937 *rnd, - std::vector fill_colors, bool is_hwc) { - try { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input, output)); - std::shared_ptr input_cv = CVTensor::AsCVTensor(*output); - RETURN_IF_NOT_OK(ValidateCutOutImage(input_cv, is_hwc, box_height, box_width)); - uint32_t channel_index = is_hwc ? kChannelIndexHWC : kChannelIndexCHW; - uint32_t height_index = is_hwc ? 0 : 1; - uint32_t width_index = is_hwc ? 1 : 2; - uint64_t num_channels = input_cv->shape()[channel_index]; - int64_t image_h = input_cv->shape()[height_index]; - int64_t image_w = input_cv->shape()[width_index]; - uint8_t type_size = input_cv->type().SizeInBytes(); - // for random color - std::normal_distribution normal_distribution(0, 1); - std::uniform_int_distribution height_distribution_bound(0, static_cast(image_h) - box_height); - std::uniform_int_distribution width_distribution_bound(0, static_cast(image_w) - box_width); - std::uniform_int_distribution height_distribution_unbound(0, static_cast(image_h) + box_height); - std::uniform_int_distribution width_distribution_unbound(0, static_cast(image_w) + box_width); - - if (fill_colors.empty()) { - fill_colors = std::vector(num_channels, 0); - } - CHECK_FAIL_RETURN_UNEXPECTED(fill_colors.size() == num_channels, - "Number of fill colors (" + std::to_string(fill_colors.size()) + - ") does not match the number of channels (" + std::to_string(num_channels) + ")."); - // core logic - // update values based on random erasing or cutout - for (int32_t i = 0; i < num_patches; i++) { - // rows in cv mat refers to the height of the cropped box - // we determine h_start and w_start using two different distributions as erasing is used by two different - // image augmentations. The bounds are also different in each case. - int32_t h_start = (bounded) ? height_distribution_bound(*rnd) : (height_distribution_unbound(*rnd) - box_height); - int32_t w_start = (bounded) ? width_distribution_bound(*rnd) : (width_distribution_unbound(*rnd) - box_width); - - int64_t max_width = (w_start + box_width > image_w) ? image_w : w_start + box_width; - int64_t max_height = (h_start + box_height > image_h) ? image_h : h_start + box_height; - // check for starting range >= 0, here the start range is checked after for cut out, for random erasing - // w_start and h_start will never be less than 0. - h_start = (h_start < 0) ? 0 : h_start; - w_start = (w_start < 0) ? 0 : w_start; - - if (is_hwc) { - uchar *buffer = GetPtr(input_cv); - int64_t num_bytes = type_size * static_cast(num_channels) * (max_width - w_start); - for (int x = h_start; x < max_height; x++) { - auto ret = memset_s(buffer + (x * image_w + w_start) * num_channels * type_size, num_bytes, 0, num_bytes); - if (ret != EOK) { - RETURN_STATUS_UNEXPECTED("CutOut: memset_s failed for HWC scenario."); - } - } - } else { - int64_t num_bytes = type_size * (max_width - w_start); - for (uint64_t c = 0; c < num_channels; c++) { - uchar *buffer = GetPtr(input_cv) + (type_size * c * image_h * image_w); - for (int x = h_start; x < max_height; x++) { - auto ret = memset_s(buffer + (x * image_w + w_start) * type_size, num_bytes, 0, num_bytes); - if (ret != EOK) { - RETURN_STATUS_UNEXPECTED("CutOut: memset_s failed for CHW scenario."); - } - } - } - } - } - - *output = std::static_pointer_cast(input_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("CutOut: " + std::string(e.what())); - } - - return Status::OK(); -} - -Status Erase(const std::shared_ptr &input, std::shared_ptr *output, int32_t top, int32_t left, - int32_t height, int32_t width, const std::vector &value, bool inplace) { - try { - std::vector size; - RETURN_IF_NOT_OK(ImageSize(input, &size)); - int64_t image_h = size[kHeightIndex]; - int64_t image_w = size[kWidthIndex]; - if (height > image_h || width > image_w) { - RETURN_STATUS_UNEXPECTED( - "Erase: box size is too large for image erase, got box height: " + std::to_string(height) + - "box weight: " + std::to_string(width) + ", and image height: " + std::to_string(image_h) + - ", image width: " + std::to_string(image_w)); - } - - std::shared_ptr input_cv; - if (!inplace) { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input, output)); - input_cv = CVTensor::AsCVTensor(*output); - } else { - input_cv = CVTensor::AsCVTensor(input); - } - cv::Mat input_img = input_cv->mat(); - - int32_t h_start = top; - int32_t w_start = left; - h_start = (h_start < 0) ? 0 : h_start; - w_start = (w_start < 0) ? 0 : w_start; - - int32_t max_width = (w_start + width > image_w) ? static_cast(image_w) : w_start + width; - int32_t max_height = (h_start + height > image_h) ? static_cast(image_h) : h_start + height; - int32_t true_width = max_width - w_start; - int32_t true_height = max_height - h_start; - - float fill_r = value[kRIndex]; - float fill_g = value[kRIndex]; - float fill_b = value[kRIndex]; - const size_t kMaxFillValuesSize = 3; - if (value.size() == kMaxFillValuesSize) { - fill_r = value[kRIndex]; - fill_g = value[kGIndex]; - fill_b = value[kBIndex]; - } - - cv::Rect idx = cv::Rect(w_start, h_start, true_width, true_height); - cv::Scalar fill_color = cv::Scalar(fill_r, fill_g, fill_b); - (void)input_img(idx).setTo(fill_color); - - if (!inplace) { - *output = std::static_pointer_cast(input_cv); - } else { - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &output_cv)); - *output = std::static_pointer_cast(output_cv); - } - - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Erase: " + std::string(e.what())); - } - - return Status::OK(); -} - -Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, - const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, - uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "Pad", {1, 2, 3, 4, 5, 6, 10, 11, 12}, {2, 3}, {1, 3})); - - // input image - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Pad: load image failed."); - } - - // get the border type in openCV - auto b_type = GetCVBorderType(border_types); - // output image - cv::Mat out_image; - if (b_type == cv::BORDER_CONSTANT) { - cv::Scalar fill_color = cv::Scalar(fill_r, fill_g, fill_b); - cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type, fill_color); - } else { - cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(out_image, input_cv->Rank(), &output_cv)); - // pad the dimension if shape information is only 2 dimensional, this is grayscale - if (input_cv->Rank() == kDefaultImageRank && input_cv->shape()[kChannelIndexHWC] == kMinImageChannel && - output_cv->Rank() == kMinImageRank) { - RETURN_IF_NOT_OK(output_cv->ExpandDim(kChannelIndexHWC)); - } - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Pad: " + std::string(e.what())); - } -} - -Status Perspective(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "Perspective", {1, 2, 3, 4, 5, 6, 10, 11, 12}, {2, 3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Perspective: load image failed."); - } - const int kListSize = 4; - // Get Point - cv::Point2f cv_src_point[kListSize]; - cv::Point2f cv_dst_point[kListSize]; - for (int i = 0; i < kListSize; i++) { - cv_src_point[i] = cv::Point2f(static_cast(start_points[i][0]), static_cast(start_points[i][1])); - cv_dst_point[i] = cv::Point2f(static_cast(end_points[i][0]), static_cast(end_points[i][1])); - } - - // Perspective Operation - std::shared_ptr output_cv; - cv::Mat M = cv::getPerspectiveTransform(cv_src_point, cv_dst_point, cv::DECOMP_LU); - cv::Mat src_img = input_cv->mat(); - - cv::Mat dst_img; - cv::warpPerspective(src_img, dst_img, M, src_img.size(), GetCVInterpolationMode(interpolation)); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(dst_img, input_cv->Rank(), &output_cv)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Perspective: " + std::string(e.what())); - } -} - -Status RandomLighting(const std::shared_ptr &input, std::shared_ptr *output, float rnd_r, float rnd_g, - float rnd_b) { - try { - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input, output)); - std::shared_ptr input_cv = CVTensor::AsCVTensor(*output); - cv::Mat input_img = input_cv->mat(); - - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED( - "RandomLighting: Cannot convert from OpenCV type, unknown " - "CV type. Currently supported data type: [int8, uint8, int16, uint16, " - "int32, float16, float32, float64]."); - } - - if (input_cv->Rank() != kDefaultImageRank || input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED( - "RandomLighting: input tensor is not in shape of or channel is not 3, got rank: " + - std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(input_cv->shape()[kChannelIndexHWC])); - } - auto input_type = input->type(); - CHECK_FAIL_RETURN_UNEXPECTED(input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && - input_type != DataType::DE_INT64 && !input_type.IsString(), - "RandomLighting: invalid tensor type of uint32, int64, uint64, string or bytes."); - - std::vector> eig = {{55.46 * -0.5675, 4.794 * 0.7192, 1.148 * 0.4009}, - {55.46 * -0.5808, 4.794 * -0.0045, 1.148 * -0.8140}, - {55.46 * -0.5836, 4.794 * -0.6948, 1.148 * 0.4203}}; - - float pca_r = eig[0][0] * rnd_r + eig[0][1] * rnd_g + eig[0][2] * rnd_b; - float pca_g = eig[1][0] * rnd_r + eig[1][1] * rnd_g + eig[1][2] * rnd_b; - float pca_b = eig[2][0] * rnd_r + eig[2][1] * rnd_g + eig[2][2] * rnd_b; - for (int row = 0; row < input_img.rows; row++) { - for (int col = 0; col < input_img.cols; col++) { - auto r = static_cast(input_img.at(row, col)[0]); - auto g = static_cast(input_img.at(row, col)[1]); - auto b = static_cast(input_img.at(row, col)[2]); - input_img.at(row, col)[kRIndex] = cv::saturate_cast(r + pca_r); - input_img.at(row, col)[kGIndex] = cv::saturate_cast(g + pca_g); - input_img.at(row, col)[kBIndex] = cv::saturate_cast(b + pca_b); - } - } - - *output = std::static_pointer_cast(input_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RandomLighting: " + std::string(e.what())); - } -} - -Status RgbaToRgb(const std::shared_ptr &input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "RgbaToRgb", {3, 5, 11})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (input_cv->shape().Size() != kDefaultImageChannel || input_cv->shape()[kChannelIndexHWC] != kMaxImageChannel) { - std::string err_msg = - "RgbaToRgb: rank of image is not: " + std::to_string(kDefaultImageChannel) + - ", but got: " + std::to_string(input_cv->shape().Size()) + - ", or channels of image should be 4, but got: " + std::to_string(input_cv->shape()[kChannelIndexHWC]); - RETURN_STATUS_UNEXPECTED(err_msg); - } - TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1], 3}); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGBA2RGB)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RgbaToRgb: " + std::string(e.what())); - } -} - -Status RgbaToBgr(const std::shared_ptr &input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "RgbaToBgr", {3, 5, 11})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (input_cv->shape().Size() != kDefaultImageChannel || input_cv->shape()[kChannelIndexHWC] != kMaxImageChannel) { - std::string err_msg = - "RgbaToBgr: rank of image is not: " + std::to_string(kDefaultImageChannel) + - ", but got: " + std::to_string(input_cv->shape().Size()) + - ", or channels of image should be 4, but got: " + std::to_string(input_cv->shape()[kChannelIndexHWC]); - RETURN_STATUS_UNEXPECTED(err_msg); - } - TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1], 3}); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGBA2BGR)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RgbaToBgr: " + std::string(e.what())); - } -} - -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "RgbToBgr", {3, 4, 5, 6, 10, 11, 12})); - auto input_type = input->type(); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] RgbToBgr: load image failed."); - } - if (input_cv->Rank() != kDefaultImageRank || input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: input tensor is not in shape of or channel is not 3, got rank: " + - std::to_string(input_cv->Rank()) + - ", and channel: " + std::to_string(input_cv->shape()[2])); - } - - cv::Mat image = input_cv->mat().clone(); - if (input_type == DataType::DE_FLOAT16 || input_type == DataType::DE_INT16 || input_type == DataType::DE_UINT16) { - for (int i = 0; i < input_cv->mat().rows; ++i) { - auto *p1 = input_cv->mat().ptr(i); - auto *p2 = image.ptr(i); - for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][kBIndex] = p1[j][kRIndex]; - p2[j][kGIndex] = p1[j][kGIndex]; - p2[j][kRIndex] = p1[j][kBIndex]; - } - } - } else if (input_type == DataType::DE_FLOAT32 || input_type == DataType::DE_INT32) { - for (int i = 0; i < input_cv->mat().rows; ++i) { - auto *p1 = input_cv->mat().ptr(i); - auto *p2 = image.ptr(i); - for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][kBIndex] = p1[j][kRIndex]; - p2[j][kGIndex] = p1[j][kGIndex]; - p2[j][kRIndex] = p1[j][kBIndex]; - } - } - } else if (input_type == DataType::DE_FLOAT64) { - for (int i = 0; i < input_cv->mat().rows; ++i) { - auto *p1 = input_cv->mat().ptr(i); - auto *p2 = image.ptr(i); - for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][kBIndex] = p1[j][kRIndex]; - p2[j][kGIndex] = p1[j][kGIndex]; - p2[j][kRIndex] = p1[j][kBIndex]; - } - } - } else { - cv::cvtColor(input_cv->mat(), image, cv::COLOR_RGB2BGR); - } - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(image, input_cv->Rank(), &output_cv)); - - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); - } -} - -Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "RgbToGray", {3, 5, 11})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (input_cv->Rank() != kDefaultImageRank || input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("RgbToGray: image shape is not or channel is not 3, got rank: " + - std::to_string(input_cv->Rank()) + ", and shape: " + input_cv->shape().ToString()); - } - TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1]}); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGB2GRAY)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToGray: " + std::string(e.what())); - } -} - -Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height) { - struct jpeg_decompress_struct cinfo {}; - struct JpegErrorManagerCustom jerr {}; - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = JpegErrorExitCustom; - try { - jpeg_create_decompress(&cinfo); - if (input->type() == DataType::DE_BYTES) { - uint32_t len = 0; - RETURN_IF_NOT_OK(input->GetStringLength(&len)); - JpegSetSource(&cinfo, input->GetStringsBuffer(), len); - } else { - JpegSetSource(&cinfo, input->GetMutableBuffer(), input->SizeInBytes()); - } - (void)jpeg_read_header(&cinfo, TRUE); - jpeg_calc_output_dimensions(&cinfo); - RETURN_IF_NOT_OK(CheckJpegExit(&cinfo)); - } catch (std::runtime_error &e) { - jpeg_destroy_decompress(&cinfo); - RETURN_STATUS_UNEXPECTED(e.what()); - } - *img_height = static_cast(cinfo.output_height); - *img_width = static_cast(cinfo.output_width); - jpeg_destroy_decompress(&cinfo); - return Status::OK(); -} - -Status GetAffineMatrix(const std::shared_ptr &input, std::vector *matrix, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear) { - CHECK_FAIL_RETURN_UNEXPECTED(translation.size() >= 2, "AffineOp::Compute translation_ size should >= 2"); - float_t translation_x = translation[0]; - float_t translation_y = translation[1]; - float_t degrees_tmp = 0.0; - RETURN_IF_NOT_OK(DegreesToRadians(degrees, °rees_tmp)); - CHECK_FAIL_RETURN_UNEXPECTED(shear.size() >= 2, "AffineOp::Compute shear_ size should >= 2"); - float_t shear_x = shear[0]; - float_t shear_y = shear[1]; - RETURN_IF_NOT_OK(DegreesToRadians(shear_x, &shear_x)); - RETURN_IF_NOT_OK(DegreesToRadians(-1 * shear_y, &shear_y)); - - // Apply Affine Transformation - // T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] - // C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1] - // RSS is rotation with scale and shear matrix - // RSS(a, s, (sx, sy)) = - // = R(a) * S(s) * SHy(sy) * SHx(sx) - // = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] - // [ s*sin(a - sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] - // [ 0 , 0 , 1 ] - // - // where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: - // SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] - // [0, 1 ] [-tan(s), 1] - // - // Thus, the affine matrix is M = T * C * RSS * C^-1 - - // image is hwc, rows = shape()[0] - float_t cx = static_cast(input->shape()[1] - 1) / 2.0F; - float_t cy = static_cast(input->shape()[0] - 1) / 2.0F; - - CHECK_FAIL_RETURN_UNEXPECTED(cos(shear_y) != 0.0, "AffineOp: cos(shear_y) should not be zero."); - - // Calculate RSS - *matrix = std::vector{ - static_cast(scale * cos(degrees_tmp + shear_y) / cos(shear_y)), - static_cast(scale * (-1 * cos(degrees_tmp + shear_y) * tan(shear_x) / cos(shear_y) - sin(degrees_tmp))), - 0, - static_cast(scale * sin(degrees_tmp + shear_y) / cos(shear_y)), - static_cast(scale * (-1 * sin(degrees_tmp + shear_y) * tan(shear_x) / cos(shear_y) + cos(degrees_tmp))), - 0}; - // Compute T * C * RSS * C^-1 - // Compute T * C * RSS * C^-1 - (*matrix)[2] = (1 - (*matrix)[0]) * cx - (*matrix)[1] * cy + translation_x; - (*matrix)[5] = (1 - (*matrix)[4]) * cy - (*matrix)[3] * cx + translation_y; - return Status::OK(); -} - -Status Affine(const std::shared_ptr &input, std::shared_ptr *output, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value) { - try { - RETURN_IF_NOT_OK(ValidateImageRank("Affine", input->Rank())); - dsize_t channel = 1; - RETURN_IF_NOT_OK(ImageNumChannels(input, &channel)); - CHECK_FAIL_RETURN_UNEXPECTED(channel <= kMaxImageChannel || interpolation != InterpolationMode::kCubic, - "Affine: interpolation can not be CUBIC when image channel is greater than 4."); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Affine: load image failed."); - } - - std::vector matrix; - RETURN_IF_NOT_OK(GetAffineMatrix(input_cv, &matrix, degrees, translation, scale, shear)); - cv::Mat affine_mat(matrix); - affine_mat = affine_mat.reshape(1, {2, 3}); - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - RETURN_UNEXPECTED_IF_NULL(output_cv); - cv::warpAffine(input_cv->mat(), output_cv->mat(), affine_mat, input_cv->mat().size(), - GetCVInterpolationMode(interpolation), cv::BORDER_CONSTANT, - cv::Scalar(fill_value[kRIndex], fill_value[kGIndex], fill_value[kBIndex])); - (*output) = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); - } -} - -Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_x, - int32_t kernel_y, float sigma_x, float sigma_y) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (input_cv->mat().data == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] GaussianBlur: load image failed."); - } - cv::Mat output_cv_mat; - cv::GaussianBlur(input_cv->mat(), output_cv_mat, cv::Size(kernel_x, kernel_y), static_cast(sigma_x), - static_cast(sigma_y)); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_cv_mat, input_cv->Rank(), &output_cv)); - (*output) = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("GaussianBlur: " + std::string(e.what())); - } -} - -Status ComputePatchSize(const std::shared_ptr &input_cv, - std::shared_ptr> *patch_size, int32_t num_height, int32_t num_width, - SliceMode slice_mode) { - if (input_cv->mat().data == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] SlicePatches: Tensor could not convert to CV Tensor."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Affine", input_cv->Rank())); - - cv::Mat in_img = input_cv->mat(); - cv::Size s = in_img.size(); - if (num_height == 0 || num_height > s.height) { - RETURN_STATUS_UNEXPECTED( - "SlicePatches: The number of patches on height axis equals 0 or is greater than height, got number of patches:" + - std::to_string(num_height)); - } - if (num_width == 0 || num_width > s.width) { - RETURN_STATUS_UNEXPECTED( - "SlicePatches: The number of patches on width axis equals 0 or is greater than width, got number of patches:" + - std::to_string(num_width)); - } - int32_t patch_h = s.height / num_height; - if (s.height % num_height != 0) { - if (slice_mode == SliceMode::kPad) { - patch_h += 1; // patch_h * num_height - s.height - } - } - int32_t patch_w = s.width / num_width; - if (s.width % num_width != 0) { - if (slice_mode == SliceMode::kPad) { - patch_w += 1; // patch_w * num_width - s.width - } - } - (*patch_size)->first = patch_h; - (*patch_size)->second = patch_w; - return Status::OK(); -} - -Status SlicePatches(const std::shared_ptr &input, std::vector> *output, - int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) { - if (num_height == DEFAULT_NUM_HEIGHT && num_width == DEFAULT_NUM_WIDTH) { - (*output).push_back(input); - return Status::OK(); - } - - auto patch_size = std::make_shared>(0, 0); - int32_t patch_h = 0; - int32_t patch_w = 0; - - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - RETURN_IF_NOT_OK(ComputePatchSize(input_cv, &patch_size, num_height, num_width, slice_mode)); - std::tie(patch_h, patch_w) = *patch_size; - - cv::Mat in_img = input_cv->mat(); - cv::Size s = in_img.size(); - try { - cv::Mat out_img; - if (slice_mode == SliceMode::kPad) { // padding on right and bottom directions - auto padding_h = patch_h * num_height - s.height; - auto padding_w = patch_w * num_width - s.width; - out_img = cv::Mat(s.height + padding_h, s.width + padding_w, in_img.type(), cv::Scalar::all(fill_value)); - in_img.copyTo(out_img(cv::Rect(0, 0, s.width, s.height))); - } else { - out_img = in_img; - } - for (int i = 0; i < num_height; ++i) { - for (int j = 0; j < num_width; ++j) { - std::shared_ptr patch_cv; - cv::Rect rect(j * patch_w, i * patch_h, patch_w, patch_h); - cv::Mat patch(out_img(rect)); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(patch, input_cv->Rank(), &patch_cv)); - (*output).push_back(std::static_pointer_cast(patch_cv)); - } - } - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("SlicePatches: " + std::string(e.what())); - } -} - -Status Solarize(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &threshold) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "Solarize", {1, 2, 3, 4, 5, 6, 11, 12}, {2, 3}, {1, 3})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat input_img = input_cv->mat(); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("Solarize: load image failed."); - } - - std::shared_ptr mask_mat_tensor; - std::shared_ptr output_cv_tensor; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &mask_mat_tensor)); - - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv_tensor)); - RETURN_UNEXPECTED_IF_NULL(mask_mat_tensor); - RETURN_UNEXPECTED_IF_NULL(output_cv_tensor); - - auto threshold_min = threshold[0], threshold_max = threshold[1]; - - if (threshold_min == threshold_max) { - mask_mat_tensor->mat().setTo(0, ~(input_cv->mat() >= threshold_min)); - } else { - mask_mat_tensor->mat().setTo(0, ~((input_cv->mat() >= threshold_min) & (input_cv->mat() <= threshold_max))); - } - - // solarize desired portion - const float max_size = 255.f; - output_cv_tensor->mat() = cv::Scalar::all(max_size) - mask_mat_tensor->mat(); - input_cv->mat().copyTo(output_cv_tensor->mat(), input_cv->mat() < threshold_min); - if (threshold_min < threshold_max) { - input_cv->mat().copyTo(output_cv_tensor->mat(), input_cv->mat() > threshold_max); - } - - *output = std::static_pointer_cast(output_cv_tensor); - } - - catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Solarize: " + std::string(e.what())); - } - return Status::OK(); -} - -Status ToTensor(const std::shared_ptr &input, std::shared_ptr *output, const DataType &data_type) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] ToTensor: load image failed."); - } - if (input_cv->Rank() == kMinImageRank) { - // If input tensor is 2D, we assume we have HW dimensions - RETURN_IF_NOT_OK(input_cv->ExpandDim(kMinImageRank)); - } - CHECK_FAIL_RETURN_UNEXPECTED( - input_cv->shape().Size() > kChannelIndexHWC, - "ToTensor: rank of input data should be greater than: " + std::to_string(kChannelIndexHWC) + - ", but got:" + std::to_string(input_cv->shape().Size())); - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - if (input_cv->shape().Size() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("ToTensor: image shape should be , but got rank: " + - std::to_string(input_cv->shape().Size())); - } - - int height = static_cast(input_cv->shape()[0]); - int width = static_cast(input_cv->shape()[1]); - - // OpenCv has a bug in extractChannel when the type is float16. - // To avoid the segfault, we cast to float32 first. - if (input_cv->type() == DataType(DataType::DE_FLOAT16)) { - RETURN_IF_NOT_OK(TypeCast(input_cv, output, DataType(DataType::DE_FLOAT32))); - input_cv = CVTensor::AsCVTensor(*output); - } - - std::shared_ptr output_cv; - // Reshape from HCW to CHW - RETURN_IF_NOT_OK( - CVTensor::CreateEmpty(TensorShape{num_channels, height, width}, DataType(DataType::DE_FLOAT32), &output_cv)); - // Rescale tensor by dividing by 255 - const auto kMaxBitValueinFloat = static_cast(kMaxBitValue); - for (int i = 0; i < num_channels; ++i) { - cv::Mat mat_t; - cv::extractChannel(input_cv->mat(), mat_t, i); - cv::Mat mat; - RETURN_IF_NOT_OK(output_cv->MatAtIndex({i}, &mat)); - mat_t.convertTo(mat, CV_32F, 1 / kMaxBitValueinFloat, 0); - } - - // Process tensor output according to desired output data type - if (data_type != DataType(DataType::DE_FLOAT32)) { - RETURN_IF_NOT_OK(TypeCast(output_cv, output, data_type)); - } else { - *output = std::move(output_cv); - } - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("ToTensor: " + std::string(e.what())); - } -} - -// round half to even -float Round(float value) { - const int32_t kEven = 2; - float rnd = round(value); - float rnd_l = floor(value); - float rnd_h = ceil(value); - if (std::fabs((value - rnd_l) - kHalf) <= std::numeric_limits::epsilon()) { - if (common::IsDoubleEqual(fmod(rnd, kEven), 0.0)) { - return rnd; - } else if (value > 0) { - return rnd_l; - } else { - return rnd_h; - } - } - return rnd; -} - -std::vector Linspace(float start, float end, int n, float scale, float offset, bool round) { - std::vector linear(n); - float step = (n == 1) ? 0 : (end - start) / static_cast(n - 1); - for (size_t i = 0; i < linear.size(); ++i) { - linear[i] = (start + static_cast(i) * step) * scale + offset; - if (round) { - linear[i] = Round(linear[i]); - } - } - return linear; -} - -Status ApplyAugment(const std::shared_ptr &input, std::shared_ptr *output, const std::string &op_name, - float magnitude, InterpolationMode interpolation, const std::vector &fill_value) { - if (op_name == "ShearX") { - float_t shear = magnitude * 180.F / CV_PI; - AffineOp affine(0.0, {0, 0}, 1.0, {shear, 0.0}, interpolation, fill_value); - RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "ShearY") { - float_t shear = magnitude * 180.F / CV_PI; - AffineOp affine(0.0, {0, 0}, 1.0, {0.0, shear}, interpolation, fill_value); - RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "TranslateX") { - float_t translate = magnitude; - AffineOp affine(0.0, {translate, 0}, 1.0, {0.0, 0.0}, interpolation, fill_value); - RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "TranslateY") { - float_t translate = magnitude; - AffineOp affine(0.0, {0, translate}, 1.0, {0.0, 0.0}, interpolation, fill_value); - RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "Rotate") { - RETURN_IF_NOT_OK(Rotate(input, output, {}, magnitude, interpolation, false, fill_value[kRIndex], - fill_value[kBIndex], fill_value[kGIndex])); - } else if (op_name == "Brightness") { - RETURN_IF_NOT_OK(AdjustBrightness(input, output, 1 + magnitude)); - } else if (op_name == "Color") { - RETURN_IF_NOT_OK(AdjustSaturation(input, output, 1 + magnitude)); - } else if (op_name == "Contrast") { - RETURN_IF_NOT_OK(AdjustContrast(input, output, 1 + magnitude)); - } else if (op_name == "Sharpness") { - SharpnessOp sharpness(1 + magnitude); - RETURN_IF_NOT_OK(sharpness.Compute(input, output)); - } else if (op_name == "Posterize") { - PosterizeOp posterize(static_cast(magnitude)); - RETURN_IF_NOT_OK(posterize.Compute(input, output)); - } else if (op_name == "Solarize") { - RETURN_IF_NOT_OK(Solarize(input, output, {magnitude, magnitude})); - } else if (op_name == "AutoContrast") { - RETURN_IF_NOT_OK(AutoContrast(input, output, 0.0, {})); - } else if (op_name == "Equalize") { - RETURN_IF_NOT_OK(Equalize(input, output)); - } else if (op_name == "Identity") { - *output = std::static_pointer_cast(input); - } else if (op_name == "Invert") { - InvertOp invert; - RETURN_IF_NOT_OK(invert.Compute(input, output)); - } else { - RETURN_STATUS_UNEXPECTED("ApplyAugment: the provided operator " + op_name + " is not supported."); - } - return Status::OK(); -} - -Status EncodeJpeg(const std::shared_ptr &image, std::shared_ptr *output, int quality) { - RETURN_UNEXPECTED_IF_NULL(output); - - std::string err_msg; - if (image->type() != DataType::DE_UINT8) { - err_msg = "EncodeJpeg: The type of the image data should be UINT8, but got " + image->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - TensorShape shape = image->shape(); - int rank = static_cast(shape.Rank()); - if (rank < kMinImageRank || rank > kDefaultImageRank) { - err_msg = "EncodeJpeg: The image has invalid dimensions. It should have two or three dimensions, but got "; - err_msg += std::to_string(rank) + " dimensions."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int channels; - if (rank == kDefaultImageRank) { - channels = static_cast(shape[kMinImageRank]); - if (channels != kMinImageChannel && channels != kDefaultImageChannel) { - err_msg = "EncodeJpeg: The image has invalid channels. It should have 1 or 3 channels, but got "; - err_msg += std::to_string(channels) + " channels."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - channels = 1; - } - - if (quality < kMinJpegQuality || quality > kMaxJpegQuality) { - err_msg = "EncodeJpeg: Invalid quality " + std::to_string(quality) + ", should be in range of [" + - std::to_string(kMinJpegQuality) + ", " + std::to_string(kMaxJpegQuality) + "]."; - - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::vector params = {cv::IMWRITE_JPEG_QUALITY, quality, cv::IMWRITE_JPEG_PROGRESSIVE, 0, - cv::IMWRITE_JPEG_OPTIMIZE, 0, cv::IMWRITE_JPEG_RST_INTERVAL, 0}; - - std::vector buffer; - cv::Mat image_matrix; - - std::shared_ptr input_cv = CVTensor::AsCVTensor(image); - image_matrix = input_cv->mat(); - if (!image_matrix.data) { - RETURN_STATUS_UNEXPECTED("EncodeJpeg: Load the image tensor failed."); - } - - if (channels == kMinImageChannel) { - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".JPEG", image_matrix, buffer, params), - "EncodeJpeg: Failed to encode image."); - } else { - cv::Mat image_bgr; - cv::cvtColor(image_matrix, image_bgr, cv::COLOR_RGB2BGR); - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".JPEG", image_bgr, buffer, params), - "EncodeJpeg: Failed to encode image."); - } - - TensorShape tensor_shape = TensorShape({(long int)buffer.size()}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(tensor_shape, DataType(DataType::DE_UINT8), buffer.data(), output)); - - return Status::OK(); -} - -Status EncodePng(const std::shared_ptr &image, std::shared_ptr *output, int compression_level) { - RETURN_UNEXPECTED_IF_NULL(output); - - std::string err_msg; - if (image->type() != DataType::DE_UINT8) { - err_msg = "EncodePng: The type of the image data should be UINT8, but got " + image->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - TensorShape shape = image->shape(); - int rank = static_cast(shape.Rank()); - if (rank < kMinImageRank || rank > kDefaultImageRank) { - err_msg = "EncodePng: The image has invalid dimensions. It should have two or three dimensions, but got "; - err_msg += std::to_string(rank) + " dimensions."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int channels; - if (rank == kDefaultImageRank) { - channels = static_cast(shape[kMinImageRank]); - if (channels != kMinImageChannel && channels != kDefaultImageChannel) { - err_msg = "EncodePng: The image has invalid channels. It should have 1 or 3 channels, but got "; - err_msg += std::to_string(channels) + " channels."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - channels = 1; - } - - if (compression_level < kMinPngCompression || compression_level > kMaxPngCompression) { - err_msg = "EncodePng: Invalid compression_level " + std::to_string(compression_level) + - ", should be in range of [" + std::to_string(kMinPngCompression) + ", " + - std::to_string(kMaxPngCompression) + "]."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::vector params = {cv::IMWRITE_PNG_COMPRESSION, compression_level, cv::IMWRITE_PNG_STRATEGY, - cv::IMWRITE_PNG_STRATEGY_RLE}; - std::vector buffer; - cv::Mat image_matrix; - - std::shared_ptr input_cv = CVTensor::AsCVTensor(image); - image_matrix = input_cv->mat(); - if (!image_matrix.data) { - RETURN_STATUS_UNEXPECTED("EncodePng: Load the image tensor failed."); - } - - if (channels == kMinImageChannel) { - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".PNG", image_matrix, buffer, params), - "EncodePng: Failed to encode image."); - } else { - cv::Mat image_bgr; - cv::cvtColor(image_matrix, image_bgr, cv::COLOR_RGB2BGR); - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".PNG", image_bgr, buffer, params), "EncodePng: Failed to encode image."); - } - - TensorShape tensor_shape = TensorShape({(long int)buffer.size()}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(tensor_shape, DataType(DataType::DE_UINT8), buffer.data(), output)); - - return Status::OK(); -} - -Status ReadFile(const std::string &filename, std::shared_ptr *output) { - RETURN_UNEXPECTED_IF_NULL(output); - - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("ReadFile: Invalid file path, " + filename + " does not exist."); - } - if (!Path(realpath.value()).IsFile()) { - RETURN_STATUS_UNEXPECTED("ReadFile: Invalid file path, " + filename + " is not a regular file."); - } - - RETURN_IF_NOT_OK(Tensor::CreateFromFile(realpath.value(), output)); - return Status::OK(); -} - -Status ReadImage(const std::string &filename, std::shared_ptr *output, ImageReadMode mode) { - RETURN_UNEXPECTED_IF_NULL(output); - - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - std::string err_msg = "ReadImage: Invalid file path, " + filename + " does not exist."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (!Path(realpath.value()).IsFile()) { - RETURN_STATUS_UNEXPECTED("ReadImage: Invalid file path, " + filename + " is not a regular file."); - } - - cv::Mat image; - int cv_mode = static_cast(mode) - 1; - image = cv::imread(realpath.value(), cv_mode); - if (image.data == nullptr) { - RETURN_STATUS_UNEXPECTED("ReadImage: Failed to read file " + filename); - } - - std::shared_ptr output_cv; - if (mode == ImageReadMode::kCOLOR || image.channels() > 1) { - cv::Mat image_rgb; - cv::cvtColor(image, image_rgb, cv::COLOR_BGRA2RGB); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(image_rgb, kDefaultImageRank, &output_cv)); - } else { - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(image, kDefaultImageRank, &output_cv)); - } - *output = std::static_pointer_cast(output_cv); - - return Status::OK(); -} - -Status WriteFile(const std::string &filename, const std::shared_ptr &data) { - std::string err_msg; - - if (data->type() != DataType::DE_UINT8) { - err_msg = "WriteFile: The type of the elements of data should be UINT8, but got " + data->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - long int data_size = data->Size(); - const char *data_buffer; - if (data_size >= kDeMaxDim || data_size < 0) { - err_msg = "WriteFile: Invalid data->Size() , should be >= 0 && < " + std::to_string(kDeMaxDim); - err_msg += " , but got " + std::to_string(data_size) + " for " + filename; - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (data_size > 0) { - if (data->type() == DataType::DE_BYTES) { - data_buffer = (const char *)data->GetStringsBuffer(); - } else { - data_buffer = (const char *)data->GetMutableBuffer(); - } - if (data_buffer == nullptr) { - err_msg = "WriteFile: Invalid data->GetBufferSize() , should not be nullptr."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - TensorShape shape = data->shape(); - int rank = static_cast(shape.Rank()); - if (rank != kMinImageChannel) { - err_msg = "WriteFile: The data has invalid dimensions. It should have only one dimension, but got "; - err_msg += std::to_string(rank) + " dimensions."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - Path file(filename); - if (!file.Exists()) { - int file_descriptor; - RETURN_IF_NOT_OK(file.CreateFile(&file_descriptor)); - RETURN_IF_NOT_OK(file.CloseFile(file_descriptor)); - } - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("WriteFile: Invalid file path, " + filename + " failed to get the real path."); - } - if (!Path(realpath.value()).IsFile()) { - RETURN_STATUS_UNEXPECTED("WriteFile: Invalid file path, " + filename + " is not a regular file."); - } - - std::ofstream fs(realpath.value().c_str(), std::ios::out | std::ios::trunc | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "WriteFile: Failed to open the file: " + filename + " for writing."); - - if (data_size > 0) { - fs.write(data_buffer, data_size); - if (fs.fail()) { - err_msg = "WriteFile: Failed to write the file " + filename; - fs.close(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - fs.close(); - ChangeFileMode(realpath.value(), S_IRUSR | S_IWUSR); - return Status::OK(); -} - -Status WriteJpeg(const std::string &filename, const std::shared_ptr &image, int quality) { - std::string err_msg; - - if (image->type() != DataType::DE_UINT8) { - err_msg = "WriteJpeg: The type of the elements of image should be UINT8, but got " + image->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - TensorShape shape = image->shape(); - int rank = static_cast(shape.Rank()); - if (rank < kMinImageRank || rank > kDefaultImageRank) { - err_msg = "WriteJpeg: The image has invalid dimensions. It should have two or three dimensions, but got "; - err_msg += std::to_string(rank) + " dimensions."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int channels; - if (rank == kDefaultImageRank) { - channels = static_cast(shape[kMinImageRank]); - if (channels != kMinImageChannel && channels != kDefaultImageChannel) { - err_msg = "WriteJpeg: The image has invalid channels. It should have 1 or 3 channels, but got "; - err_msg += std::to_string(channels) + " channels."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - channels = 1; - } - - if (quality < kMinJpegQuality || quality > kMaxJpegQuality) { - err_msg = "WriteJpeg: Invalid quality " + std::to_string(quality) + ", should be in range of [" + - std::to_string(kMinJpegQuality) + ", " + std::to_string(kMaxJpegQuality) + "]."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::vector params = {cv::IMWRITE_JPEG_QUALITY, quality, cv::IMWRITE_JPEG_PROGRESSIVE, 0, - cv::IMWRITE_JPEG_OPTIMIZE, 0, cv::IMWRITE_JPEG_RST_INTERVAL, 0}; - - std::vector buffer; - cv::Mat image_matrix; - - std::shared_ptr input_cv = CVTensor::AsCVTensor(image); - image_matrix = input_cv->mat(); - if (!image_matrix.data) { - RETURN_STATUS_UNEXPECTED("WriteJpeg: Load the image tensor failed."); - } - - if (channels == kMinImageChannel) { - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".JPEG", image_matrix, buffer, params), - "WriteJpeg: Failed to encode image."); - } else { - cv::Mat image_bgr; - cv::cvtColor(image_matrix, image_bgr, cv::COLOR_RGB2BGR); - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".JPEG", image_bgr, buffer, params), - "WriteJpeg: Failed to encode image."); - } - - Path file(filename); - if (!file.Exists()) { - int file_descriptor; - RETURN_IF_NOT_OK(file.CreateFile(&file_descriptor)); - RETURN_IF_NOT_OK(file.CloseFile(file_descriptor)); - } - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("WriteJpeg: Invalid file path, " + filename + " failed to get the real path."); - } - if (!Path(realpath.value()).IsFile()) { - RETURN_STATUS_UNEXPECTED("WriteJpeg: Invalid file path, " + filename + " is not a regular file."); - } - - std::ofstream fs(realpath.value().c_str(), std::ios::out | std::ios::trunc | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "WriteJpeg: Failed to open the file " + filename + " for writing."); - - fs.write((const char *)buffer.data(), (long int)buffer.size()); - if (fs.fail()) { - fs.close(); - RETURN_STATUS_UNEXPECTED("WriteJpeg: Failed to write the file " + filename); - } - fs.close(); - ChangeFileMode(realpath.value(), S_IRUSR | S_IWUSR); - return Status::OK(); -} - -Status WritePng(const std::string &filename, const std::shared_ptr &image, int compression_level) { - std::string err_msg; - - if (image->type() != DataType::DE_UINT8) { - err_msg = "WritePng: The type of the elements of image should be UINT8, but got " + image->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - TensorShape shape = image->shape(); - int rank = static_cast(shape.Rank()); - if (rank < kMinImageRank || rank > kDefaultImageRank) { - err_msg = "WritePng: The image has invalid dimensions. It should have two or three dimensions, but got "; - err_msg += std::to_string(rank) + " dimensions."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int channels; - if (rank == kDefaultImageRank) { - channels = static_cast(shape[kMinImageRank]); - if (channels != kMinImageChannel && channels != kDefaultImageChannel) { - err_msg = "WritePng: The image has invalid channels. It should have 1 or 3 channels, but got "; - err_msg += std::to_string(channels) + " channels."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - channels = 1; - } - - if (compression_level < kMinPngCompression || compression_level > kMaxPngCompression) { - err_msg = "WritePng: Invalid compression_level " + std::to_string(compression_level) + ", should be in range of [" + - std::to_string(kMinPngCompression) + ", " + std::to_string(kMaxPngCompression) + "]."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - std::vector params = {cv::IMWRITE_PNG_COMPRESSION, compression_level, cv::IMWRITE_PNG_STRATEGY, - cv::IMWRITE_PNG_STRATEGY_RLE}; - std::vector buffer; - cv::Mat image_matrix; - - std::shared_ptr input_cv = CVTensor::AsCVTensor(image); - image_matrix = input_cv->mat(); - if (!image_matrix.data) { - RETURN_STATUS_UNEXPECTED("WritePng: Load the image tensor failed."); - } - - if (channels == kMinImageChannel) { - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".PNG", image_matrix, buffer, params), - "WritePng: Failed to encode image."); - } else { - cv::Mat image_bgr; - cv::cvtColor(image_matrix, image_bgr, cv::COLOR_RGB2BGR); - CHECK_FAIL_RETURN_UNEXPECTED(cv::imencode(".PNG", image_bgr, buffer, params), "WritePng: Failed to encode image."); - } - - Path file(filename); - if (!file.Exists()) { - int file_descriptor; - RETURN_IF_NOT_OK(file.CreateFile(&file_descriptor)); - RETURN_IF_NOT_OK(file.CloseFile(file_descriptor)); - } - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("WritePng: Invalid file path, " + filename + " failed to get the real path."); - } - struct stat sb {}; - stat(realpath.value().c_str(), &sb); - if (S_ISREG(sb.st_mode) == 0) { - RETURN_STATUS_UNEXPECTED("WritePng: Invalid file path, " + filename + " is not a regular file."); - } - - std::ofstream fs(realpath.value().c_str(), std::ios::out | std::ios::trunc | std::ios::binary); - CHECK_FAIL_RETURN_UNEXPECTED(!fs.fail(), "WritePng: Failed to open the file " + filename + " for writing."); - - fs.write((const char *)buffer.data(), (long int)buffer.size()); - if (fs.fail()) { - fs.close(); - RETURN_STATUS_UNEXPECTED("WritePng: Failed to write the file " + filename); - } - fs.close(); - ChangeFileMode(realpath.value(), S_IRUSR | S_IWUSR); - return Status::OK(); -} - -// support list -const unsigned char kBmpMagic[] = "\x42\x4D"; -constexpr dsize_t kBmpMagicLen = 2; -const unsigned char kTiffMagic1[] = "\x4D\x4D"; -const unsigned char kTiffMagic2[] = "\x49\x49"; -constexpr dsize_t kTiffMagicLen = 2; - -Status DumpImageAndAppendStatus(const std::shared_ptr &image, const Status &status) { - Status local_status = status; - std::string file_name = "./abnormal_image."; - std::string file_suffix; - std::string error_info = local_status.GetErrDescription(); - - uint32_t image_length = 0; - uchar *image_ptr = nullptr; - if (image->type() == DataType::DE_BYTES) { - RETURN_IF_NOT_OK(image->GetStringLength(&image_length)); - image_ptr = image->GetStringsBuffer(); - } else { - image_length = image->SizeInBytes(); - image_ptr = image->GetMutableBuffer(); - } - - if (image_length == 0) { - return local_status; - } - - if (memcmp(image_ptr, kJpegMagic, kJpegMagicLen) == 0) { // support - file_suffix = "jpg"; - } else if (memcmp(image_ptr, kPngMagic, kPngMagicLen) == 0) { // support - file_suffix = "png"; - } else if (memcmp(image_ptr, kBmpMagic, kBmpMagicLen) == 0) { // support - file_suffix = "bmp"; - } else if (memcmp(image_ptr, kTiffMagic1, kTiffMagicLen) == 0 || // support - memcmp(image_ptr, kTiffMagic2, kTiffMagicLen) == 0) { - file_suffix = "tif"; - } else { - file_suffix = "exception"; - error_info += " Unknown image type."; - } - - auto ret = WriteFile(file_name + file_suffix, image); - if (ret == Status::OK()) { - error_info += " Dump the abnormal image to [" + (file_name + file_suffix) + - "]. You can check this image first through the image viewer. If you find that " + - "the image is abnormal, delete it from the dataset and re-run."; - } - local_status.SetErrDescription(error_info); - return local_status; -} - -// unsupported list -const unsigned char kGifMagic[] = "\x47\x49\x46"; -constexpr dsize_t kGifMagicLen = 3; -const unsigned char kWebpMagic[] = "\x00\x57\x45\x42"; -constexpr dsize_t kWebpMagicLen = 4; - -Status CheckUnsupportedImage(const std::shared_ptr &image) { - bool unsupport_flag = false; - - std::string file_name = "./unsupported_image."; - std::string file_suffix; - if (image->SizeInBytes() == 0) { - RETURN_STATUS_UNEXPECTED("Image file size is 0."); - } - - uchar *image_ptr = nullptr; - if (image->type() == DataType::DE_BYTES) { - image_ptr = image->GetStringsBuffer(); - } else { - image_ptr = image->GetMutableBuffer(); - } - - if (memcmp(image_ptr, kGifMagic, kGifMagicLen) == 0) { // unsupported - file_suffix = "gif"; - unsupport_flag = true; - } else if (memcmp(image_ptr + 7, kWebpMagic, kWebpMagicLen) == 0) { // unsupported: skip the 7 bytes - file_suffix = "webp"; - unsupport_flag = true; - } - - if (unsupport_flag) { - auto ret = WriteFile(file_name + file_suffix, image); - if (ret == Status::OK()) { - RETURN_STATUS_UNEXPECTED("Unsupported image type [" + file_suffix + "] and dump the image to [" + - (file_name + file_suffix) + "]. Please delete it from the dataset and re-run."); - } else { - ret.SetErrDescription("Unsupported image type [" + file_suffix + "], but dump the image failed. " + - "Error info: " + ret.GetErrDescription()); - return ret; - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/image_utils.h b/mindspore-lite/minddata/dataset/kernels/image/image_utils.h deleted file mode 100755 index 664c35945..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/image_utils.h +++ /dev/null @@ -1,601 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_IMAGE_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_IMAGE_UTILS_H_ - -#if defined(_WIN32) || defined(_WIN64) -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#elif __APPLE__ -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "./jpeglib.h" -#include "./jerror.h" -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -constexpr dsize_t kChannelIndexHWC = 2; // images are hwc, so index 2 represents number of channels -constexpr dsize_t kChannelIndexCHW = 0; // images are chw, so index 0 represents number of channels -constexpr dsize_t kMinImageRank = 2; // images have at least 2 dimensions -constexpr dsize_t kDefaultImageRank = 3; // images are hwc channels in general -constexpr int32_t kMaxBitValue = 255; // max bit value after decode is 256 -constexpr dsize_t kMinImageChannel = 1; // image ops support minimum of 1 channel -constexpr dsize_t kDefaultImageChannel = 3; // images are 3 channels in general -constexpr dsize_t kMaxImageChannel = 4; // image ops support maximum of 4 channel -constexpr float kHalf = 0.5; // to get the half of a value -constexpr dsize_t kRIndex = 0; // index of red channel in RGB format -constexpr dsize_t kGIndex = 1; // index of green channel in RGB format -constexpr dsize_t kBIndex = 2; // index of blue channel in RGB format -constexpr dsize_t kHeightIndex = 0; // index of height of HWC images -constexpr dsize_t kWidthIndex = 1; // index of width of HWC images -constexpr dsize_t kMinJpegQuality = 1; // the minimum quality for JPEG -constexpr dsize_t kMaxJpegQuality = 100; // the maximum quality for JPEG -constexpr dsize_t kMinPngCompression = 0; // the minimum compression level for PNG -constexpr dsize_t kMaxPngCompression = 9; // the maximum compression level for PNG -constexpr dsize_t kChannelIndexNHWC = 3; -constexpr dsize_t kNHWCImageRank = 4; -constexpr dsize_t kWidthIndexNHWC = 2; -constexpr dsize_t kHeightIndexNHWC = 1; -constexpr dsize_t kWidthIndexNCHW = 3; -constexpr dsize_t kHeightIndexNCHW = 2; - -void JpegErrorExitCustom(j_common_ptr cinfo); - -struct JpegErrorManagerCustom { - // "public" fields - struct jpeg_error_mgr pub; - // for return to caller - jmp_buf setjmp_buffer; -}; - -/// \brief Returns the interpolation mode in openCV format -/// \param[in] mode Interpolation mode in DE format -int GetCVInterpolationMode(InterpolationMode mode); - -/// \brief Returns the openCV equivalent of the border type used for padding. -/// \param type -/// \return Status code -int GetCVBorderType(BorderType type); - -/// \brief Get the number of input image channels. -/// \param[in] image Tensor of the image. -/// \param[out] channels Channels of the image. -/// \return The status code. -Status ImageNumChannels(const std::shared_ptr &image, dsize_t *channels); - -/// \brief Get the size of input image. -/// \param[in] image Tensor of the image. -/// \param[out] size Size of the image as [height, width]. -/// \return The status code. -Status ImageSize(const std::shared_ptr &image, std::vector *size); - -/// \brief Validate image Dtype, rank and channel. -/// \param[in] image Image tensor to be validated. -/// \param[in] op_name operator name. -/// \param[in] valid_dtype Valid date type of the image tensor. Default: {}, means not to check date type. -/// \param[in] valid_rank Valid dimension of the image tensor. Default: {}, means not to check dimension. -/// \param[in] valid_channel Valid channel of the image tensor. Default: {}, means not to check channel. -Status ValidateImage(const std::shared_ptr &image, const std::string &op_name, - const std::set &valid_dtype = {}, const std::set &valid_rank = {}, - const std::set &valid_channel = {}); - -/// \brief Validate image dtype. -/// \param[in] op_name operator name. -/// \param[in] dtype Date type of the image tensor. -Status ValidateImageDtype(const std::string &op_name, DataType dtype); - -/// \brief Validate image rank. -/// \param[in] op_name operator name. -/// \param[in] rank refers to the rank of input image shape. -Status ValidateImageRank(const std::string &op_name, int32_t rank); - -/// \brief Returns the check result of tensor rank and tensor shape -/// \param[in] tensor: The input tensor need to check -/// \param[in] channel: The channel index of tensor shape. -/// \param[out] return true if channel of tensor shape is 1 or 3. -bool CheckTensorShape(const std::shared_ptr &tensor, const int &channel); - -/// \brief Returns flipped image -/// \param[in] input/output: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param flip_code: 1 for Horizontal (around y-axis), 0 for Vertical (around x-axis), -1 for both -/// The flipping happens in place. -Status Flip(std::shared_ptr input, std::shared_ptr *output, int flip_code); - -/// \brief Returns Horizontally flipped image -/// \param input/output: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// The flipping happens in place. -Status HorizontalFlip(std::shared_ptr input, std::shared_ptr *output); - -/// \brief Returns Vertically flipped image -/// \param input/output: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \note The flipping happens in place. -Status VerticalFlip(std::shared_ptr input, std::shared_ptr *output); - -/// \brief Returns Resized image. -/// \param input/output: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param output_height: height of output -/// \param output_width: width of output -/// \param fx: horizontal scale -/// \param fy: vertical scale -/// \param InterpolationMode: the interpolation mode -/// \param output: Resized image of shape or -/// and same type as input -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx = 0.0, double fy = 0.0, - InterpolationMode mode = InterpolationMode::kLinear); - -/// \brief Returns Decoded image -/// Supported images: -/// BMP JPEG JPG PNG TIFF -/// supported by opencv, if user need more image analysis capabilities, please compile opencv particularlly. -/// \param input: CVTensor containing the not decoded image 1D bytes -/// \param output: Decoded image Tensor of shape and type DE_UINT8. Pixel order is RGB -Status Decode(const std::shared_ptr &input, std::shared_ptr *output); - -Status DecodeCv(const std::shared_ptr &input, std::shared_ptr *output); - -DATASET_API bool IsNonEmptyJPEG(const std::shared_ptr &input); - -bool IsNonEmptyPNG(const std::shared_ptr &input); - -void JpegSetSource(j_decompress_ptr c_info, const void *data, int64_t data_size); - -Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int x = 0, int y = 0, - int w = 0, int h = 0); - -/// \brief Returns Rescaled image -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param rescale: rescale parameter -/// \param shift: shift parameter -/// \param output: Rescaled image Tensor of same input shape and type DE_FLOAT32 -Status Rescale(const std::shared_ptr &input, std::shared_ptr *output, float rescale, float shift); - -/// \brief Returns cropped ROI of an image -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param x: starting horizontal position of ROI -/// \param y: starting vertical position of ROI -/// \param w: width of the ROI -/// \param h: height of the ROI -/// \param output: Cropped image Tensor of shape or and same input type. -Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h); - -/// \brief Change the color space of the image. -/// \param input: The input image. -/// \param output: The output image. -/// \param convert_mode: The mode of image channel conversion. -Status ConvertColor(const std::shared_ptr &input, std::shared_ptr *output, ConvertMode convert_mode); - -/// \brief Swaps the channels in the image, i.e. converts HWC to CHW -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param output: Tensor of shape or and same input type. -Status HwcToChw(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Masks the given part of the input image with a another image (sub_mat) -/// \param[in] sub_mat The image we want to mask with -/// \param[in] input The pointer to the image we want to mask -/// \param[in] x The horizontal coordinate of left side of crop box -/// \param[in] y The vertical coordinate of the top side of crop box -/// \param[in] width The width of the mask box -/// \param[in] height The height of the mask box -/// \param[in] image_format The format of the image (CHW or HWC) -/// \param[out] input Masks the input image in-place and returns it -/// @return Status ok/error -Status MaskWithTensor(const std::shared_ptr &sub_mat, std::shared_ptr *input, int x, int y, int width, - int height, ImageFormat image_format); - -/// \brief Copies a value from a source tensor into a destination tensor -/// \note This is meant for images and therefore only works if tensor is uint8 or float32 -/// \param[in] source_tensor The tensor we take the value from -/// \param[in] dest_tensor The pointer to the tensor we want to copy the value to -/// \param[in] source_indx index of the value in the source tensor -/// \param[in] dest_indx index of the value in the destination tensor -/// \param[out] dest_tensor Copies the value to the given dest_tensor and returns it -/// @return Status ok/error -Status CopyTensorValue(const std::shared_ptr &source_tensor, std::shared_ptr *dest_tensor, - const std::vector &source_indx, const std::vector &dest_indx); - -/// \brief Swap the red and blue pixels (RGB <-> BGR) -/// \param input: Tensor of shape and any OpenCv compatible type, see CVTensor. -/// \param output: Swapped image of same shape and type -Status SwapRedAndBlue(std::shared_ptr input, std::shared_ptr *output); - -/// \brief Crops and resizes the image -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param x: horizontal start point -/// \param y: vertical start point -/// \param crop_height: height of the cropped ROI -/// \param crop_width: width of the cropped ROI -/// \param target_width: width of the final resized image -/// \param target_height: height of the final resized image -/// \param InterpolationMode: the interpolation used in resize operation -/// \param output: Tensor of shape or -/// and same type as input -Status CropAndResize(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, - int crop_height, int crop_width, int target_height, int target_width, InterpolationMode mode); - -/// \brief Returns rotated image -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param center: rotation center -/// \param degree: degree to rotate -/// \param expand: if reshape is necessary -/// \param output: rotated image of same input type. -Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, std::vector center, - float degree, InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, bool expand = false, - uint8_t fill_r = 0, uint8_t fill_g = 0, uint8_t fill_b = 0); - -/// \brief Returns Normalized image -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param mean: Tensor of shape <3> and type DE_FLOAT32 which are mean of each channel in RGB order -/// \param std: Tensor of shape <3> and type DE_FLOAT32 which are std of each channel in RGB order -/// \param is_hwc: Check if input is HWC/CHW format -/// \param output: Normalized image Tensor of same input shape and type DE_FLOAT32 -Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, - std::vector std, bool is_hwc); - -/// \brief Returns Normalized and padded image -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param mean: vector of float values which are mean of each channel -/// \param std: vector of float values which are std of each channel -/// \param dtype: output dtype -/// \param is_hwc: Check if input is HWC/CHW format -/// \param output: Normalized image Tensor and pad an extra channel, return a dtype Tensor -Status NormalizePad(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, - std::vector std, const std::string &dtype, bool is_hwc); - -/// \brief Returns image with adjusted brightness. -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param alpha: Alpha value to adjust brightness by. Should be a positive number. -/// If user input one value in python, the range is [1 - value, 1 + value]. -/// This will output original image multiplied by alpha. 0 gives a black image, 1 gives the -/// original image while 2 increases the brightness by a factor of 2. -/// \param output: Adjusted image of same shape and type. -Status AdjustBrightness(const std::shared_ptr &input, std::shared_ptr *output, float alpha); - -/// \brief Returns image with adjusted contrast. -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param alpha: Alpha value to adjust contrast by. Should be a positive number. -/// If user input one value in python, the range is [1 - value, 1 + value]. -/// 0 gives a solid gray image, 1 gives the original image while 2 increases -/// the contrast by a factor of 2. -/// \param output: Adjusted image of same shape and type. -Status AdjustContrast(const std::shared_ptr &input, std::shared_ptr *output, float alpha); - -/// \brief Returns image with contrast maximized. -/// \param input: Tensor of shape // in RGB/Grayscale and any OpenCv compatible type, see CVTensor. -/// \param cutoff: Cutoff percentage of how many pixels are to be removed (high pixels change to 255 and low change -/// to 0) from the high and low ends of the histogram. -/// \param ignore: Pixel values to be ignored in the algorithm. -Status AutoContrast(const std::shared_ptr &input, std::shared_ptr *output, float cutoff, - const std::vector &ignore); - -/// \brief Returns image with gamma correction. -/// \param[in] input: Tensor of shape // in RGB/Grayscale and any OpenCV compatible type, -/// see CVTensor. -/// \param[in] gamma: Non negative real number, same as gamma in the equation. gamma larger than 1 make the shadows -/// darker, while gamma smaller than 1 make dark regions lighter. -/// \param[in] gain: The constant multiplier. -/// \param[out] output: Adjusted image of same shape and type. -Status AdjustGamma(const std::shared_ptr &input, std::shared_ptr *output, float gamma, float gain); - -/// \brief Returns image with adjusted saturation. -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param alpha: Alpha value to adjust saturation by. Should be a positive number. -/// If user input one value in python, the range is [1 - value, 1 + value]. -/// 0 will give a black and white image, 1 will give the original image while -/// 2 will enhance the saturation by a factor of 2. -/// \param output: Adjusted image of same shape and type. -Status AdjustSaturation(const std::shared_ptr &input, std::shared_ptr *output, float alpha); - -/// \brief Adjust the sharpness of the input image. -/// \param[in] input: Tensor of input image. -/// \param[out] output: Tensor of output image. -/// \param[in] alpha: How much to adjust the sharpness. -/// \return Status code. -Status AdjustSharpness(const std::shared_ptr &input, std::shared_ptr *output, float alpha); - -/// \brief Returns image with adjusted hue. -/// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param hue: Hue value to adjust by, should be within range [-0.5, 0.5]. 0.5 and - 0.5 will reverse the hue channel -/// completely. -/// If user input one value in python, the range is [-value, value]. -/// \param output: Adjusted image of same shape and type. -Status AdjustHue(const std::shared_ptr &input, std::shared_ptr *output, float hue); - -/// \brief Returns image with equalized histogram. -/// \param[in] input: Tensor of shape // in RGB/Grayscale and -/// any OpenCv compatible type, see CVTensor. -/// \param[out] output: Equalized image of same shape and type. -Status Equalize(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Masks out a random section from the image with set dimension -/// \param input: input Tensor -/// \param output: cutOut Tensor -/// \param box_height: height of the cropped box -/// \param box_width: width of the cropped box -/// \param num_patches: number of boxes to cut out from the image -/// \param bounded: boolean flag to toggle between random erasing and cutout -/// \param random_color: whether or not random fill value should be used -/// \param fill_colors: vector of color fill values for erase -/// \param is_hwc: Check if input is HWC/CHW format -Status CutOut(const std::shared_ptr &input, std::shared_ptr *output, int32_t box_height, - int32_t box_width, int32_t num_patches, bool bounded, bool random_color, std::mt19937 *rnd, - std::vector fill_colors = {}, bool is_hwc = true); - -/// \brief Erase the input image with given value -/// \param input: input Tensor -/// \param output: erase Tensor -/// \param top: top of the cropped box -/// \param left: left of the cropped box -/// \param height: height of the cropped box -/// \param width: width of the cropped box -/// \param value: fill value for erase -/// \param inplace: whether to apply erasing inplace -Status Erase(const std::shared_ptr &input, std::shared_ptr *output, int32_t top, int32_t left, - int32_t height, int32_t width, const std::vector &value, bool inplace); - -/// \brief Invert the colors of the input image. -/// \param[in] input: Tensor of input image. -/// \param[out] output: Tensor of output image. -/// \return Status code. -Status Invert(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Pads the input image and puts the padded image in the output -/// \param input: input Tensor -/// \param output: padded Tensor -/// \param pad_top: amount of padding done in top -/// \param pad_bottom: amount of padding done in bottom -/// \param pad_left: amount of padding done in left -/// \param pad_right: amount of padding done in right -/// \param border_types: the interpolation to be done in the border -/// \param fill_r: red fill value for pad -/// \param fill_g: green fill value for pad -/// \param fill_b: blue fill value for pad. -Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, - const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, - uint8_t fill_r = 0, uint8_t fill_g = 0, uint8_t fill_b = 0); - -/// \brief Posterize the input image by reducing the number of bits for ecah color channel. -/// \param[in] input: Tensor of input image. -/// \param[out] output: Tensor of output image. -/// \param[in] bits: The number of bits to keep for each channel. -/// \return Status code. -Status Posterize(const std::shared_ptr &input, std::shared_ptr *output, uint8_t bits); - -/// \brief Add AlexNet-style PCA-based noise to an image. -/// \param[in] input The input image. -/// \param[out] output The output image. -/// \param[in] rnd_r Random weight for red channel. -/// \param[in] rnd_g Random weight for green channel. -/// \param[in] rnd_b Random weight for blue channel. -/// \return Status code. -Status RandomLighting(const std::shared_ptr &input, std::shared_ptr *output, float rnd_r, float rnd_g, - float rnd_b); - -/// \brief Take in a 4 channel image in RBGA to RGB -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbaToRgb(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Take in a 4 channel image in RBGA to BGR -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbaToBgr(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Take in a 3 channel image in RBG to BGR -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Take in a 3 channel image in RBG to GRAY -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Get jpeg image width and height -/// \param input: CVTensor containing the not decoded image 1D bytes -/// \param img_width: the jpeg image width -/// \param img_height: the jpeg image height -Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height); - -/// \brief Get an affine matrix that applies affine transformation -/// \param[in] input Input Tensor -/// \param[in] matrix The transformation matrix -/// \param[in] degrees Range of the rotation degrees -/// \param[in] translation The horizontal and vertical translations -/// \param[in] scale The scaling factor -/// \param[in] shear The shear angle -Status GetAffineMatrix(const std::shared_ptr &input, std::vector *matrix, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear); - -/// \brief Geometrically transform the input image -/// \param[in] input Input Tensor -/// \param[out] output Transformed Tensor -/// \param[in] degrees Range of the rotation degrees -/// \param[in] translation The horizontal and vertical translations -/// \param[in] scale The scaling factor -/// \param[in] shear The shear angle -/// \param[in] interpolation The interpolation mode -/// \param[in] fill_value Fill value for pad -Status Affine(const std::shared_ptr &input, std::shared_ptr *output, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value); - -/// \brief Filter the input image with a Gaussian kernel -/// \param[in] input Input Tensor -/// \param[out] output Transformed Tensor -/// \param[in] kernel_size_x Gaussian kernel size of width -/// \param[in] kernel_size_y Gaussian kernel size of height -/// \param[in] sigma_x Gaussian kernel standard deviation of width -/// \param[in] sigma_y Gaussian kernel standard deviation of height -Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_size_x, - int32_t kernel_size_y, float sigma_x, float sigma_y); - -/// \brief Apply perspective transformation on input image. -/// \param[in] input Input Tensor. -/// \param[out] output Transformed Tensor. -/// \param[in] start_points List containing four lists of two integers corresponding to four -/// corners [top-left, top-right, bottom-right, bottom-left] of the original image. -/// \param[in] end_points List containing four lists of two integers corresponding to four -/// corners [top-left, top-right, bottom-right, bottom-left] of the transformed image. -/// \param[in] interpolation Method of interpolation. -Status Perspective(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation); - -/// \brief Slice tensor to multiple patches. -/// \param[in] input Input Tensor -/// \param[out] output Vector of Output Tensor -/// \param[in] num_height Number of patches in vertical direction. -/// \param[in] num_width Number of patches in horizontal direction. -/// \param[in] slice_mode Mode represents padding or drop. -/// \param[in] fill_value The value of filled pixel in right and bottom border when padding. -Status SlicePatches(const std::shared_ptr &input, std::vector> *output, - int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value); - -/// \brief Solarize the image by inverting all pixel values within the threshold. -/// \param[in] input Input Tensor -/// \param[out] output Output Tensor -/// \param[in] threshold Pixel value range to be inverted. -Status Solarize(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &threshold); - -/// \brief Compute patch height and width. -/// \param[in] input Input CVTensor -/// \param[out] patch_size Size of patch -/// \param[in] num_height Number of patches in vertical direction. -/// \param[in] num_width Number of patches in horizontal direction. -/// \param[in] slice_mode Mode represents padding or drop. -Status ComputePatchSize(const std::shared_ptr &input_cv, - std::shared_ptr> *patch_size, int32_t num_height, int32_t num_width, - SliceMode slice_mode); - -/// \brief Rescale and convert HWC to CHW format. -/// \param[in] input The input image -/// \param[in] data_type The output data type -/// \param[out] output The output image -/// \return Status code -Status ToTensor(const std::shared_ptr &input, std::shared_ptr *output, const DataType &data_type); - -/// \brief Generate a vector that contains n numbers between start and end with evenly interval. -/// \param[in] start Start number. -/// \param[in] end End number. -/// \param[in] n Count of numbers. -/// \param[in] scale Zoom scale. -/// \param[in] offset Bias. -/// \param[in] round Round input to the nearest integer. -std::vector Linspace(float start, float end, int32_t n, float scale = 1.0, float offset = 0, bool round = false); - -/// \brief Round input to the nearest integer. Note that this function implements the "round half to even" to break -/// ties when a number is equidistant from two integers. -/// \param[in] value Input value. -float Round(float value); - -/// \brief Perform the selected augment. -/// \param[in] input The input tensor. -/// \param[in] output The output tensor. -/// \param[in] op_name The selected op. -/// \param[in] magnitude The magnitude value. -/// \param[in] interpolation Possible options for interpolation method. -/// \param[in] fill_value Values used to fill. -Status ApplyAugment(const std::shared_ptr &input, std::shared_ptr *output, const std::string &op_name, - float magnitude, InterpolationMode interpolation, const std::vector &fill_value); - -/// \brief Encode the image as JPEG data. -/// \param[in] image The image to be encoded. -/// \param[out] output The Tensor data. -/// \param[in] quality The quality for the output tensor, in range of [1, 100]. Default: 75. -/// \return The status code. -Status EncodeJpeg(const std::shared_ptr &image, std::shared_ptr *output, int quality = 75); - -/// \brief Encode the image as PNG data. -/// \param[in] image The image to be encoded. -/// \param[out] output The Tensor data. -/// \param[in] compression_level The compression_level for encoding, in range of [0, 9]. Default: 6. -/// \return The status code. -Status EncodePng(const std::shared_ptr &image, std::shared_ptr *output, int compression_level = 6); - -/// \brief Reads a file in binary mode. -/// \param[in] filename The path to the file to be read. -/// \param[out] output The binary data. -/// \return The status code. -Status ReadFile(const std::string &filename, std::shared_ptr *output); - -/// \brief Reads a image file and decode it into one or three channels data. -/// \param[in] filename The path to the image file to be read. -/// \param[out] output Output Tensor. -/// \param[in] mode The read mode used for optionally converting the image, can be one of -/// [ImageReadMode::kUNCHANGED, ImageReadMode::kGRAYSCALE, ImageReadMode::kCOLOR]. Default: -/// ImageReadMode::kUNCHANGED. -/// - ImageReadMode::kUNCHANGED, remain the output in the original format. -/// - ImageReadMode::kGRAYSCALE, convert the output into one channel grayscale data. -/// - ImageReadMode::kCOLOR, convert the output into three channels RGB color data. -/// \return The status code. -Status ReadImage(const std::string &filename, std::shared_ptr *output, - ImageReadMode mode = ImageReadMode::kUNCHANGED); - -/// \brief Write the one dimension uint8 data into a file using binary mode. -/// \param[in] filename The path to the file to be written. -/// \param[in] data The tensor data. -/// \return The status code. -Status WriteFile(const std::string &filename, const std::shared_ptr &data); - -/// \brief Write the image data into a JPEG file. -/// \param[in] filename The path to the file to be written. -/// \param[in] image The data tensor. -/// \param[in] quality The quality for JPEG file, in range of [1, 100]. Default: 75. -/// \return Status code. -Status WriteJpeg(const std::string &filename, const std::shared_ptr &image, int quality = 75); - -/// \brief Write the image into a PNG file. -/// \param[in] filename The path to the file to be written. -/// \param[in] image The data tensor. -/// \param[in] compression_level The compression level for PNG file, in range of [0, 9]. Default: 6. -/// \return Status code. -Status WritePng(const std::string &filename, const std::shared_ptr &image, int compression_level = 6); - -/// \brief Dump the abnormal image to disk and facilitate user to check it. -/// \param[in] image The data Tensor. -/// \param[in] status The previous error status which is needed to append more info. -/// \return Status code. -Status DumpImageAndAppendStatus(const std::shared_ptr &image, const Status &status); - -/// \brief Check the unsupported image and dump it to disk. -/// \param[in] image The data Tensor. -/// \return Status code. -Status CheckUnsupportedImage(const std::shared_ptr &image); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_IMAGE_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/invert_op.cc b/mindspore-lite/minddata/dataset/kernels/image/invert_op.cc deleted file mode 100644 index e59c819c7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/invert_op.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/invert_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// only supports RGB images -Status InvertOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - CHECK_FAIL_RETURN_UNEXPECTED( - input->Rank() == kDefaultImageRank, - "Invert: input tensor is not in shape of , but got rank: " + std::to_string(input->Rank())); - CHECK_FAIL_RETURN_UNEXPECTED(input->shape()[kChannelIndexHWC] == kDefaultImageChannel, - "Invert: the number of channels of input tensor is not 3, but got: " + - std::to_string(input->shape()[kChannelIndexHWC])); - return Invert(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/invert_op.h b/mindspore-lite/minddata/dataset/kernels/image/invert_op.h deleted file mode 100644 index abc76bd3d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/invert_op.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_INVERT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_INVERT_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class InvertOp : public TensorOp { - public: - InvertOp() = default; - - ~InvertOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kInvertOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_INVERT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/CMakeLists.txt b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/canny.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/canny.cc deleted file mode 100644 index b6e97fcec..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/canny.cc +++ /dev/null @@ -1,284 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include - -#include "lite_cv/lite_mat.h" -#include "lite_cv/image_process.h" - -#ifdef ENABLE_ANDROID -#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) -#define USE_NEON -#include -#endif -#endif - -constexpr float kAngle22_5 = 0.39269908169872414; -constexpr float kAngle67_5 = 1.1780972450961724; -constexpr int kCertainBorder = 2; -constexpr float kHalf = 0.5; -constexpr int kUncertainBorder = 1; -constexpr int kNotBorder = 0; - -namespace mindspore { -namespace dataset { -static void GetSobelKernel(float *kernel, int flag, int ksize, double scale) { - std::vector buffer(ksize + 1); - - if (ksize == 1) { - buffer[0] = 1; - } else if (ksize == 3) { - if (flag == 0) { - buffer[0] = 1, buffer[1] = 2, buffer[2] = 1; - } else if (flag == 1) { - buffer[0] = -1, buffer[1] = 0, buffer[2] = 1; - } else { - buffer[0] = 1, buffer[1] = -2, buffer[2] = 1; - } - } else { - float old, now; - buffer[0] = 1; - for (int i = 0; i < ksize; i++) { - buffer[i + 1] = 0; - } - for (int i = 0; i < ksize - flag - 1; i++) { - old = buffer[0]; - for (int j = 1; j <= ksize; j++) { - now = buffer[j] + buffer[j - 1]; - buffer[j - 1] = old; - old = now; - } - } - for (int i = 0; i < flag; i++) { - old = -buffer[0]; - for (int j = 1; j <= ksize; j++) { - now = buffer[j - 1] - buffer[j]; - buffer[j - 1] = old; - old = now; - } - } - } - - scale = flag == 0 ? scale : 1.0; - for (int i = 0; i < ksize; i++) { - kernel[i] = buffer[i] * static_cast(scale); - } -} - -bool Sobel(const LiteMat &src, LiteMat &dst, int flag_x, int flag_y, int ksize, double scale, // NOLINT - PaddBorderType pad_type) { - if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { - return false; - } - if (flag_x < 0 || flag_y < 0 || flag_x + flag_y <= 0 || flag_x >= ksize || flag_y >= ksize) { - return false; - } - - if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || - dst.data_type_ != LDataType::FLOAT32) { - dst.Init(src.width_, src.height_, src.channel_, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - LiteMat kx, ky; - kx.Init(ksize, 1, 1, LDataType::FLOAT32); - ky.Init(1, ksize, 1, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(kx); - RETURN_FALSE_IF_LITEMAT_EMPTY(ky); - - GetSobelKernel(kx, flag_x, ksize, scale); - GetSobelKernel(ky, flag_y, ksize, scale); - - return ConvRowCol(src, kx, ky, dst, LDataType::FLOAT32, pad_type); -} - -static float GetEdge(const std::vector &temp, int width, int height, int x, int y) { - if (x >= 0 && y >= 0 && x < width && y < height) { - return temp[y * width + x]; - } else { - return -1.0f; - } -} - -static float Round(float value) { - // rounding if the result is even - // eg. 1.5 -> 2, 2.5 -> 2 - float rnd = roundf(value); - float rnd_l = floorf(value); - float rnd_h = ceilf(value); - if (std::fabs(value - rnd_l - kHalf) <= std::numeric_limits::epsilon()) { - if (fmod(rnd, 2) == 0) { - return rnd; - } else if (value > 0) { - return rnd_l; - } else { - return rnd_h; - } - } - return rnd; -} - -static bool NonMaximumSuppression(const LiteMat &gx, const LiteMat &gy, LiteMat &edges, bool L2gradient) { // NOLINT - edges.Init(gx.width_, gx.height_, gx.channel_, gx.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(edges); - - const float *gx_ptr = gx; - const float *gy_ptr = gy; - float *edges_ptr = edges; - - int size = gx.height_ * gx.width_; - std::vector temp(size); - for (int i = 0; i < size; i++) { - float gx_value = Round(gx_ptr[i]); - float gy_value = Round(gy_ptr[i]); - if (L2gradient) { - temp[i] = sqrtf(gx_value * gx_value + gy_value * gy_value); - } else { - temp[i] = std::abs(gx_value) + std::abs(gy_value); - } - } - - for (int y = 0; y < gx.height_; y++) { - for (int x = 0; x < gx.width_; x++) { - float gx_value = Round(gx_ptr[y * gx.width_ + x]); - float gy_value = Round(gy_ptr[y * gx.width_ + x]); - - float gx_value_abs = std::abs(gx_value); - float gy_value_abs = std::abs(gy_value); - float angle_value = atan2f(gy_value_abs, gx_value_abs); - float edge_value = temp[y * gx.width_ + x]; - float edge_pre, edge_nex; - if (angle_value < kAngle22_5 || angle_value > kAngle67_5) { - if (angle_value < kAngle22_5) { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y); - } else { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x, y + 1); - } - if (edge_value > edge_pre && edge_value >= edge_nex) { - edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; - } else { - edges_ptr[y * gx.width_ + x] = 0.f; - } - } else { - if (gx_value * gy_value < 0) { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x + 1, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x - 1, y + 1); - } else { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y + 1); - } - if (edge_value > edge_pre && edge_value > edge_nex) { - edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; - } else { - edges_ptr[y * gx.width_ + x] = 0.f; - } - } - } - } - return true; -} - -static void Hysteresis(const LiteMat &edges, uint8_t *dst, double low_thresh, double high_thresh) { - const float *edges_ptr = edges; - - int size = edges.height_ * edges.width_; - std::vector stack; - std::vector buffer(size); - int buffer_step = edges.width_; - for (int y = 0; y < edges.height_; y++) { - for (int x = 0; x < edges.width_; x++) { - int pos = y * edges.width_ + x; - float edge_value = edges_ptr[pos]; - if (edge_value > high_thresh) { - buffer[pos] = kCertainBorder; - stack.push_back(pos); - } else if (edge_value <= low_thresh) { - buffer[pos] = kNotBorder; - } else { - buffer[pos] = kUncertainBorder; - } - } - } - - while (!stack.empty()) { - int pos = stack.back(); - stack.pop_back(); - int y = static_cast(pos / buffer_step); - int x = pos % buffer_step; - for (int i = -1; i < 2; i++) { - for (int j = -1; j < 2; j++) { - int next_y = y + i; - int next_x = x + j; - if (next_y < 0 || next_x < 0 || next_y >= edges.height_ || next_x >= edges.width_ || - (next_y == y && next_x == x)) { - continue; - } - int next = next_y * buffer_step + next_x; - if (buffer[next] == kUncertainBorder) { - buffer[next] = kCertainBorder; - stack.push_back(next); - } - } - } - } - - for (int i = 0; i < size; i++) { - if (buffer[i] == kCertainBorder) { - dst[i] = 255; - } else { - dst[i] = 0; - } - } -} - -bool Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize, // NOLINT - bool L2gradient) { - if (src.IsEmpty() || src.data_type_ != LDataType::UINT8 || src.channel_ != 1) { - return false; - } - if (low_thresh < 0 || high_thresh < 0 || low_thresh > high_thresh) { - return false; - } - if (ksize % 2 == 0 || ksize < 3 || ksize > 7) { - return false; - } - if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || - dst.data_type_ != src.data_type_) { - dst.Init(src.width_, src.height_, src.channel_, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - double scale = ksize == 7 ? 1 / 16.0 : 1.0; - low_thresh *= scale; - high_thresh *= scale; - - LiteMat gx, gy; - Sobel(src, gx, 1, 0, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); - Sobel(src, gy, 0, 1, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); - - LiteMat edges; - bool status = NonMaximumSuppression(gx, gy, edges, L2gradient); - if (!status) { - return false; - } - - Hysteresis(edges, dst, low_thresh, high_thresh); - return true; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc deleted file mode 100644 index b7ee90c34..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include - -#include "lite_cv/lite_mat.h" -#include "lite_cv/image_process.h" - -#ifdef ENABLE_ANDROID -#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) -#define USE_NEON -#include -#endif -#endif - -namespace mindspore { -namespace dataset { -static void GetGaussianKernel(float *kernel, int size, double sigma) { - int n = (size - 1) / 2; - std::vector buffer(n); - float sum = 0; - for (int i = 0; i < n; i++) { - int x = i - n; - float g = expf(-0.5 * x * x / (sigma * sigma)); - buffer[i] = g; - sum += g; - } - sum = sum * 2 + 1; - if (size % 2 == 0) { - sum += 1; - } - - const float scale = 1.F / sum; - for (int i = 0; i < n; i++) { - float g = buffer[i] * scale; - kernel[i] = g; - kernel[size - 1 - i] = g; - } - kernel[n] = scale; - if (size % 2 == 0) { - kernel[n + 1] = scale; - } -} - -bool GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector &ksize, double sigmaX, // NOLINT - double sigmaY, PaddBorderType pad_type) { - if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { - return false; - } - if (ksize.size() != 2 || ksize[0] <= 0 || ksize[1] <= 0 || ksize[0] % 2 != 1 || ksize[1] % 2 != 1) { - return false; - } - if (sigmaX <= 0) { - return false; - } - if (sigmaY <= 0) { - sigmaY = sigmaX; - } - if (ksize[0] == 1 && ksize[1] == 1) { - dst = src; - return true; - } - - LiteMat kx, ky; - kx.Init(ksize[0], 1, 1, LDataType::FLOAT32); - ky.Init(1, ksize[1], 1, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(kx); - RETURN_FALSE_IF_LITEMAT_EMPTY(ky); - - GetGaussianKernel(kx, ksize[0], sigmaX); - GetGaussianKernel(ky, ksize[1], sigmaY); - - return ConvRowCol(src, kx, ky, dst, src.data_type_, pad_type); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.cc deleted file mode 100644 index b0298d500..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.cc +++ /dev/null @@ -1,2160 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef ENABLE_NEON -#include -#endif - -namespace mindspore { -namespace dataset { -constexpr uint32_t kR2Gray = 9798; -constexpr uint32_t kG2Gray = 19235; -constexpr uint32_t kB2Gray = 3735; -constexpr int32_t kGrayShift = 15; -constexpr int32_t kGrayShiftDelta = 1 << (kGrayShift - 1); -constexpr int32_t kYScale = 0x0101; -constexpr int32_t kU2B = -128; -constexpr int32_t kU2G = 25; -constexpr int32_t kV2R = -102; -constexpr int32_t kV2G = 52; -constexpr int32_t kY2G = 18997; -constexpr int32_t kY2GB = -1160; -constexpr int32_t kB2B = kU2B * 128 + kY2GB; -constexpr int32_t kB2G = kU2G * 128 + kV2G * 128 + kY2GB; -constexpr int32_t kB2R = kV2R * 128 + kY2GB; -constexpr int32_t kChannelThree = 3; - -static bool Equal(const float &a, const float &b) { return std::fabs(a - b) < 1e-6; } - -static inline bool InitBilinearWeight(int *data_ptr, int16_t *weight_ptr, double scale, int dst_length, int src_length, - int a) { - const int RESIZE_SCALE = 1 << 11; - if (data_ptr == nullptr || weight_ptr == nullptr) { - return false; - } - - int *data_start_ptr = data_ptr; - int16_t *weight_start_ptr = weight_ptr; - - for (unsigned int i = 0; i < dst_length; i++) { - auto src_f_x = static_cast((i + 0.5) * scale - 0.5); - int src_u_x = static_cast(floor(src_f_x)); - src_f_x -= src_u_x; - if (src_u_x < 0) { - src_u_x = 0; - src_f_x = 0.0f; - } - if (src_u_x >= src_length - 1) { - src_u_x = src_length - 2; - src_f_x = 1.0f; - } - data_start_ptr[i] = src_u_x * a; - int16_t t0 = INT16_CAST((1.0f - src_f_x) * RESIZE_SCALE); - int16_t t1 = INT16_CAST(src_f_x * RESIZE_SCALE); - - weight_start_ptr[i * 2] = t0; - weight_start_ptr[i * 2 + 1] = t1; - } - return true; -} - -static bool ResizeBilinear3C(const unsigned char *src, int src_width, int src_height, unsigned char *dst, int dst_width, - int dst_height) { - double scale_width = static_cast(src_width) / dst_width; - double scale_height = static_cast(src_height) / dst_height; - - if (dst_height >= (INT_MAX / 2 - dst_width)) { - return false; - } - if (dst_height >= (INT_MAX / 3 / dst_width)) { - return false; - } - - // The allocate memory cannot exceed 2GB. - if ((2 * sizeof(int) * (2 * dst_width + dst_height)) > INT_MAX) { - return false; - } - int *data_buf = new (std::nothrow) int[2 * sizeof(int) * (2 * dst_width + dst_height)]; - if (data_buf == nullptr) { - return false; - } - - int *x_offset = data_buf; - int *y_offset = data_buf + dst_width + dst_width; - - auto *x_weight = reinterpret_cast(data_buf + dst_width); - auto *y_weight = reinterpret_cast(data_buf + dst_width + dst_width + dst_height); - - if (!InitBilinearWeight(x_offset, x_weight, scale_width, dst_width, src_width, 3)) { - delete[] data_buf; - return false; - } - if (!InitBilinearWeight(y_offset, y_weight, scale_height, dst_height, src_height, 1)) { - delete[] data_buf; - return false; - } - - LiteMat x_tmp_buf0(dst_width * 3 + 1, LDataType::UINT16); - LiteMat x_tmp_buf1(dst_width * 3 + 1, LDataType::UINT16); - int16_t *row0_ptr = reinterpret_cast(x_tmp_buf0.data_ptr_); - int16_t *row1_ptr = reinterpret_cast(x_tmp_buf1.data_ptr_); - - int prev_height = -2; - - for (int y = 0; y < dst_height; y++) { - int y_span = y_offset[y]; - - if (y_span == prev_height) { - } else if (y_span == prev_height + 1) { - int16_t *tmp = row0_ptr; - row0_ptr = row1_ptr; - row1_ptr = tmp; - const unsigned char *src_start = src + 3 * src_width * (y_span + 1); - const int16_t *x_weight_p = x_weight; - int16_t *row1_ptr1 = row1_ptr; - for (int x = 0; x < dst_width; x++) { - const unsigned char *src_start_p = src_start + x_offset[x]; - row1_ptr1[0] = (src_start_p[0] * x_weight_p[0] + src_start_p[3] * x_weight_p[1]) >> 4; - row1_ptr1[1] = (src_start_p[1] * x_weight_p[0] + src_start_p[4] * x_weight_p[1]) >> 4; - row1_ptr1[2] = (src_start_p[2] * x_weight_p[0] + src_start_p[5] * x_weight_p[1]) >> 4; - x_weight_p += 2; - row1_ptr1 += 3; - } - } else { - const unsigned char *src0 = src + 3 * src_width * (y_span); - const unsigned char *src1 = src + 3 * src_width * (y_span + 1); - - const int16_t *x_weight_ptr = x_weight; - int16_t *row0_ptr0 = row0_ptr; - int16_t *row1_ptr1 = row1_ptr; - for (int x = 0; x < dst_width; x++) { - const unsigned char *src0_ptr = src0 + x_offset[x]; - const unsigned char *src1_ptr = src1 + x_offset[x]; - - for (int c = 0; c < 3; c++) { - row0_ptr0[c] = (src0_ptr[c] * x_weight_ptr[0] + src0_ptr[c + 3] * x_weight_ptr[1]) >> 4; - row1_ptr1[c] = (src1_ptr[c] * x_weight_ptr[0] + src1_ptr[c + 3] * x_weight_ptr[1]) >> 4; - } - - x_weight_ptr += 2; - row0_ptr0 += 3; - row1_ptr1 += 3; - } - } - prev_height = y_span; - - int16_t *row0_ptr0 = row0_ptr; - int16_t *row1_ptr1 = row1_ptr; - unsigned char *dst_ptr = dst + dst_width * 3 * (y); - - for (int k = 0; k < dst_width * 3; k++) { - auto t0 = static_cast((y_weight[0] * static_cast(*row0_ptr0++)) >> 16); - auto t1 = static_cast((y_weight[1] * static_cast(*row1_ptr1++)) >> 16); - *dst_ptr++ = static_cast((t0 + t1 + 2) >> 2); - } - y_weight += 2; - } - delete[] data_buf; - return true; -} - -static bool ResizeBilinear1C(const unsigned char *src, int src_width, int src_height, unsigned char *dst, int dst_width, - int dst_height) { - double scale_width = static_cast(src_width) / dst_width; - double scale_height = static_cast(src_height) / dst_height; - - if (dst_height >= (INT_MAX / 2 - dst_width)) { - return false; - } - if (dst_height >= (INT_MAX / dst_width)) { - return false; - } - - // The allocate memory cannot exceed 2GB. - if ((2 * sizeof(int) * (2 * dst_width + dst_height)) > INT_MAX) { - return false; - } - int *data_buf = new (std::nothrow) int[2 * sizeof(int) * (2 * dst_width + dst_height)]; - if (data_buf == nullptr) { - return false; - } - - int *x_offset = data_buf; - int *y_offset = data_buf + dst_width + dst_width; - - auto *x_weight = reinterpret_cast(data_buf + dst_width); - auto *y_weight = reinterpret_cast(data_buf + dst_width + dst_width + dst_height); - - if (!InitBilinearWeight(x_offset, x_weight, scale_width, dst_width, src_width, 1)) { - delete[] data_buf; - return false; - } - if (!InitBilinearWeight(y_offset, y_weight, scale_height, dst_height, src_height, 1)) { - delete[] data_buf; - return false; - } - - LiteMat x_tmp_buf0(dst_width, LDataType::UINT16); - LiteMat x_tmp_buf1(dst_width, LDataType::UINT16); - int16_t *row0_ptr = reinterpret_cast(x_tmp_buf0.data_ptr_); - int16_t *row1_ptr = reinterpret_cast(x_tmp_buf1.data_ptr_); - - int prev_height = -2; - - for (int y = 0; y < dst_height; y++) { - int y_span = y_offset[y]; - - if (y_span == prev_height) { - } else if (y_span == prev_height + 1) { - int16_t *tmp = row0_ptr; - row0_ptr = row1_ptr; - row1_ptr = tmp; - const unsigned char *src_start = src + src_width * (y_span + 1); - const int16_t *x_weight_p = x_weight; - int16_t *row1_ptr1 = row1_ptr; - for (int x = 0; x < dst_width; x++) { - const unsigned char *src_start_p = src_start + x_offset[x]; - if ((src_start_p + 3 - src) >= (src_width * src_height)) { - continue; - } - row1_ptr1[x] = (src_start_p[0] * x_weight_p[0] + src_start_p[3] * x_weight_p[1]) >> 4; - x_weight_p += 2; - } - } else { - const unsigned char *src0 = src + src_width * (y_span); - const unsigned char *src1 = src + src_width * (y_span + 1); - - const int16_t *x_weight_ptr = x_weight; - int16_t *row0_ptr0 = row0_ptr; - int16_t *row1_ptr1 = row1_ptr; - for (int x = 0; x < dst_width; x++) { - const unsigned char *src0_ptr = src0 + x_offset[x]; - const unsigned char *src1_ptr = src1 + x_offset[x]; - - row0_ptr0[x] = (src0_ptr[0] * x_weight_ptr[0] + src0_ptr[3] * x_weight_ptr[1]) >> 4; - row1_ptr1[x] = (src1_ptr[0] * x_weight_ptr[0] + src1_ptr[3] * x_weight_ptr[1]) >> 4; - - x_weight_ptr += 2; - } - } - prev_height = y_span; - - int16_t *row0_ptr0 = row0_ptr; - int16_t *row1_ptr1 = row1_ptr; - unsigned char *dst_ptr = dst + dst_width * (y); - - for (int k = 0; k < dst_width; k++) { - auto t0 = static_cast((y_weight[0] * static_cast(*row0_ptr0++)) >> 16); - auto t1 = static_cast((y_weight[1] * static_cast(*row1_ptr1++)) >> 16); - *dst_ptr++ = static_cast((t0 + t1 + 2) >> 2); - } - - y_weight += 2; - } - delete[] data_buf; - return true; -} - -static inline uint8_t clip(float value) { - int int_val = static_cast(roundf(value)); - return std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), int_val)); -} - -template -static bool Conv2DImplement(const LiteMat &src, const LiteMat &kernel, T2 *dst, LDataType dst_type, - PaddBorderType pad_type) { - int border_x = static_cast(kernel.width_ / 2); - int border_y = static_cast(kernel.height_ / 2); - - LiteMat pad_mat; - - if ((border_x > INT_MAX / 2) || (src.width_ > INT_MAX - 2 * border_x)) { - return false; - } - if ((border_y > INT_MAX / 2) || (src.height_ > INT_MAX - 2 * border_y)) { - return false; - } - - pad_mat.Init(src.width_ + 2 * border_x, src.height_ + 2 * border_y, src.channel_, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(pad_mat); - - if (!Pad(src, pad_mat, border_y, border_y, border_x, border_x, pad_type)) { - return false; - } - - const T1 *pad_ptr = pad_mat; - const float *kernel_ptr = kernel; - - int pad_step = pad_mat.width_ * pad_mat.channel_; - int dst_step = src.width_ * src.channel_; - - if (src.channel_ == 1) { - for (int y = border_y; y < pad_mat.height_ - border_y; y++) { - for (int x = border_x; x < pad_mat.width_ - border_x; x++) { - float conv_sum = 0; - for (int i = -border_y; i < -border_y + kernel.height_; i++) { - for (int j = -border_x; j < -border_x + kernel.width_; j++) { - conv_sum += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_] * - kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; - } - } - if (dst_type == LDataType::UINT8) { - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_] = clip(conv_sum); - } else { - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_] = conv_sum; - } - } - } - } else if (src.channel_ == 3) { - for (int y = border_y; y < pad_mat.height_ - border_y; y++) { - for (int x = border_x; x < pad_mat.width_ - border_x; x++) { - float conv_sum_b = 0; - float conv_sum_g = 0; - float conv_sum_r = 0; - for (int i = -border_y; i < -border_y + kernel.height_; i++) { - for (int j = -border_x; j < -border_x + kernel.width_; j++) { - conv_sum_b += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_] * - kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; - conv_sum_g += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_ + 1] * - kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; - conv_sum_r += pad_ptr[(y + i) * pad_step + (x + j) * pad_mat.channel_ + 2] * - kernel_ptr[(i + border_y) * kernel.width_ + (j + border_x)]; - } - } - if (dst_type == LDataType::UINT8) { - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_] = clip(conv_sum_b); - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 1] = clip(conv_sum_g); - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 2] = clip(conv_sum_r); - } else { - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_] = conv_sum_b; - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 1] = conv_sum_g; - dst[(y - border_y) * dst_step + (x - border_x) * src.channel_ + 2] = conv_sum_r; - } - } - } - } else { - return false; - } - return true; -} - -bool Conv2D(const LiteMat &src, const LiteMat &kernel, LiteMat &dst, LDataType dst_type, PaddBorderType pad_type) { - if (src.IsEmpty() || kernel.IsEmpty()) { - return false; - } - if ((dst_type != LDataType::UINT8 && dst_type != LDataType::FLOAT32) || kernel.data_type_ != LDataType::FLOAT32) { - return false; - } - if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || - dst.data_type_ != dst_type) { - dst.Init(src.width_, src.height_, src.channel_, dst_type); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - if (src.data_type_ == LDataType::UINT8 && dst.data_type_ == LDataType::UINT8) { - return Conv2DImplement(src, kernel, dst, dst_type, pad_type); - } else if (src.data_type_ == LDataType::UINT8 && dst.data_type_ == LDataType::FLOAT32) { - return Conv2DImplement(src, kernel, dst, dst_type, pad_type); - } else if (src.data_type_ == LDataType::FLOAT32 && dst.data_type_ == LDataType::UINT8) { - return Conv2DImplement(src, kernel, dst, dst_type, pad_type); - } else if (src.data_type_ == LDataType::FLOAT32 && dst.data_type_ == LDataType::FLOAT32) { - return Conv2DImplement(src, kernel, dst, dst_type, pad_type); - } else { - return false; - } -} - -bool ConvRowCol(const LiteMat &src, const LiteMat &kx, const LiteMat &ky, LiteMat &dst, LDataType dst_type, - PaddBorderType pad_type) { - if (src.IsEmpty() || kx.IsEmpty() || ky.IsEmpty()) { - return false; - } - if (dst_type != LDataType::UINT8 && dst_type != LDataType::FLOAT32) { - return false; - } - if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || - dst.data_type_ != dst_type) { - dst.Init(src.width_, src.height_, src.channel_, dst_type); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - LiteMat mid; - bool ret = Conv2D(src, kx, mid, LDataType::FLOAT32, pad_type) && Conv2D(mid, ky, dst, dst_type, pad_type); - return ret; -} - -bool ResizeBilinear(const LiteMat &src, LiteMat &dst, int dst_w, int dst_h) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (dst_h <= 0 || dst_w <= 0) { - return false; - } - if (src.data_type_ != LDataType::UINT8) { - return false; - } - if (src.channel_ != 3 && src.channel_ != 1) { - return false; - } - if (dst.IsEmpty()) { - dst.Init(dst_w, dst_h, src.channel_, LDataType::UINT8); - if (dst.IsEmpty()) { - return false; - } - } - if (dst.height_ != dst_h || dst.width_ != dst_w || dst.channel_ != src.channel_) { - return false; - } - if (dst.data_type_ != LDataType::UINT8) { - return false; - } - - if (src.channel_ == 3) { - const unsigned char *src_start_p = src; - unsigned char *dst_start_p = dst; - return ResizeBilinear3C(src_start_p, src.width_, src.height_, dst_start_p, dst_w, dst_h); - } else { // channel == 1 - const unsigned char *src_start_p = src; - unsigned char *dst_start_p = dst; - return ResizeBilinear1C(src_start_p, src.width_, src.height_, dst_start_p, dst_w, dst_h); - } -} - -static bool ConvertBGR(const unsigned char *data, LDataType data_type, int w, int h, LiteMat &mat) { - if (data_type == LDataType::UINT8) { - mat.Init(w, h, kChannelThree, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - unsigned char *dst_ptr = mat; - // mindspore lite version, there is no securec lib - memcpy(dst_ptr, data, w * h * kChannelThree * sizeof(unsigned char)); - } else { - return false; - } - return true; -} - -static bool ConvertRGBAToBGR(const unsigned char *data, LDataType data_type, int w, int h, LiteMat &mat) { - if (data_type == LDataType::UINT8) { - mat.Init(w, h, kChannelThree, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - unsigned char *ptr = mat; - const unsigned char *data_ptr = data; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - ptr[0] = data_ptr[2]; - ptr[1] = data_ptr[1]; - ptr[2] = data_ptr[0]; - ptr += 3; - data_ptr += 4; - } - } - } else { - return false; - } - return true; -} - -static bool ConvertRGBAToRGB(const unsigned char *data, LDataType data_type, int w, int h, LiteMat &mat) { - if (data_type == LDataType::UINT8) { - mat.Init(w, h, kChannelThree, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - unsigned char *ptr = mat; - const unsigned char *data_ptr = data; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - ptr[0] = data_ptr[0]; - ptr[1] = data_ptr[1]; - ptr[2] = data_ptr[2]; - ptr += 3; - data_ptr += 4; - } - } - } else { - return false; - } - return true; -} - -static bool ConvertYUV420SPToBGR(const uint8_t *data, LDataType data_type, bool flag, int w, int h, LiteMat &mat) { - if (data == nullptr || w <= 0 || h <= 0) { - return false; - } - if (data_type == LDataType::UINT8) { - mat.Init(w, h, kChannelThree, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - const uint8_t *y_ptr = data; - const uint8_t *uv_ptr = y_ptr + w * h; - uint8_t *bgr_ptr = mat; - const int bgr_stride = 3 * w; - - for (uint64_t y = 0; y < h; ++y) { - uint8_t *bgr_buf = bgr_ptr; - const uint8_t *uv_buf = uv_ptr; - const uint8_t *y_buf = y_ptr; - uint8_t u; - uint8_t v; - for (int x = 0; x < w - 1; x += 2) { - if (flag) { - // NV21 - u = uv_buf[1]; - v = uv_buf[0]; - } else { - // NV12 - u = uv_buf[0]; - v = uv_buf[1]; - } - uint32_t tmp_y = (uint32_t)(y_buf[0] * kYScale * kY2G) >> 16; - // b - bgr_buf[0] = std::clamp((int32_t)(-(u * kU2B) + tmp_y + kB2B) >> 6, 0, 255); - // g - bgr_buf[1] = std::clamp((int32_t)(-(u * kU2G + v * kV2G) + tmp_y + kB2G) >> 6, 0, 255); - // r - bgr_buf[2] = std::clamp((int32_t)(-(v * kV2R) + tmp_y + kB2R) >> 6, 0, 255); - - tmp_y = (uint32_t)(y_buf[1] * kYScale * kY2G) >> 16; - bgr_buf[3] = std::clamp((int32_t)(-(u * kU2B) + tmp_y + kB2B) >> 6, 0, 255); - bgr_buf[4] = std::clamp((int32_t)(-(u * kU2G + v * kV2G) + tmp_y + kB2G) >> 6, 0, 255); - bgr_buf[5] = std::clamp((int32_t)(-(v * kV2R) + tmp_y + kB2R) >> 6, 0, 255); - - y_buf += 2; - uv_buf += 2; - bgr_buf += 6; - } - if (w & 1) { - if (flag) { - // NV21 - u = uv_buf[1]; - v = uv_buf[0]; - } else { - // NV12 - u = uv_buf[0]; - v = uv_buf[1]; - } - uint32_t tmp_y = (uint32_t)(y_buf[0] * kYScale * kY2G) >> 16; - bgr_buf[0] = std::clamp((int32_t)(-(u * kU2B) + tmp_y + kB2B) >> 6, 0, 255); - bgr_buf[1] = std::clamp((int32_t)(-(u * kU2G + v * kV2G) + tmp_y + kB2G) >> 6, 0, 255); - bgr_buf[2] = std::clamp((int32_t)(-(v * kV2R) + tmp_y + kB2R) >> 6, 0, 255); - } - - bgr_ptr += bgr_stride; - y_ptr += w; - if (y & 1) { - uv_ptr += w; - } - } - } - return true; -} - -static bool ConvertRGBAToGRAY(const unsigned char *data, LDataType data_type, int w, int h, LiteMat &mat) { - if (data_type == LDataType::UINT8) { - mat.Init(w, h, 1, LDataType::UINT8); - if (mat.IsEmpty()) { - return false; - } - unsigned char *ptr = mat; - const unsigned char *data_ptr = data; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - *ptr = (data_ptr[2] * kB2Gray + data_ptr[1] * kG2Gray + data_ptr[0] * kR2Gray + kGrayShiftDelta) >> kGrayShift; - ptr++; - data_ptr += 4; - } - } - } else { - return false; - } - return true; -} - -bool InitFromPixel(const unsigned char *data, LPixelType pixel_type, LDataType data_type, int w, int h, LiteMat &m) { - if (data == nullptr) { - return false; - } - if (w <= 0 || h <= 0) { - return false; - } - - if (data_type != LDataType::UINT8) { - return false; - } - if (pixel_type == LPixelType::RGBA2BGR) { - return ConvertRGBAToBGR(data, data_type, w, h, m); - } else if (pixel_type == LPixelType::RGBA2GRAY) { - return ConvertRGBAToGRAY(data, data_type, w, h, m); - } else if (pixel_type == LPixelType::RGBA2RGB) { - return ConvertRGBAToRGB(data, data_type, w, h, m); - } else if (pixel_type == LPixelType::NV212BGR) { - return ConvertYUV420SPToBGR(data, data_type, true, w, h, m); - } else if (pixel_type == LPixelType::NV122BGR) { - return ConvertYUV420SPToBGR(data, data_type, false, w, h, m); - } else if (pixel_type == LPixelType::BGR) { - return ConvertBGR(data, data_type, w, h, m); - } else if (pixel_type == LPixelType::RGB) { - return ConvertBGR(data, data_type, w, h, m); - } else { - return false; - } -} - -bool ConvertTo(const LiteMat &src, LiteMat &dst, double scale) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (src.data_type_ != LDataType::UINT8) { - return false; - } - - if (scale < 0.0 || scale > 100) { - return false; - } - - if (dst.IsEmpty()) { - dst.Init(src.width_, src.height_, src.channel_, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } else if (src.width_ != dst.width_ || src.height_ != dst.height_ || src.channel_ != dst.channel_ || - dst.data_type_ != LDataType::FLOAT32) { - return false; - } - - const auto *src_ptr = reinterpret_cast(src.data_ptr_); - auto *dst_ptr = reinterpret_cast(dst.data_ptr_); - int64_t total_size = src.height_ * src.width_ * src.channel_; - int64_t x = 0; -#ifdef ENABLE_NEON - float32x4_t v_scale = vdupq_n_f32(static_cast(scale)); - float32x4_t v_c = vdupq_n_f32(0.0f); - const int64_t step = 16; - for (; x <= total_size - step; x += step) { - uint8x16_t v_src = vld1q_u8(src_ptr + x); - uint8x16_t v_dst; - - uint16x8_t v_l_16x8 = vmovl_u8(vget_low_u8(v_src)); - uint16x8_t v_h_16x8 = vmovl_u8(vget_high_u8(v_src)); - - float32x4_t v_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_l_16x8))); - float32x4_t v_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_l_16x8))); - float32x4_t v_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v_h_16x8))); - float32x4_t v_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v_h_16x8))); - -#if defined(__aarch64__) || defined(_M_ARM64) - v_ll_f32x4 = vfmaq_f32(v_c, v_ll_f32x4, v_scale); - v_lh_f32x4 = vfmaq_f32(v_c, v_lh_f32x4, v_scale); - v_hl_f32x4 = vfmaq_f32(v_c, v_hl_f32x4, v_scale); - v_hh_f32x4 = vfmaq_f32(v_c, v_hh_f32x4, v_scale); -#else - v_ll_f32x4 = vmlaq_f32(v_c, v_ll_f32x4, v_scale); - v_lh_f32x4 = vmlaq_f32(v_c, v_lh_f32x4, v_scale); - v_hl_f32x4 = vmlaq_f32(v_c, v_hl_f32x4, v_scale); - v_hh_f32x4 = vmlaq_f32(v_c, v_hh_f32x4, v_scale); -#endif - - vst1q_f32(dst_ptr + x, v_ll_f32x4); - vst1q_f32(dst_ptr + x + 4, v_lh_f32x4); - vst1q_f32(dst_ptr + x + 8, v_hl_f32x4); - vst1q_f32(dst_ptr + x + 12, v_hh_f32x4); - } -#endif - for (; x < total_size; x++) { - dst_ptr[x] = static_cast(src_ptr[x] * scale); - } - return true; -} - -template -static bool CropInternal(const LiteMat &src, LiteMat &dst, int x, int y, int w, int h) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - int dst_h = h; - int dst_w = w; - int dst_c = src.channel_; - if (dst.IsEmpty()) { - dst.Init(dst_w, dst_h, dst_c, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - if (dst.height_ != h || dst.width_ != w || dst.channel_ != src.channel_) { - return false; - } - if (dst.data_type_ != src.data_type_) { - return false; - } - const T *src_start_p = src; - T *dst_start_p = dst; - for (int i_h = 0; i_h < dst_h; i_h++) { - const T *src_index_p = src_start_p + (y + i_h) * src.width_ * dst_c + x * dst_c; - T *dst_index_p = dst_start_p + i_h * dst_w * dst_c; - // mindspore lite version, there is no securec lib - memcpy(dst_index_p, src_index_p, dst_w * dst_c * sizeof(T)); - } - return true; -} - -bool Crop(const LiteMat &src, LiteMat &dst, int x, int y, int w, int h) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (x < 0 || y < 0 || w <= 0 || h <= 0) { - return false; - } - if (y > src.height_ - h || x > src.width_ - w) { - return false; - } - - if (src.data_type_ == LDataType::UINT8) { - return CropInternal(src, dst, x, y, w, h); - } else if (src.data_type_ == LDataType::FLOAT32) { - return CropInternal(src, dst, x, y, w, h); - } else { - return false; - } -} - -static bool CheckZero(const std::vector &vs) { - return std::any_of(vs.begin(), vs.end(), [](const float &v) { return Equal(v, 0.0f); }); -} - -static bool CheckZero(const std::vector &vs) { - return std::any_of(vs.begin(), vs.end(), [](const float &v) { return v == 0; }); -} - -static bool CheckMeanAndStd(const LiteMat &src, LiteMat &dst, int channel, const std::vector &mean, - const std::vector &std) { - if (mean.empty() && std.empty()) { - return false; - } - if (src.data_type_ != LDataType::FLOAT32 && src.data_type_ != LDataType::UINT8) { - return false; - } - if (!mean.empty()) { - if (mean.size() != channel) { - return false; - } - } - if (!std.empty()) { - if (CheckZero(std)) { - return false; - } - if (std.size() != channel) { - return false; - } - } - if (dst.IsEmpty()) { - dst.Init(src.width_, src.height_, src.channel_, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - if (dst.height_ != src.height_ || dst.width_ != src.width_ || dst.channel_ != src.channel_) { - return false; - } - if (dst.data_type_ != LDataType::FLOAT32) { - return false; - } - return true; -} - -bool SubStractMeanNormalize(const LiteMat &src, LiteMat &dst, const std::vector &mean, - const std::vector &std) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (!CheckMeanAndStd(src, dst, src.channel_, mean, std)) { - return false; - } - LiteMat src_f; - if (src.data_type_ == LDataType::UINT8) { - ConvertTo(src, src_f, 1.0); - } else { - src_f = src; - } - - const float *src_start_p = src_f; - float *dst_start_p = dst; - if ((!mean.empty()) && std.empty()) { - for (int h = 0; h < src_f.height_; h++) { - for (int w = 0; w < src_f.width_; w++) { - uint32_t src_start = (h * src_f.width_ + w) * src_f.channel_; - for (int c = 0; c < src_f.channel_; c++) { - uint32_t index = src_start + c; - dst_start_p[index] = src_start_p[index] - mean[c]; - } - } - } - } else if (mean.empty() && (!std.empty())) { - for (int h = 0; h < src_f.height_; h++) { - for (int w = 0; w < src_f.width_; w++) { - uint32_t src_start = (h * src_f.width_ + w) * src_f.channel_; - for (int c = 0; c < src_f.channel_; c++) { - uint32_t index = src_start + c; - dst_start_p[index] = src_start_p[index] / std[c]; - } - } - } - } else if ((!mean.empty()) && (!std.empty())) { - for (int h = 0; h < src_f.height_; h++) { - for (int w = 0; w < src_f.width_; w++) { - uint32_t src_start = (h * src_f.width_ + w) * src_f.channel_; - for (int c = 0; c < src_f.channel_; c++) { - uint32_t index = src_start + c; - dst_start_p[index] = (src_start_p[index] - mean[c]) / std[c]; - } - } - } - } else { - return false; - } - return true; -} - -template -static void PadWithConstant(const LiteMat &src, LiteMat &dst, const int top, const int bottom, const int left, - const int right, const PaddBorderType pad_type, uint8_t fill_b_or_gray, uint8_t fill_g, - uint8_t fill_r) { - std::vector row_buffer(dst.width_ * dst.channel_ * dst.elem_size_); - T *const_ptr = reinterpret_cast(row_buffer.data()); - int src_step = src.width_ * src.channel_ * src.elem_size_; - int dst_step = dst.width_ * dst.channel_ * dst.elem_size_; - if (dst.channel_ == 1) { - for (int i = 0; i < dst_step; i++) { - const_ptr[i] = fill_b_or_gray; - } - } else if (dst.channel_ == 3) { - for (int i = 0; i < dst.width_; i++) { - const_ptr[i * dst.channel_] = fill_b_or_gray; - const_ptr[i * dst.channel_ + 1] = fill_g; - const_ptr[i * dst.channel_ + 2] = fill_r; - } - } - - auto *dst_ptr = reinterpret_cast(dst.data_ptr_); - auto *src_ptr = reinterpret_cast(src.data_ptr_); - for (int i = 0; i < top; i++) { - // mindspore lite version, there is no securec lib - memcpy(dst_ptr + i * dst_step, const_ptr, dst_step); - } - - int left_size = left * dst.channel_ * dst.elem_size_; - int right_size = right * dst.channel_ * dst.elem_size_; - uint8_t *dst_raw_data = dst_ptr + static_cast(top * dst_step + left_size); - for (int i = 0; i < src.height_; i++, dst_raw_data += dst_step, src_ptr += src_step) { - // mindspore lite version, there is no securec lib - memcpy(dst_raw_data, src_ptr, src_step); - memcpy(dst_raw_data - left_size, const_ptr, left_size); - memcpy(dst_raw_data + src_step, const_ptr, right_size); - } - - for (int i = dst.height_ - bottom; i < dst.height_; i++) { - // mindspore lite version, there is no securec lib - memcpy(dst_ptr + i * dst_step, const_ptr, dst_step); - } -} - -static int PadFromPos(int p, int len, PaddBorderType pad_type) { - constexpr auto pixel_factor = 2; - if (p >= 0 && p < len) { - return p; - } - if (pad_type == PaddBorderType::PADD_BORDER_REPLICATE) { - return p < 0 ? 0 : len - 1; - } else { - // calculate the position of pixel in reflect mode like edcb|abcdef|edcb - while (p < 0 || p >= len) { - if (p < 0) { - p = -p; - } else { - p = pixel_factor * len - p - pixel_factor; - } - } - return p; - } -} - -template -static void PadImplement(const LiteMat &src, LiteMat &dst, const int top, const int bottom, const int left, - const int right, const PaddBorderType pad_type) { - int src_step = src.width_ * src.channel_; - int dst_step = dst.width_ * dst.channel_; - - auto *src_data_ptr = reinterpret_cast(src.data_ptr_); - auto *dst_data_ptr = reinterpret_cast(dst.data_ptr_); - for (int i = 0; i < src.height_; i++) { - // mindspore lite version, there is no securec lib - memcpy(dst_data_ptr + (i + top) * dst.steps_[0] + left * dst.steps_[1], src_data_ptr + i * src.steps_[0], - src.steps_[0]); - } - - const T *src_ptr = src; - T *dst_ptr = dst; - for (int y = 0; y < dst.height_; y++) { - for (int x = 0; x < dst.width_; x++) { - if (y < top || y >= dst.height_ - bottom || x < left || x >= dst.width_ - right) { - int src_y = PadFromPos(y - top, src.height_, pad_type); - int src_x = PadFromPos(x - left, src.width_, pad_type); - for (int cn = 0; cn < dst.channel_; cn++) { - dst_ptr[y * dst_step + x * dst.channel_ + cn] = src_ptr[src_y * src_step + src_x * src.channel_ + cn]; - } - } - } - } -} - -template -void ExtractChannelImpl(const T *src_ptr, T *dst_ptr, int height, int width, int channel, int col) { - int total = height * width; - int i = 0; - int src_idx = col; - for (; i < total; i++, src_idx += channel) { - dst_ptr[i] = src_ptr[src_idx]; - } -} - -bool ExtractChannel(LiteMat &src, LiteMat &dst, int col) { - if (src.IsEmpty() || col < 0 || col > src.channel_ - 1) { - return false; - } - - if (src.data_type_ == LDataType::FLOAT32 || src.data_type_ == LDataType::UINT8) { - if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != 1 || - dst.data_type_ != src.data_type_) { - dst.Init(src.width_, src.height_, 1, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - } - - if (src.data_type_ == LDataType::FLOAT32) { - ExtractChannelImpl(src, dst, src.height_, src.width_, src.channel_, col); - } else if (src.data_type_ == LDataType::UINT8) { - ExtractChannelImpl(src, dst, src.height_, src.width_, src.channel_, col); - } else { - return false; - } - return true; -} - -bool Split(const LiteMat &src, std::vector &mv) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (src.data_type_ == LDataType::FLOAT32) { - const float *src_start_p = src; - for (int c = 0; c < src.channel_; c++) { - LiteMat dst; - dst.Init(src.width_, src.height_, 1, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - float *dst_start_p = dst; - for (int h = 0; h < src.height_; h++) { - uint32_t src_start = h * src.width_ * src.channel_; - uint32_t dst_start = h * dst.width_; - for (int w = 0; w < src.width_; w++) { - uint32_t src_index = src_start + w * src.channel_ + c; - uint32_t dst_index = dst_start + w; - dst_start_p[dst_index] = src_start_p[src_index]; - } - } - mv.emplace_back(dst); - } - return true; - } else if (src.data_type_ == LDataType::UINT8) { - const uint8_t *src_start_p = src; - for (int c = 0; c < src.channel_; c++) { - LiteMat dst; - dst.Init(src.width_, src.height_, 1, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - uint8_t *dst_start_p = dst; - for (int h = 0; h < src.height_; h++) { - uint32_t src_start = h * src.width_ * src.channel_; - uint32_t dst_start = h * dst.width_; - for (int w = 0; w < src.width_; w++) { - uint32_t src_index = src_start + w * src.channel_ + c; - uint32_t dst_index = dst_start + w; - dst_start_p[dst_index] = src_start_p[src_index]; - } - } - mv.emplace_back(dst); - } - return true; - } else { - return false; - } -} - -template -inline void MergeImpl(const std::vector &mv, T *dst_ptr, int height, int width, int channel) { - T *mv_ptr[4]; - int area = height * width; - for (int c = 0; c < channel; c++) { - mv_ptr[c] = reinterpret_cast(mv[c].data_ptr_); - } - for (int i = 0; i < area; i++) { - for (int c = 0; c < channel; c++) { - dst_ptr[c] = *mv_ptr[c]; - mv_ptr[c]++; - } - dst_ptr += channel; - } -} - -bool Merge(const std::vector &mv, LiteMat &dst) { - constexpr int64_t mv_size_three = 3; - constexpr int64_t mv_size_four = 4; - if (mv.size() != 1 && mv.size() != mv_size_three && mv.size() != mv_size_four) { - return false; - } - - int width = mv[0].width_; - int height = mv[0].height_; - int channel = static_cast(mv.size()); - LDataType data_type = mv[0].data_type_; - - // The arrays in list must be single-channel - if (std::any_of(mv.begin(), mv.end(), [](const LiteMat &m) { return m.IsEmpty() || m.channel_ != 1; })) { - return false; - } - - for (int i = 1; i < mv.size(); i++) { - if (width != mv[i].width_ || height != mv[i].height_ || data_type != mv[i].data_type_) { - return false; - } - } - - if (dst.IsEmpty() || dst.width_ != width || dst.height_ != height || dst.channel_ != channel || - dst.data_type_ != data_type) { - dst.Init(width, height, channel, data_type); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - if (dst.data_type_ == LDataType::FLOAT32) { - MergeImpl(mv, dst, height, width, channel); - } else if (dst.data_type_ == LDataType::UINT8) { - MergeImpl(mv, dst, height, width, channel); - } else { - return false; - } - return true; -} - -inline bool CheckInt(const std::vector &nums) { - if (std::any_of(nums.begin(), nums.end(), [](const auto &num) { return num < 0; })) { - return false; - } - return true; -} - -bool Pad(const LiteMat &src, LiteMat &dst, int top, int bottom, int left, int right, PaddBorderType pad_type, - uint8_t fill_b_or_gray, uint8_t fill_g, uint8_t fill_r) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (!CheckInt({top, bottom, left, right})) { - return false; - } - if (src.width_ > std::numeric_limits::max() - left || - src.width_ + left > std::numeric_limits::max() - right) { - return false; - } - if (src.height_ > std::numeric_limits::max() - top || - src.height_ + top > std::numeric_limits::max() - bottom) { - return false; - } - int dst_width = src.width_ + left + right; - int dst_height = src.height_ + top + bottom; - if (dst.IsEmpty()) { - dst.Init(dst_width, dst_height, src.channel_, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - if (dst.width_ != dst_width || dst.height_ != dst_height || src.channel_ != dst.channel_) { - return false; - } - if (src.data_type_ != dst.data_type_) { - return false; - } - if (pad_type == PADD_BORDER_CONSTANT && src.data_type_ == LDataType::FLOAT32) { - PadWithConstant(src, dst, top, bottom, left, right, pad_type, fill_b_or_gray, fill_g, fill_r); - } else if (pad_type == PADD_BORDER_CONSTANT && src.data_type_ == LDataType::UINT8) { - PadWithConstant(src, dst, top, bottom, left, right, pad_type, fill_b_or_gray, fill_g, fill_r); - } else if (src.data_type_ == LDataType::FLOAT32) { - PadImplement(src, dst, top, bottom, left, right, pad_type); - } else if (src.data_type_ == LDataType::UINT8) { - PadImplement(src, dst, top, bottom, left, right, pad_type); - } else { - return false; - } - return true; -} - -std::vector> GetDefaultBoxes(const BoxesConfig &config) { - size_t size = config.num_default.size(); - if (size <= 1 || config.feature_size.size() != size || config.steps.size() != size || - config.aspect_rations.size() != size) { - return {}; - } - if (config.max_scale < config.min_scale) { - return {}; - } - std::vector fk; - auto num = static_cast(config.img_shape[0]); - for (auto step : config.steps) { - if (step == 0) { - return {}; - } - fk.push_back(num / static_cast(step)); - } - float scale_rate = (config.max_scale - config.min_scale) / static_cast(config.num_default.size() - 1); - std::vector scales(config.num_default.size()); - for (int i = 0; i < scales.size(); i++) { - scales[i] = config.min_scale + scale_rate * static_cast(i); - } - scales.push_back(1.0f); - std::vector> default_boxes; - for (auto i = 0; i < config.feature_size.size(); i++) { - float sk1 = scales[i]; - float sk2 = scales[i + 1]; - float sk3 = std::sqrt(sk1 * sk2); - std::vector> all_sizes; - float w; - float h; - constexpr int32_t kRation = 2; - if (i == 0) { - // Multiplying the square root by sk1 - w = sk1 * sqrtf(kRation); - // Divide sk2 by the square root - h = sk1 / sqrtf(kRation); - all_sizes = {{0.1, 0.1}, {w, h}, {h, w}}; - } else { - all_sizes = {{sk1, sk1}}; - for (float ration : config.aspect_rations[i]) { - w = sk1 * sqrtf(ration); - h = sk1 / sqrtf(ration); - all_sizes.push_back({w, h}); - all_sizes.push_back({h, w}); - } - all_sizes.push_back({sk3, sk3}); - } - - for (int j = 0; j < config.feature_size[i]; j++) { - for (int k = 0; k < config.feature_size[i]; k++) { - for (auto &all_size : all_sizes) { - float cx = (static_cast(k) + 0.5F) / fk[i]; - float cy = (static_cast(j) + 0.5F) / fk[i]; - default_boxes.push_back({cy, cx, all_size[1], all_size[0]}); - } - } - } - } - return default_boxes; -} - -void ConvertBoxes(std::vector> &boxes, const std::vector> &default_boxes, - const BoxesConfig &config) { - constexpr int64_t prior_scaling_size = 2; - if (boxes.size() != default_boxes.size() || config.prior_scaling.size() != prior_scaling_size) { - boxes = {}; - return; - } - for (int i = 0; i < default_boxes.size(); i++) { - if (boxes[i].size() != 4 || default_boxes[i].size() != 4) { - boxes = {}; - return; - } - // 0, 1, 2, 3 is the vector index of boxes and default_boxes - boxes[i][0] = boxes[i][0] * config.prior_scaling[0] * default_boxes[i][2] + default_boxes[i][0]; - boxes[i][1] = boxes[i][1] * config.prior_scaling[0] * default_boxes[i][3] + default_boxes[i][1]; - boxes[i][2] = expf(boxes[i][2] * config.prior_scaling[1]) * default_boxes[i][2]; - boxes[i][3] = expf(boxes[i][3] * config.prior_scaling[1]) * default_boxes[i][3]; - } -} - -std::vector ApplyNms(const std::vector> &all_boxes, std::vector &all_scores, float thres, - int max_boxes) { - if (all_boxes.size() != all_scores.size()) { - return {}; - } - size_t boxes_num = all_boxes.size(); - std::vector areas(boxes_num); - std::vector order(boxes_num); - for (auto i = 0; i < boxes_num; i++) { - if (all_boxes[i].size() < 4) { - return {}; - } - areas[i] = (all_boxes[i][3] - all_boxes[i][1] + 1) * (all_boxes[i][2] - all_boxes[i][0] + 1); - order[i] = i; - } - - std::sort(order.begin(), order.end(), - [&all_scores](int pos1, int pos2) { return (all_scores[pos1] > all_scores[pos2]); }); - std::vector keep; - while (!order.empty()) { - int i = order[0]; - keep.push_back(i); - if (keep.size() >= max_boxes) { - break; - } - int len = static_cast(order.size() - 1); - std::vector ovr(len); - for (int j = 0; j < len; j++) { - float xx1 = std::max(all_boxes[i][1], all_boxes[order[j + 1]][1]); - float yy1 = std::max(all_boxes[i][0], all_boxes[order[j + 1]][0]); - float xx2 = std::min(all_boxes[i][3], all_boxes[order[j + 1]][3]); - float yy2 = std::min(all_boxes[i][2], all_boxes[order[j + 1]][2]); - - float w = std::max(0.0f, xx2 - xx1 + 1); - float h = std::max(0.0f, yy2 - yy1 + 1); - float inter = w * h; - ovr[j] = inter / (areas[i] + areas[order[j + 1]] - inter); - } - std::vector inds; - for (int j = 0; j < len; j++) { - if (ovr[j] <= thres) { - inds.push_back(j + 1); - } - } - std::vector new_order; - new_order.reserve(inds.size()); - (void)std::transform(inds.begin(), inds.end(), std::back_inserter(new_order), - [&order](int index) -> int { return order[index]; }); - order = new_order; - } - return keep; -} - -template -bool ImplementAffine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector &dsize, - Pixel_Type borderValue) { - if (src.IsEmpty() || dsize.size() != 2 || CheckZero(dsize)) { - return false; - } - - double IM[6]; - for (int i = 0; i < 6; i++) { - IM[i] = M[i]; - } - - double D = IM[0] * IM[4] - IM[1] * IM[3]; - D = std::fabs(D) > std::numeric_limits::epsilon() ? 1.0f / D : 0; - double A11 = IM[4] * D, A22 = IM[0] * D; - IM[0] = A11; - IM[1] *= -D; - IM[3] *= -D; - IM[4] = A22; - double b1 = -IM[0] * IM[2] - IM[1] * IM[5]; - double b2 = -IM[3] * IM[2] - IM[4] * IM[5]; - IM[2] = b1; - IM[5] = b2; - if (out_img.IsEmpty()) { - out_img.Init(static_cast(dsize[0]), static_cast(dsize[1]), sizeof(Pixel_Type), src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(out_img); - } else if (out_img.height_ != dsize[1] || out_img.width_ != dsize[0] || out_img.channel_ != src.channel_) { - return false; - } else if (out_img.data_type_ != src.data_type_) { - return false; - } - - for (int y = 0; y < out_img.height_; y++) { - for (int x = 0; x < out_img.width_; x++) { - int src_x = IM[0] * x + IM[1] * y + IM[2]; - int src_y = IM[3] * x + IM[4] * y + IM[5]; - if (src_x >= 0 && src_y >= 0 && src_x < src.width_ && src_y < src.height_) { - Pixel_Type src_pixel = static_cast(src.data_ptr_)[src_y * src.width_ + src_x]; - static_cast(out_img.data_ptr_)[y * out_img.width_ + x] = src_pixel; - } else { - static_cast(out_img.data_ptr_)[y * out_img.width_ + x] = borderValue; - } - } - } - - return true; -} - -bool Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, UINT8_C1 borderValue) { - if (src.channel_ == 1 && src.data_type_ == LDataType::UINT8) { - return ImplementAffine(src, out_img, M, dsize, borderValue); - } else { - return false; - } -} - -bool Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, UINT8_C3 borderValue) { - if (src.channel_ == 3 && src.data_type_ == LDataType::UINT8) { - return ImplementAffine(src, out_img, M, dsize, borderValue); - } else { - return false; - } -} - -bool Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, FLOAT32_C1 borderValue) { - if (src.channel_ == 1 && src.data_type_ == LDataType::FLOAT32) { - return ImplementAffine(src, out_img, M, dsize, borderValue); - } else { - return false; - } -} - -bool Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, FLOAT32_C3 borderValue) { - constexpr int64_t channel = 3; - if (src.channel_ == channel && src.data_type_ == LDataType::FLOAT32) { - return ImplementAffine(src, out_img, M, dsize, borderValue); - } else { - return false; - } -} - -inline void RotationMatrix2DImpl(float x, float y, double angle, double scale, LiteMat &M) { - angle *= CV_PI / 180; - double alpha = std::cos(angle) * scale; - double beta = std::sin(angle) * scale; - - M.ptr(0)[0] = alpha; - M.ptr(0)[1] = beta; - M.ptr(0)[2] = (1 - alpha) * x - beta * y; - M.ptr(1)[0] = -beta; - M.ptr(1)[1] = alpha; - M.ptr(1)[2] = beta * x + (1 - alpha) * y; -} - -bool GetRotationMatrix2D(float x, float y, double angle, double scale, LiteMat &M) { - M.Init(3, 2, LDataType(LDataType::DOUBLE)); - RETURN_FALSE_IF_LITEMAT_EMPTY(M); - RotationMatrix2DImpl(x, y, angle, scale, M); - return true; -} - -template -bool TransposeImpl(const LiteMat &src, LiteMat &dst) { - int m = src.width_; - int n = src.height_; - - dst.Init(n, m, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - dst.ptr(i)[j] = src.ptr(j)[i]; - } - } - - return true; -} - -bool Transpose(const LiteMat &src, LiteMat &dst) { - if (src.IsEmpty()) { - return false; - } - if (src.data_type_ == LDataType::DOUBLE) { - return TransposeImpl(src, dst); - } else if (src.data_type_ == LDataType::FLOAT32) { - return TransposeImpl(src, dst); - } else { - return false; - } -} - -template -static inline T Hypot_(T a, T b) { - a = std::abs(a); - b = std::abs(b); - if (a > b) { - b /= a; - return a * std::sqrt(1 + b * b); - } - - if (b > 0) { - a /= b; - return b * std::sqrt(1 + a * a); - } - return 0; -} - -template -void Calculation(int n, int m, std::vector &W, LiteMat &A, LiteMat &V, const T eps) { - int max_iter = std::max(m, 30); - for (int iter = 0; iter < max_iter; iter++) { - bool change = false; - T c; - T s; - - for (int i = 0; i < n - 1; i++) { - for (int j = i + 1; j < n; j++) { - T *Ai = A.ptr(i); - T *Aj = A.ptr(j); - double a = W[i]; - double p = 0; - double b = W[j]; - - for (int k = 0; k < m; k++) { - p += static_cast(Ai[k] * Aj[k]); - } - - if (std::abs(p) <= eps * std::sqrt(static_cast(a * b))) { - continue; - } - - p *= 2; - double beta = a - b; - double gamma = Hypot_(static_cast(p), beta); - - if (beta < 0) { - double delta = (gamma - beta) * 0.5; - s = (T)std::sqrt(delta / gamma); - c = (T)(p / (gamma * s * 2)); - } else { - c = (T)std::sqrt((gamma + beta) / (gamma * 2)); - s = (T)(p / (gamma * c * 2)); - } - - a = 0; - b = 0; - for (int k = 0; k < m; k++) { - T t0 = c * Ai[k] + s * Aj[k]; - T t1 = -s * Ai[k] + c * Aj[k]; - Ai[k] = t0; - Aj[k] = t1; - a += static_cast(t0 * t0); - b += static_cast(t1 * t1); - } - W[i] = a; - W[j] = b; - change = true; - T *Vi = V.ptr(i); - T *Vj = V.ptr(j); - - for (int k = 0; k < n; k++) { - T t0 = c * Vi[k] + s * Vj[k]; - T t1 = -s * Vi[k] + c * Vj[k]; - Vi[k] = t0; - Vj[k] = t1; - } - } - } - - if (!change) { - break; - } - } -} - -template -void CalculationMatrix(int n, int m, std::vector &W, LiteMat &A, LiteMat &V, const T eps) { - for (int i = 0; i < n; i++) { - double sd = 0.; - for (int j = 0; j < m; j++) { - T t = A.ptr(i)[j]; - sd += static_cast(t * t); - } - W[i] = sd; - - for (int k = 0; k < n; k++) { - V.ptr(i)[k] = 0; - } - V.ptr(i)[i] = 1; - } - - Calculation(n, m, W, A, V, eps); - for (int i = 0; i < n; i++) { - double sd = 0; - for (int k = 0; k < m; k++) { - T t = A.ptr(i)[k]; - sd += static_cast(t * t); - } - W[i] = std::sqrt(sd); - } - - for (int i = 0; i < n - 1; i++) { - int mid = i; - for (int j = i + 1; j < n; j++) { - if (W[mid] < W[j]) { - mid = j; - } - } - - if (i != mid) { - std::swap(W[i], W[mid]); - for (int j = 0; j < m; j++) { - std::swap(A.ptr(i)[j], A.ptr(mid)[j]); - } - - for (int j = 0; j < n; j++) { - std::swap(V.ptr(i)[j], V.ptr(mid)[j]); - } - } - } -} - -template -void JacobiSVD(LiteMat &A, LiteMat &_W, LiteMat &V) { - double min_val = FLT_MIN; - T eps = (T)(FLT_EPSILON * 2); - int m = A.width_; - int n = _W.height_; - int urows = m; - std::vector W(n, 0.); - - CalculationMatrix(n, m, W, A, V, eps); - for (int i = 0; i < n; i++) { - _W.ptr(i)[0] = (T)W[i]; - } - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution dis(0, 4294967294); - - for (int i = 0; i < urows; i++) { - double mid = i < n ? W[i] : 0; - for (int ii = 0; ii < 100 && mid <= min_val; ii++) { - const T val0 = (T)(1. / m); - for (int k = 0; k < m; k++) { - unsigned int rng = dis(gen); - T val = (rng & 256) != 0 ? val0 : -val0; - A.ptr(i)[k] = val; - } - - for (int inner = 0; inner < 2; inner++) { - for (int j = 0; j < i; j++) { - mid = 0; - for (int k = 0; k < m; k++) { - mid += A.ptr(i)[k] * A.ptr(j)[k]; - } - T asum = 0; - for (int k = 0; k < m; k++) { - T t = (T)(A.ptr(i)[k] - mid * A.ptr(j)[k]); - A.ptr(i)[k] = t; - asum += std::abs(t); - } - - asum = asum > eps * 100 ? 1 / asum : 0; - for (int k = 0; k < m; k++) { - A.ptr(i)[k] *= asum; - } - } - } - - mid = 0; - for (int k = 0; k < m; k++) { - T t = A.ptr(i)[k]; - mid += static_cast(t * t); - } - mid = std::sqrt(mid); - } - - T s = (T)(mid > min_val ? 1 / mid : 0.); - for (int k = 0; k < m; k++) { - A.ptr(i)[k] *= s; - } - } -} - -template -void SVBkSb(int m, int n, LiteMat w, LiteMat u, LiteMat v, const LiteMat src2, LiteMat dst) { - T eps = DBL_EPSILON * 2; - double thresgold = 0; - int nm = std::min(m, n); - - for (int i = 0; i < n; i++) { - dst.ptr(i)[0] = 0; - } - - for (int i = 0; i < nm; i++) { - for (int j = 0; j < w.width_; j++) { - thresgold += w.ptr(i)[j]; - } - } - thresgold *= eps; - - for (int i = 0; i < nm; i++) { - double wi = w.ptr(i)[0]; - if (static_cast(std::abs(wi)) < thresgold) { - continue; - } - wi = 1 / wi; - double s = 0; - for (int j = 0; j < n; j++) { - s += u.ptr(i)[j] * src2.ptr(j)[0]; - } - - s *= wi; - for (int j = 0; j < n; j++) { - dst.ptr(j)[0] = dst.ptr(j)[0] + s * v.ptr(i)[j]; - } - } -} - -bool GetPerspectiveTransformImpl(const LiteMat &src1, const LiteMat &src2, const LiteMat &dst) { - LDataType type = src1.data_type_; - int m = src1.height_; - int m_ = m; - int n = src1.width_; - - if (m < n) { - return false; - } - - double val_a[64] = {0}; - double val_v[64] = {0}; - double val_w[8] = {0}; - LiteMat a(m_, n, val_a, type); - Transpose(src1, a); - LiteMat w(1, n, val_w, type); - LiteMat v(n, n, val_v, type); - LiteMat u; - - JacobiSVD(a, w, v); - u = a; - - SVBkSb(m_, n, w, u, v, src2, dst); - return true; -} - -bool GetPerspectiveTransform(std::vector src_point, std::vector dst_point, LiteMat &M) { - if (src_point.size() != 4 || dst_point.size() != 4) { - return false; - } - double m[8][8]; - double n[8]; - LiteMat src1(8, 8, m, LDataType(LDataType::DOUBLE)); - LiteMat src2(1, 8, n, LDataType(LDataType::DOUBLE)); - - for (int i = 0; i < 4; ++i) { - m[i][0] = m[i + 4][3] = src_point[i].x; - m[i][1] = m[i + 4][4] = src_point[i].y; - m[i][2] = m[i + 4][5] = 1; - m[i][3] = m[i][4] = m[i][5] = m[i + 4][0] = m[i + 4][1] = m[i + 4][2] = 0; - m[i][6] = -src_point[i].x * dst_point[i].x; - m[i][7] = -src_point[i].y * dst_point[i].x; - m[i + 4][6] = -src_point[i].x * dst_point[i].y; - m[i + 4][7] = -src_point[i].y * dst_point[i].y; - n[i] = dst_point[i].x; - n[i + 4] = dst_point[i].y; - } - - M.Init(3, 3, LDataType(LDataType::DOUBLE)); - RETURN_FALSE_IF_LITEMAT_EMPTY(M); - LiteMat dst(1, 8, M.data_ptr_, LDataType(LDataType::DOUBLE)); - - GetPerspectiveTransformImpl(src1, src2, dst); - M.ptr(2)[2] = 1; - return true; -} - -bool GetAffineTransformImpl(LiteMat &src, LiteMat &dst) { - int m = src.height_; - int n = dst.width_; - for (int i = 0; i < m; i++) { - int k = i; - for (int j = i + 1; j < m; j++) { - if (std::abs(src.ptr(j)[i]) > std::abs(src.ptr(k)[i])) { - k = j; - } - } - - if (std::abs(src.ptr(k)[i]) < DBL_EPSILON * 100) { - dst.Init(1, 6, LDataType(LDataType::DOUBLE)); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - (void)memset(dst.data_ptr_, 0, 6 * sizeof(double)); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - return false; - } - if (k != i) { - for (int j = i; j < m; j++) { - std::swap(src.ptr(i)[j], src.ptr(k)[j]); - } - - if (dst.data_ptr_) { - for (int j = 0; j < n; j++) { - std::swap(dst.ptr(i)[j], dst.ptr(k)[j]); - } - } - } - - const double d = -1 / src.ptr(i)[i]; - for (int j = i + 1; j < m; j++) { - double alpha = src.ptr(j)[i] * d; - for (k = i + 1; k < m; k++) { - src.ptr(j)[k] += alpha * src.ptr(i)[k]; - } - - if (dst.data_ptr_) { - for (k = 0; k < n; k++) { - dst.ptr(j)[k] += alpha * dst.ptr(i)[k]; - } - } - } - } - - if (dst.data_ptr_) { - for (int i = m - 1; i >= 0; i--) { - for (int j = 0; j < n; j++) { - double s = dst.ptr(i)[j]; - for (int k = i + 1; k < m; k++) { - s -= src.ptr(i)[k] * dst.ptr(k)[j]; - } - dst.ptr(i)[j] = s / src.ptr(i)[i]; - } - } - } - - return true; -} - -bool GetAffineTransform(std::vector src_point, std::vector dst_point, LiteMat &M) { - if (src_point.size() != 3 || dst_point.size() != 3) { - return false; - } - double m[6 * 6]; - double n[6]; - LiteMat src1(6, 6, m, LDataType(LDataType::DOUBLE)); - LiteMat src2(1, 6, n, LDataType(LDataType::DOUBLE)); - RETURN_FALSE_IF_LITEMAT_EMPTY(src1); - RETURN_FALSE_IF_LITEMAT_EMPTY(src2); - - for (int i = 0; i < 3; i++) { - int j = i * 12; - int k = i * 12 + 6; - m[j] = m[k + 3] = src_point[i].x; - m[j + 1] = m[k + 4] = src_point[i].y; - m[j + 2] = m[k + 5] = 1; - m[j + 3] = m[j + 4] = m[j + 5] = 0; - m[k] = m[k + 1] = m[k + 2] = 0; - n[i * 2] = dst_point[i].x; - n[i * 2 + 1] = dst_point[i].y; - } - - GetAffineTransformImpl(src1, src2); - M.Init(3, 2, 1, LDataType(LDataType::DOUBLE)); - RETURN_FALSE_IF_LITEMAT_EMPTY(M); - for (int i = 0; i < M.height_; i++) { - for (int j = 0; j < M.width_; j++) { - M.ptr(i)[j] = src2.ptr(i * M.width_ + j)[0]; - } - } - return true; -} - -bool ConvertRgbToBgr(const LiteMat &src, const LDataType &data_type, int w, int h, LiteMat &mat) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (data_type == LDataType::UINT8) { - if (src.IsEmpty()) { - return false; - } - if (mat.IsEmpty()) { - mat.Init(w, h, 3, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - } - if (mat.channel_ != 3) { - return false; - } - if ((src.width_ != w) || (src.height_ != h)) { - return false; - } - unsigned char *ptr = mat; - const unsigned char *data_ptr = src; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - ptr[0] = data_ptr[2]; - ptr[1] = data_ptr[1]; - ptr[2] = data_ptr[0]; - - ptr += 3; - data_ptr += 3; - } - } - } else { - return false; - } - return true; -} - -bool ConvertRgbToGray(const LiteMat &src, LDataType data_type, int w, int h, LiteMat &mat) { - RETURN_FALSE_IF_LITEMAT_EMPTY(src); - if (data_type == LDataType::UINT8) { - if (src.IsEmpty()) { - return false; - } - if (mat.IsEmpty()) { - mat.Init(w, h, 1, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(mat); - } - if (mat.channel_ != 1) { - return false; - } - if ((src.width_ != w) || (src.height_ != h)) { - return false; - } - unsigned char *ptr = mat; - const unsigned char *data_ptr = src; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - *ptr = (data_ptr[2] * kB2Gray + data_ptr[1] * kG2Gray + data_ptr[0] * kR2Gray + kGrayShiftDelta) >> kGrayShift; - ptr++; - data_ptr += 3; - } - } - } else { - return false; - } - return true; -} - -void UpdateOrientationAfineMat(const LiteMat &src, int *rotationDstWidth, int *rotationDstHeight, float (*varM)[2][3], - int img_orientation) { - int srcOrientation = img_orientation; - if (IM_TOOL_EXIF_ORIENTATION_0_DEG_MIRROR == srcOrientation) { - // 0, 2 is the matrix index of varM - (*varM)[0][0] *= -1; - (*varM)[0][2] += static_cast(*rotationDstWidth - 1); - } else if ((IM_TOOL_EXIF_ORIENTATION_180_DEG == srcOrientation) || - (IM_TOOL_EXIF_ORIENTATION_180_DEG_MIRROR == srcOrientation)) { - // 0, 1, 2 is the matrix index of varM - (*varM)[0][0] = -1; - (*varM)[0][1] = 0; - (*varM)[0][2] = static_cast(*rotationDstWidth - 1); - (*varM)[1][0] = 0; - (*varM)[1][1] = -1; - (*varM)[1][2] = static_cast(*rotationDstHeight - 1); - if (IM_TOOL_EXIF_ORIENTATION_180_DEG_MIRROR == srcOrientation) { - /* with (*varM)irror */ - (*varM)[0][0] *= -1; - (*varM)[0][2] -= static_cast(*rotationDstWidth - 1); - } - } else if ((IM_TOOL_EXIF_ORIENTATION_90_DEG_MIRROR == srcOrientation) || - (IM_TOOL_EXIF_ORIENTATION_90_DEG == srcOrientation)) { - /* 90 Deg rotation */ - *rotationDstWidth = src.height_; - *rotationDstHeight = src.width_; - (*varM)[0][0] = 0; - (*varM)[0][1] = -1; - (*varM)[0][2] = static_cast(*rotationDstWidth - 1); - (*varM)[1][0] = 1; - (*varM)[1][1] = 0; - (*varM)[1][2] = 0; - if (IM_TOOL_EXIF_ORIENTATION_90_DEG_MIRROR == srcOrientation) { - /* with Mirror */ - (*varM)[0][1] *= -1; - (*varM)[0][2] -= static_cast(*rotationDstWidth - 1); - } - } else if ((IM_TOOL_EXIF_ORIENTATION_270_DEG_MIRROR == srcOrientation) || - (IM_TOOL_EXIF_ORIENTATION_270_DEG == srcOrientation)) { - /* 270 Deg rotation */ - *rotationDstWidth = src.height_; - *rotationDstHeight = src.width_; - (*varM)[0][0] = 0; - (*varM)[0][1] = 1; - (*varM)[0][2] = 0; - (*varM)[1][0] = -1; - (*varM)[1][1] = 0; - (*varM)[1][2] = static_cast(*rotationDstHeight - 1); - if (IM_TOOL_EXIF_ORIENTATION_270_DEG_MIRROR == srcOrientation) { - /* with Mirror */ - (*varM)[0][1] *= -1; - (*varM)[0][2] += static_cast(*rotationDstWidth - 1); - } - } -} - -void ImageToolsConvertImage(const LiteMat &src, const LiteMat &dst, imageToolsImage_t *imageIn, - imageToolsImage_t *imageOut) { - imageIn->image_buff = src.data_ptr_; - imageIn->h = src.height_; - imageIn->w = src.width_; - imageIn->stride = src.width_; - imageIn->dataType = IM_TOOL_DATA_TYPE_UINT8; - - imageOut->image_buff = dst.data_ptr_; - imageOut->h = dst.height_; - imageOut->w = dst.width_; - imageOut->stride = dst.width_; - imageOut->dataType = IM_TOOL_DATA_TYPE_FLOAT; -} - -int InvAffine2x3(float M[2][3], float invM[][3]) { - float inv_det = M[0][0] * M[1][1] - M[1][0] * M[0][1]; - if (std::fabs(inv_det) <= std::numeric_limits::epsilon()) { - return IM_TOOL_RETURN_STATUS_FAILED; - } - invM[1][1] = M[0][0] / inv_det; - invM[0][1] = -M[0][1] / inv_det; - invM[1][0] = -M[1][0] / inv_det; - invM[0][0] = M[1][1] / inv_det; - invM[0][2] = (M[0][1] * M[1][2] - M[1][1] * M[0][2]) / inv_det; - invM[1][2] = -(M[0][0] * M[1][2] - M[1][0] * M[0][2]) / inv_det; - return IM_TOOL_RETURN_STATUS_SUCCESS; -} - -static float *CalDst(float *dst, float v1, float v2, float v3) { - *dst++ = v1; - *dst++ = v2; - *dst++ = v3; - return dst; -} - -static void ImageWarpAffineHWCFloat(imageToolsImage_t image, imageToolsImage_t warped_image, float invM[2][3]) { - // 3 is r, g, b - warped_image.stride *= 3; - image.stride *= 3; - - auto *warped_image_buff = reinterpret_cast(warped_image.image_buff); - - auto *image_buff = reinterpret_cast(image.image_buff); - for (int y0 = 0; y0 < warped_image.h; y0++) { - // Init pointers to start of rows - float *dst = warped_image_buff + y0 * warped_image.stride; - - for (int x0 = 0; x0 < warped_image.w; x0++) { - // number 0, 1, 2 is the index of MATRIX 'invM' - float fPosx = (static_cast(x0) * invM[0][0]) + (static_cast(y0) * invM[0][1]) + invM[0][2]; - float fPosy = (static_cast(x0) * invM[1][0]) + (static_cast(y0) * invM[1][1]) + invM[1][2]; - int iPosy = static_cast(fPosy + 2) - 2; // for floor like result until -2. - int iPosx = static_cast(fPosx + 2) - 2; // for floor like result until -2. - if ((iPosx < -1) || (iPosx >= image.w) || (iPosy < -1) || (iPosy >= image.h)) { - dst = CalDst(dst, 0.0f, 0.0f, 0.0f); - continue; - } - float fRsiduy = fPosy - static_cast(iPosy); - float fRsidux = fPosx - static_cast(iPosx); - float fOut0 = 0; - float fOut1 = 0; - float fOut2 = 0; - float *fTopeLeft = image_buff + iPosy * image.stride + iPosx * 3; - float fCoeff = 1 - fRsidux - fRsiduy + fRsidux * fRsiduy; - if ((iPosx >= 0) && (iPosy >= 0)) { - // number 0, 1, 2 is the index of MATRIX 'fTopeLeft' - fOut0 += fCoeff * fTopeLeft[0]; - fOut1 += fCoeff * fTopeLeft[1]; - fOut2 += fCoeff * fTopeLeft[2]; - } - float fSum = fCoeff; - fCoeff = fRsiduy - fRsidux * fRsiduy; - if ((iPosx >= 0) && (iPosy < image.h - 1)) { - // Image channel G and B could be accessed by adding number of 1, 2 - fOut0 += fCoeff * fTopeLeft[image.stride]; - fOut1 += fCoeff * fTopeLeft[image.stride + 1]; - fOut2 += fCoeff * fTopeLeft[image.stride + 2]; - } - fSum += fCoeff; - fCoeff = fRsidux - fRsidux * fRsiduy; - if ((iPosx < image.w - 1) && (iPosy >= 0)) { - // Image channel G and B could be accessed by adding number of 1, 2 - fOut0 += fCoeff * fTopeLeft[3]; - fOut1 += fCoeff * fTopeLeft[3 + 1]; - fOut2 += fCoeff * fTopeLeft[3 + 2]; - } - fSum += fCoeff; - if ((iPosx < image.w - 1) && (iPosy < image.h - 1)) { - // Image channel G and B could be accessed by adding number of 1, 2 - fOut0 += (1 - fSum) * fTopeLeft[image.stride + 3]; - fOut1 += (1 - fSum) * fTopeLeft[image.stride + 3 + 1]; - fOut2 += (1 - fSum) * fTopeLeft[image.stride + 3 + 2]; - } - dst = CalDst(dst, fOut0, fOut1, fOut2); - } - } -} - -static void ImageWarpAffineHWCUint8(imageToolsImage_t image, imageToolsImage_t warped_image, float invM[2][3]) { - // 3 is r, g, b - warped_image.stride *= 3; - image.stride *= 3; - auto *warped_image_buff = reinterpret_cast(warped_image.image_buff); - - auto *image_buff = reinterpret_cast(image.image_buff); - for (int y0 = 0; y0 < warped_image.h; y0++) { - // Init pointers to start of rows - float *dst = warped_image_buff + y0 * warped_image.stride; - - for (int x0 = 0; x0 < warped_image.w; x0++) { - float fPosx = (static_cast(x0) * invM[0][0]) + (static_cast(y0) * invM[0][1]) + invM[0][2]; - float fPosy = (static_cast(x0) * invM[1][0]) + (static_cast(y0) * invM[1][1]) + invM[1][2]; - - int iPosy = static_cast(fPosy + 2) - 2; // for floor like result until -2. - int iPosx = static_cast(fPosx + 2) - 2; // for floor like result until -2. - if ((iPosx < -1) || (iPosx >= image.w) || (iPosy < -1) || (iPosy >= image.h)) { - dst = CalDst(dst, 0.0f, 0.0f, 0.0f); - continue; - } - float fRsiduy = fPosy - static_cast(iPosy); - float fRsidux = fPosx - static_cast(iPosx); - float fOut0 = 0; - float fOut1 = 0; - float fOut2 = 0; - uint8_t *uiTopeLeft = image_buff + iPosy * image.stride + iPosx * 3; - float fCoeff = 1 - fRsidux - fRsiduy + fRsidux * fRsiduy; - if ((iPosx >= 0) && (iPosy >= 0)) { - // number 0, 1, 2 is the index of MATRIX round. - fOut0 += fCoeff * static_cast(uiTopeLeft[0]); - fOut1 += fCoeff * static_cast(uiTopeLeft[1]); - fOut2 += fCoeff * static_cast(uiTopeLeft[2]); - } - float fSum = fCoeff; - fCoeff = fRsiduy - fRsidux * fRsiduy; - if ((iPosx >= 0) && (iPosy < image.h - 1)) { - fOut0 += fCoeff * static_cast(uiTopeLeft[image.stride]); - fOut1 += fCoeff * static_cast(uiTopeLeft[image.stride + 1]); - fOut2 += fCoeff * static_cast(uiTopeLeft[image.stride + 2]); - } - fSum += fCoeff; - fCoeff = fRsidux - fRsidux * fRsiduy; - if ((iPosx < image.w - 1) && (iPosy >= 0)) { - fOut0 += fCoeff * static_cast(uiTopeLeft[3]); - fOut1 += fCoeff * static_cast(uiTopeLeft[3 + 1]); - fOut2 += fCoeff * static_cast(uiTopeLeft[3 + 2]); - } - fSum += fCoeff; - if ((iPosx < image.w - 1) && (iPosy < image.h - 1)) { - fOut0 += (1 - fSum) * static_cast(uiTopeLeft[image.stride + 3]); - fOut1 += (1 - fSum) * static_cast(uiTopeLeft[image.stride + 3 + 1]); - fOut2 += (1 - fSum) * static_cast(uiTopeLeft[image.stride + 3 + 2]); - } - dst = CalDst(dst, fOut0, fOut1, fOut2); - } - } -} - -int ImageWarpAffineHWC(imageToolsImage_t image, imageToolsImage_t warped_image, float M[2][3], bool bIsMInv) { - if ((IM_TOOL_DATA_TYPE_FLOAT != warped_image.dataType) || - ((IM_TOOL_DATA_TYPE_FLOAT != image.dataType) && (IM_TOOL_DATA_TYPE_UINT8 != image.dataType))) { - return IM_TOOL_RETURN_STATUS_INVALID_INPUT; - } - float invM[2][3]; - if (bIsMInv) { - for (int iy = 0; iy < 2; iy++) { - for (int ix = 0; ix < 3; ix++) { - invM[iy][ix] = M[iy][ix]; - } - } - } else { - if (InvAffine2x3(M, invM) != IM_TOOL_RETURN_STATUS_SUCCESS) { - return IM_TOOL_RETURN_STATUS_FAILED; - } - } - - if (IM_TOOL_DATA_TYPE_FLOAT == image.dataType) { - ImageWarpAffineHWCFloat(image, warped_image, invM); - } else { - ImageWarpAffineHWCUint8(image, warped_image, invM); - } - return IM_TOOL_RETURN_STATUS_SUCCESS; -} - -bool ResizePreserveARWithFiller(LiteMat &src, LiteMat &dst, int h, int w, float (*ratioShiftWShiftH)[3], - float (*invM)[2][3], int img_orientation) { - if (src.IsEmpty() || src.channel_ != 3 || h <= 0 || w <= 0 || h > 10000 || w > 10000) { - return false; - } - if (ratioShiftWShiftH == nullptr || invM == nullptr) { - return false; - } - if (dst.IsEmpty()) { - dst.Init(w, h, src.channel_, LDataType::FLOAT32); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - - float varM[2][3] = {{1.0, 0, 0}, {0, 1.0, 0}}; - const float divisor = 2.0; - int rotationDstWidth = src.width_; - int rotationDstHeight = src.height_; - if (rotationDstWidth == 0 || rotationDstHeight == 0) { - return false; - } - - if (dst.height_ == 0) { - return false; - } - - if (img_orientation > IM_TOOL_EXIF_ORIENTATION_0_DEG) { - UpdateOrientationAfineMat(src, &rotationDstWidth, &rotationDstHeight, &varM, img_orientation); - } - - /* Resize after orientation fix */ - float srcAR = static_cast(rotationDstWidth) / static_cast(rotationDstHeight); - float dstAR = static_cast(dst.width_) / static_cast(dst.height_); - auto dstActiveWidth = static_cast(dst.width_); - auto dstActiveHeight = static_cast(dst.height_); - float ratio, shiftW, shiftH; - if (srcAR < dstAR) { - ratio = static_cast(dst.height_) / static_cast(rotationDstHeight); - dstActiveWidth = static_cast(rotationDstWidth) * ratio; - } else { - ratio = static_cast(dst.width_) / static_cast(rotationDstWidth); - dstActiveHeight = static_cast(rotationDstHeight) * ratio; - } - shiftW = (static_cast(dst.width_) - dstActiveWidth) / divisor; - shiftH = (static_cast(dst.height_) - dstActiveHeight) / divisor; - for (auto &iy : varM) { - for (float &ix : iy) { - // cppcheck-suppress useStlAlgorithm - ix *= ratio; - } - } - - varM[0][2] += shiftW; - varM[1][2] += shiftH; - /* Resize and shift by affine transform */ - imageToolsImage_t imageIn, imageOut; - ImageToolsConvertImage(src, dst, &imageIn, &imageOut); - if (InvAffine2x3(varM, *invM) != IM_TOOL_RETURN_STATUS_SUCCESS) { - return false; - } - int retVal = ImageWarpAffineHWC(imageIn, imageOut, *invM, true); - if (retVal != 0) { - return false; - } - - // 0, 1, 2 is the index of corresponding elem in ratioShiftWShiftH - (*ratioShiftWShiftH)[0] = ratio; - (*ratioShiftWShiftH)[1] = shiftW; - (*ratioShiftWShiftH)[2] = shiftH; - - return true; -} - -template -void HWC2CHWImpl(const T *src_ptr, T *dst_ptr, int height, int width, int channel) { - int stride = width * height; - for (int i = 0; i != stride; ++i) { - for (int c = 0; c != channel; ++c) { - dst_ptr[c * stride + i] = src_ptr[i * channel + c]; - } - } -} - -bool HWC2CHW(LiteMat &src, LiteMat &dst) { - if (src.IsEmpty()) { - return false; - } - if (dst.IsEmpty() || dst.width_ != src.height_ || dst.height_ != src.channel_ || dst.channel_ != src.width_ || - dst.data_type_ != src.data_type_) { - dst.Init(src.height_, src.channel_, src.width_, src.data_type_); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } - if (src.data_type_ == LDataType::FLOAT32) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::UINT8) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::INT16) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::INT32) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::INT64) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::UINT16) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::UINT32) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::UINT64) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else if (src.data_type_ == LDataType::DOUBLE) { - HWC2CHWImpl(src, dst, src.height_, src.width_, src.channel_); - } else { - return false; - } - return true; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h deleted file mode 100644 index 034bbe752..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h +++ /dev/null @@ -1,657 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef IMAGE_PROCESS_H_ -#define IMAGE_PROCESS_H_ - -#include -#include -#include -#include -#include - -#include "lite_cv/lite_mat.h" - -namespace mindspore { -namespace dataset { -#define CV_PI 3.1415926535897932384626433832795 -#define IM_TOOL_EXIF_ORIENTATION_0_DEG 1 -#define IM_TOOL_EXIF_ORIENTATION_0_DEG_MIRROR 2 -#define IM_TOOL_EXIF_ORIENTATION_180_DEG 3 -#define IM_TOOL_EXIF_ORIENTATION_180_DEG_MIRROR 4 -#define IM_TOOL_EXIF_ORIENTATION_90_DEG_MIRROR 5 -#define IM_TOOL_EXIF_ORIENTATION_90_DEG 6 -#define IM_TOOL_EXIF_ORIENTATION_270_DEG_MIRROR 7 -#define IM_TOOL_EXIF_ORIENTATION_270_DEG 8 -#define NUM_OF_RGB_CHANNELS 9 -#define IM_TOOL_DATA_TYPE_FLOAT (1) -#define IM_TOOL_DATA_TYPE_UINT8 (2) -#define IM_TOOL_RETURN_STATUS_SUCCESS (0) -#define IM_TOOL_RETURN_STATUS_INVALID_INPUT (1) -#define IM_TOOL_RETURN_STATUS_FAILED (2) - -#define INT16_CAST(X) \ - static_cast(::std::min(::std::max(static_cast(X + (X >= 0.f ? 0.5f : -0.5f)), -32768), 32767)) - -enum PaddBorderType { - PADD_BORDER_CONSTANT = 0, /**< Fills the border with constant values. */ - PADD_BORDER_REPLICATE = 1, /**< Fills the border with replicate mode. */ - PADD_BORDER_REFLECT_101 = 4, /**< Fills the border with reflect 101 mode. */ - PADD_BORDER_DEFAULT = PADD_BORDER_REFLECT_101 /**< Default pad mode, use reflect 101 mode. */ -}; - -struct BoxesConfig { - public: - std::vector img_shape; - std::vector num_default; - std::vector feature_size; - float min_scale; - float max_scale; - std::vector> aspect_rations; - std::vector steps; - std::vector prior_scaling; -}; - -/// \brief resizing image by bilinear algorithm, the data type of currently only supports is uint8, -/// the channel of currently supports is 3 and 1. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] dst_w The width of the output image. -/// \param[in] dst_h The length of the output image. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Resize to (256, 256, 3) */ -/// ResizeBilinear(lite_mat_src, lite_mat_dst, 256, 256); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ResizeBilinear(const LiteMat &src, LiteMat &dst, int dst_w, int dst_h); - -/// \brief Init Lite Mat from pixel, the conversion of currently supports is rbgaTorgb and rgbaTobgr. -/// \note The length of the pointer must be the same as that of the multiplication of w and h. -/// \param[in] data Input image data. -/// \param[in] pixel_type The type of pixel (refer to enum LPixelType). -/// - LPixelType.BGR, pixel in BGR type. -/// - LPixelType.RGB, pixel in RGB type. -/// - LPixelType.RGBA, pixel in RGBA type. -/// - LPixelType.RGBA2GRAY, convert image from RGBA to GRAY. -/// - LPixelType.RGBA2BGR, convert image from RGBA to BGR. -/// - LPixelType.RGBA2RGB, convert image from RGBA to RGB. -/// - LPixelType.NV212BGR, convert image from NV21 to BGR. -/// - LPixelType.NV122BGR, convert image from NV12 to BGR. -/// \param[in] data_type The type of data (refer to LDataType class). -/// \param[in] w The width of the output image. -/// \param[in] h The length of the output image. -/// \param[in] m Used to store image data. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_dst; -/// InitFromPixel(p_rgb, LPixelType::RGB, LDataType::UINT8, width, height, lite_mat_dst); -/// -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API InitFromPixel(const unsigned char *data, LPixelType pixel_type, LDataType data_type, int w, int h, - LiteMat &m); - -/// \brief convert the data type, the conversion of currently supports is uint8 to float. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] scale Scale pixel value(default:1.0). -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// InitFromPixel(p_rgb, LPixelType::RGB, LDataType::UINT8, width, height, lite_mat_dst); -/// -/// LiteMat lite_mat_dst; -/// ConvertTo(lite_mat_src, lite_mat_dst); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ConvertTo(const LiteMat &src, LiteMat &dst, double scale = 1.0); - -/// \brief crop image, the channel supports is 3 and 1. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] x The x coordinate value of the starting point of the screenshot. -/// \param[in] y The y coordinate value of the starting point of the screenshot. -/// \param[in] w The width of the screenshot. -/// \param[in] h The height of the screenshot. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Crop to (32, 32, 3) */ -/// Crop(lite_mat_src, lite_mat_dst, 0, 0, 32, 32); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Crop(const LiteMat &src, LiteMat &dst, int x, int y, int w, int h); - -/// \brief normalize image, currently the supports data type is float. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] mean Mean of the data set. -/// \param[in] std Norm of the data set. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_src2; -/// ConvertTo(lite_mat_src, lite_mat_src2); -/// LiteMat lite_mat_dst; -/// -/// /* Normalize */ -/// std::vector means = {0.485, 0.456, 0.406}; -/// std::vector stds = {0.229, 0.224, 0.225}; -/// SubStractMeanNormalize(lite_mat_src2, lite_mat_dst, means, stds); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API SubStractMeanNormalize(const LiteMat &src, LiteMat &dst, const std::vector &mean, - const std::vector &std); - -/// \brief padd image, the channel supports is 3 and 1. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] top The length of top. -/// \param[in] bottom The length of bottom. -/// \param[in] left The length of left. -/// \param[in] right he length of right. -/// \param[in] pad_type The type of pad. -/// - PaddBorderType.PADD_BORDER_CONSTANT, fills the border with constant values. -/// - PaddBorderType.PADD_BORDER_REPLICATE, fills the border with replicate mode. -/// - PaddBorderType.PADD_BORDER_REFLECT_101, fills the border with reflect 101 mode. -/// - PaddBorderType.PADD_BORDER_DEFAULT, default pad mode, use reflect 101 mode. -/// \param[in] fill_b_or_gray B or GRAY. -/// \param[in] fill_g G. -/// \param[in] fill_r R. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::FLOAT32); -/// LiteMat lite_mat_dst; -/// -/// /* Pad image with 4 pixels */ -/// Pad(lite_mat_src, lite_mat_dst, 4, 4, 4, 4, PaddBorderType::PADD_BORDER_CONSTANT); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Pad(const LiteMat &src, LiteMat &dst, int top, int bottom, int left, int right, - PaddBorderType pad_type, uint8_t fill_b_or_gray = 0, uint8_t fill_g = 0, uint8_t fill_r = 0); - -/// \brief Extract image channel by index. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] col The serial number of the channel. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Extract the first channel of image */ -/// ExtractChannel(lite_mat_src, lite_mat_dst, 0); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ExtractChannel(LiteMat &src, LiteMat &dst, int col); - -/// \brief Split image channels. -/// \param[in] src Input image data. -/// \param[in] mv Vector of LiteMat containing all channels. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// std::vector lite_mat_dst; -/// -/// /* Extract all channels of image */ -/// Split(lite_mat_src, lite_mat_dst); -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Split(const LiteMat &src, std::vector &mv); - -/// \brief Create a multi-channel image out of several single-channel arrays. -/// \param[in] mv Single channel data. -/// \param[in] dst Output image data. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// std::vector lite_mat_dst; -/// -/// /* Extract all channels of image */ -/// Split(lite_mat_src, lite_mat_dst); -/// -/// /* Merge all channels to an image */ -/// LiteMat lite_mat_dst2; -/// Merge(lite_mat_dst, lite_mat_dst2); -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Merge(const std::vector &mv, LiteMat &dst); - -/// \brief Apply affine transformation for 1 channel image. -/// \param[in] src Input image data. -/// \param[in] out_img Output image data. -/// \param[in] M[6] Affine transformation matrix. -/// \param[in] dsize The size of the output image. -/// \param[in] borderValue The pixel value is used for filing after the image is captured. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height) */ -/// LiteMat lite_mat_src(width, height, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_src2; -/// ConvertRgbToGray(lite_mat_src, LDataType::UINT8, width, height, lite_mat_src2); -/// -/// /* Define Affine matrix and apply */ -/// LiteMat lite_mat_dst; -/// double M[6] = {1, 0, 0, -/// 0, 1, 0}; -/// Affine(lite_mat_src2, lite_mat_dst, M, {width, height}, UINT8_C1(0)); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, - UINT8_C1 borderValue); - -/// \brief Apply affine transformation for 3 channel image. -/// \param[in] src Input image data. -/// \param[in] out_img Output image data. -/// \param[in] M[6] Affine transformation matrix. -/// \param[in] dsize The size of the output image. -/// \param[in] borderValue The pixel value is used for filing after the image is captured. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Define Affine matrix and apply */ -/// double M[6] = {1, 0, 20, -/// 0, 1, 20}; -/// Affine(lite_mat_src, lite_mat_dst, M, {image.cols, image.rows}, UINT8_C3(0, 0, 0)); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Affine(LiteMat &src, LiteMat &out_img, const double M[6], std::vector dsize, - UINT8_C3 borderValue); - -/// \brief Get default anchor boxes for Faster R-CNN, SSD, YOLO etc. -/// \param[in] config Objects of BoxesConfig structure. -std::vector> DATASET_API GetDefaultBoxes(const BoxesConfig &config); - -/// \brief Convert the prediction boxes to the actual boxes of (y, x, h, w). -/// \param[in] boxes Actual size box. -/// \param[in] default_boxes Default box. -/// \param[in] config Objects of BoxesConfig structure. -void DATASET_API ConvertBoxes(std::vector> &boxes, - const std::vector> &default_boxes, const BoxesConfig &config); - -/// \brief Apply Non-Maximum Suppression. -/// \param[in] all_boxes All input boxes. -/// \param[in] all_scores Score after all boxes are executed through the network. -/// \param[in] thres Pre-value of IOU. -/// \param[in] max_boxes Maximum value of output box. -/// \par Example -/// \code -/// /* Apply NMS on bboxes */ -/// std::vector> all_boxes = {{1, 1, 2, 2}, {3, 3, 4, 4}, {5, 5, 6, 6}, {5, 5, 6, 6}}; -/// std::vector all_scores = {0.6, 0.5, 0.4, 0.9}; -/// std::vector keep = ApplyNms(all_boxes, all_scores, 0.5, 10); -/// \endcode -/// \return Remaining bounding boxes. -std::vector DATASET_API ApplyNms(const std::vector> &all_boxes, std::vector &all_scores, - float thres, int max_boxes); - -/// \brief affine image by linear. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] M Transformation matrix -/// \param[in] dst_w The width of the output image. -/// \param[in] dst_h The height of the output image. -/// \param[in] borderType Edge processing type. -/// - PaddBorderType.PADD_BORDER_CONSTANT, fills the border with constant values. -/// - PaddBorderType.PADD_BORDER_REPLICATE, fills the border with replicate mode. -/// - PaddBorderType.PADD_BORDER_REFLECT_101, fills the border with reflect 101 mode. -/// - PaddBorderType.PADD_BORDER_DEFAULT, default pad mode, use reflect 101 mode. -/// \param[in] borderValue Boundary fill value. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Define Affine matrix and apply */ -/// double M[6] = {1, 0, 20, -/// 0, 1, 20}; -/// LiteMat Matrix(3, 2, M, LDataType::DOUBLE); -/// std::vector border_value = {0, 0, 0}; -/// WarpAffineBilinear(lite_mat_src, lite_mat_dst, Matrix, width, height, -/// PaddBorderType::PADD_BORDER_CONSTANT, border_value); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API WarpAffineBilinear(const LiteMat &src, LiteMat &dst, const LiteMat &M, int dst_w, int dst_h, - PaddBorderType borderType, std::vector &borderValue); - -/// \brief affine image by linear. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] M Transformation matrix -/// \param[in] dst_w The width of the output image. -/// \param[in] dst_h The height of the output image. -/// \param[in] borderType Edge processing type. -/// - PaddBorderType.PADD_BORDER_CONSTANT, fills the border with constant values. -/// - PaddBorderType.PADD_BORDER_REPLICATE, fills the border with replicate mode. -/// - PaddBorderType.PADD_BORDER_REFLECT_101, fills the border with reflect 101 mode. -/// - PaddBorderType.PADD_BORDER_DEFAULT, default pad mode, use reflect 101 mode. -/// \param[in] borderValue Boundary fill value. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Get Perspective matrix and apply */ -/// std::vector src = {Point(165, 270), Point(835, 270), Point(360, 125), Point(615, 125)}; -/// std::vector dst = {Point(165, 270), Point(835, 270), Point(100, 100), Point(500, 30)}; -/// LiteMat M; -/// GetPerspectiveTransform(src, dst, M); -/// std::vector border_value = {0, 0, 0}; -/// WarpPerspectiveBilinear(lite_mat_src, lite_mat_dst, M, width, height, -/// PaddBorderType::PADD_BORDER_CONSTANT, border_value); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API WarpPerspectiveBilinear(const LiteMat &src, LiteMat &dst, const LiteMat &M, int dst_w, int dst_h, - PaddBorderType borderType, std::vector &borderValue); - -/// \brief Matrix rotation. -/// \param[in] x The value of the x-axis of the coordinate rotation point. -/// \param[in] y The value of the y-axis of the coordinate rotation point. -/// \param[in] angle Rotation angle. -/// \param[in] scale Scaling ratio. -/// \param[in] M Output transformation matrix. -/// \par Example -/// \code -/// /* Get Rotation matrix */ -/// double angle = 60.0; -/// double scale = 0.5; -/// LiteMat M; -/// GetRotationMatrix2D(1.0f, 2.0f, angle, scale, M); -/// std::cout << M.width_ << " " << M.height_ << " " << M.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API GetRotationMatrix2D(float x, float y, double angle, double scale, LiteMat &M); - -/// \brief Perspective transformation. -/// \param[in] src_point Input coordinate point. -/// \param[in] dst_point Output coordinate point. -/// \param[in] M Output matrix. -/// \par Example -/// \code -/// /* Get Perspective matrix */ -/// std::vector src = {Point(165, 270), Point(835, 270), Point(360, 125), Point(615, 125)}; -/// std::vector dst = {Point(165, 270), Point(835, 270), Point(100, 100), Point(500, 30)}; -/// LiteMat M; -/// GetPerspectiveTransform(src, dst, M); -/// std::cout << M.width_ << " " << M.height_ << " " << M.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API GetPerspectiveTransform(std::vector src_point, std::vector dst_point, LiteMat &M); - -/// \brief Affine transformation. -/// \param[in] src_point Input coordinate point. -/// \param[in] dst_point Output coordinate point. -/// \param[in] M Output matrix. -/// \par Example -/// \code -/// /* Get Affine matrix */ -/// std::vector src = {Point(50, 50), Point(200, 50), Point(50, 200)}; -/// std::vector dst = {Point(40, 40), Point(100, 40), Point(50, 90)}; -/// LiteMat M; -/// GetAffineTransform(src, dst, M); -/// std::cout << M.width_ << " " << M.height_ << " " << M.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API GetAffineTransform(std::vector src_point, std::vector dst_point, LiteMat &M); - -/// \brief Matrix transpose. -/// \param[in] src Input matrix. -/// \param[in] dst Output matrix. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_src2; -/// ConvertTo(lite_mat_src, lite_mat_src2); -/// LiteMat lite_mat_dst; -/// -/// /* Transpose image */ -/// Transpose(lite_mat_src2, lite_mat_dst); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Transpose(const LiteMat &src, LiteMat &dst); - -/// \brief Filter the image by a Gaussian kernel -/// \param[in] src LiteMat image to be processed. Only LiteMat of type UINT8 is supported now. -/// \param[in] dst LiteMat image after processing. -/// \param[in] ksize The size of Gaussian kernel. It should be a vector of size 2 as {kernel_x, kernel_y}, both value of -/// which should be positive and odd. -/// \param[in] sigmaX The Gaussian kernel standard deviation of width. It should be a positive value. -/// \param[in] sigmaY The Gaussian kernel standard deviation of height (default=0.f). It should be a positive value, -/// or will use the value of sigmaX. -/// \param[in] pad_type The padding type used while filtering (default=PaddBorderType::PADD_BORDER_DEFAULT). -/// - PaddBorderType.PADD_BORDER_CONSTANT, fills the border with constant values. -/// - PaddBorderType.PADD_BORDER_REPLICATE, fills the border with replicate mode. -/// - PaddBorderType.PADD_BORDER_REFLECT_101, fills the border with reflect 101 mode. -/// - PaddBorderType.PADD_BORDER_DEFAULT, default pad mode, use reflect 101 mode. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// /* Blur image */ -/// GaussianBlur(lite_mat_src, lite_mat_dst, {3, 5}, 3, 3); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector &ksize, double sigmaX, - double sigmaY = 0.f, PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); - -/// \brief Detect edges in an image -/// \param[in] src LiteMat image to be processed. Only single channel LiteMat of type UINT8 is supported now. -/// \param[in] dst LiteMat image after processing. -/// \param[in] low_thresh The lower bound of the edge. Pixel with value below it will not be considered as a boundary. -/// It should be a nonnegative value. -//// \param[in] high_thresh The higher bound of the edge. Pixel with value over it will -/// be absolutely considered as a boundary. It should be a nonnegative value and no less than low_thresh. -/// \param[in] ksize The size of Sobel kernel (default=3). It can only be 3, 5 or 7. -/// \param[in] L2gradient Whether to use L2 distance while calculating gradient (default=false). -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// InitFromPixel(p_rgb, LPixelType::RGB, LDataType::UINT8, width, height, lite_mat_src); -/// LiteMat lite_mat_src2; -/// ConvertRgbToGray(lite_mat_src, LDataType::UINT8, image.cols, image.rows, lite_mat_src2); -/// -/// LiteMat lite_mat_dst; -/// Canny(lite_mat_src2, lite_mat_dst, 200, 300, 5); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize = 3, - bool L2gradient = false); - -/// \brief Apply a 2D convolution over the image. -/// \param[in] src LiteMat image to be processed. Only LiteMat of type UINT8 and FLOAT32 is supported now. -/// \param[in] kernel LiteMat 2D convolution kernel. Only LiteMat of type FLOAT32 is supported now. -/// \param[in] dst LiteMat image after processing. -/// \param[in] dst_type Output data type of dst. -/// \param[in] pad_type The padding type used while filtering (default=PaddBorderType::PADD_BORDER_DEFAULT). -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src(width, height, channel, (void *)p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// LiteMat kernel; -/// kernel.Init(3, 3, 1, LDataType::FLOAT32); -/// float *kernel_ptr = kernel; -/// for (int i = 0; i < 9; i++) { -/// kernel_ptr[i] = i % 2; -/// } -/// Conv2D(lite_mat_src, kernel, lite_mat_dst, LDataType::UINT8); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Conv2D(const LiteMat &src, const LiteMat &kernel, LiteMat &dst, LDataType dst_type, - PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); - -/// \brief Applies a separable linear convolution over the image -/// \param[in] src LiteMat image to be processed. Only LiteMat of type UINT8 and FLOAT32 is supported now. -/// \param[in] kx LiteMat 1D convolution kernel. Only LiteMat of type FLOAT32 is supported now. -/// \param[in] ky LiteMat 1D convolution kernel. Only LiteMat of type FLOAT32 is supported now. -/// \param[in] dst LiteMat image after processing. -/// \param[in] dst_type Output data type of dst. -/// \param[in] pad_type The padding type used while filtering (default=PaddBorderType::PADD_BORDER_DEFAULT). -bool DATASET_API ConvRowCol(const LiteMat &src, const LiteMat &kx, const LiteMat &ky, LiteMat &dst, LDataType dst_type, - PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); - -/// \brief Filter the image by a Sobel kernel -/// \param[in] src LiteMat image to be processed. Only LiteMat of type UINT8 is supported now. -/// \param[in] dst LiteMat image after processing. -/// \param[in] flag_x Order of the derivative x. It should be a nonnegative value and can not be equal to 0 at the same -/// time with flag_y. -/// \param[in] flag_y Order of the derivative y. It should be a nonnegative value and can not be equal -/// to 0 at the same time with flag_x. -/// \param[in] ksize The size of Sobel kernel (default=3). It can only be 1, 3, 5 or 7. -/// \param[in] scale The scale factor for the computed derivative values (default=1.0). -/// \param[in] pad_type The padding type used while filtering (default=PaddBorderType::PADD_BORDER_DEFAULT). -/// - PaddBorderType.PADD_BORDER_CONSTANT, fills the border with constant values. -/// - PaddBorderType.PADD_BORDER_REPLICATE, fills the border with replicate mode. -/// - PaddBorderType.PADD_BORDER_REFLECT_101, fills the border with reflect 101 mode. -/// - PaddBorderType.PADD_BORDER_DEFAULT, default pad mode, use reflect 101 mode. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// InitFromPixel(p_rgb, LPixelType::RGB, LDataType::UINT8, width, height, lite_mat_src); -/// LiteMat lite_mat_src2; -/// ConvertRgbToGray(lite_mat_src, LDataType::UINT8, image.cols, image.rows, lite_mat_src2); -/// -/// LiteMat lite_mat_dst; -/// Sobel(lite_mat_src2, lite_mat_dst, 1, 0, 3, 1, PaddBorderType::PADD_BORDER_REPLICATE); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Sobel(const LiteMat &src, LiteMat &dst, int flag_x, int flag_y, int ksize = 3, double scale = 1.0, - PaddBorderType pad_type = PaddBorderType::PADD_BORDER_DEFAULT); - -/// \brief Convert RGB image or color image to BGR image. -/// \param[in] src Input image data. -/// \param[in] data_type The type of data (refer to LDataType class). -/// \param[in] w The width of output image. -/// \param[in] h The height of output image. -/// \param[in] mat Output image data. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(width, height, channel, p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// ConvertRgbToBgr(lite_mat_src, LDataType::UINT8, width, height, lite_mat_dst); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ConvertRgbToBgr(const LiteMat &src, const LDataType &data_type, int w, int h, LiteMat &mat); - -/// \brief Convert RGB image or color image to grayscale image. -/// \param[in] src Input image data. -/// \param[in] data_type The type of data (refer to LDataType class). -/// \param[in] w The width of output image. -/// \param[in] h The height of output image. -/// \param[in] mat Output image data. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(width, height, channel, p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// ConvertRgbToGray(lite_mat_src, LDataType::UINT8, width, height, lite_mat_dst); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ConvertRgbToGray(const LiteMat &src, LDataType data_type, int w, int h, LiteMat &mat); - -/// \brief Resize preserve AR with filler. -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \param[in] h The height of output image. -/// \param[in] w The width of output image. -/// \param[in] ratioShiftWShiftH Array that records the ratio, width shift, and height shift. -/// \param[in] invM Fixed direction array. -/// \param[in] img_orientation Way of export direction. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(width, height, channel, p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// float ratioShiftWShiftH[3] = {0}; -/// float invM[2][3] = {{0, 0, 0}, {0, 0, 0}}; -/// int h = 1000; -/// int w = 1000; -/// ResizePreserveARWithFiller(lite_mat_src, lite_mat_dst, h, w, &ratioShiftWShiftH, &invM, 0); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API ResizePreserveARWithFiller(LiteMat &src, LiteMat &dst, int h, int w, float (*ratioShiftWShiftH)[3], - float (*invM)[2][3], int img_orientation); - -/// \brief Transpose the input image; shape (H, W, C) to shape (C, H, W). -/// \param[in] src Input image data. -/// \param[in] dst Output image data. -/// \par Example -/// \code -/// /* Assume p_rgb is a pointer that points to an image with shape (width, height, channel) */ -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(width, height, channel, p_rgb, LDataType::UINT8); -/// LiteMat lite_mat_dst; -/// -/// HWC2CHW(lite_mat_src, lite_mat_dst); -/// std::cout << lite_mat_dst.width_ << " " << lite_mat_dst.height_ << " " << lite_mat_dst.channel_ << std::endl; -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API HWC2CHW(LiteMat &src, LiteMat &dst); -} // namespace dataset -} // namespace mindspore -#endif // IMAGE_PROCESS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.cc deleted file mode 100644 index afb120191..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.cc +++ /dev/null @@ -1,749 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h" - -#include -#include -#include - -#ifdef ENABLE_NEON -#include -#endif - -namespace mindspore { -namespace dataset { -LiteMat::LiteMat() { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - size_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - setSteps(0, 0, 0); - release_flag_ = false; -} - -LiteMat::LiteMat(int width, LDataType data_type) { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); - release_flag_ = false; - Init(width, data_type); -} - -LiteMat::LiteMat(int width, int height, LDataType data_type) { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); - release_flag_ = false; - Init(width, height, data_type); -} - -LiteMat::LiteMat(int width, int height, void *p_data, LDataType data_type) { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); - release_flag_ = false; - Init(width, height, p_data, data_type); -} - -LiteMat::LiteMat(int width, int height, int channel, LDataType data_type) { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); - release_flag_ = false; - Init(width, height, channel, data_type); -} - -LiteMat::LiteMat(int width, int height, int channel, void *p_data, LDataType data_type) { - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - dims_ = 0; - data_type_ = LDataType::UINT8; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); - release_flag_ = false; - Init(width, height, channel, p_data, data_type); -} - -LiteMat::~LiteMat() { Release(); } - -int LiteMat::addRef(int *p, int value) { - int v = *p; - *p += value; - return v; -} - -LiteMat::LiteMat(const LiteMat &m) { - data_ptr_ = m.data_ptr_; - elem_size_ = m.elem_size_; - width_ = m.width_; - height_ = m.height_; - channel_ = m.channel_; - c_step_ = m.c_step_; - dims_ = m.dims_; - data_type_ = m.data_type_; - ref_count_ = m.ref_count_; - size_ = m.size_; - release_flag_ = m.release_flag_; - setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); - if (ref_count_) { - addRef(ref_count_, 1); - } -} - -void LiteMat::setSteps(size_t c0, size_t c1, size_t c2) { - steps_[0] = c0; - steps_[1] = c1; - steps_[2] = c2; -} - -LiteMat &LiteMat::operator=(const LiteMat &m) { - if (this == &m) { - return *this; - } - - if (m.ref_count_) { - addRef(m.ref_count_, 1); - } - - Release(); - data_ptr_ = m.data_ptr_; - elem_size_ = m.elem_size_; - width_ = m.width_; - height_ = m.height_; - channel_ = m.channel_; - c_step_ = m.c_step_; - dims_ = m.dims_; - data_type_ = m.data_type_; - ref_count_ = m.ref_count_; - setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); - size_ = m.size_; - release_flag_ = m.release_flag_; - return *this; -} - -void LiteMat::Init(int width, LDataType data_type) { - Release(); - data_type_ = data_type; - InitElemSize(data_type); - width_ = width; - dims_ = 1; - height_ = 1; - channel_ = 1; - if (!CheckLiteMat()) { - Release(); - return; - } - c_step_ = width; - size_ = c_step_ * elem_size_; - data_ptr_ = AlignMalloc(size_); - ref_count_ = new int[1]; - *ref_count_ = 1; - steps_[0] = elem_size_; -} - -void LiteMat::Init(int width, int height, LDataType data_type) { - Release(); - data_type_ = data_type; - InitElemSize(data_type); - width_ = width; - height_ = height; - dims_ = 2; - channel_ = 1; - if (!CheckLiteMat()) { - Release(); - return; - } - c_step_ = width_ * height_; - size_ = c_step_ * elem_size_; - data_ptr_ = AlignMalloc(size_); - ref_count_ = new int[1]; - *ref_count_ = 1; - steps_[1] = elem_size_; - steps_[0] = width_ * steps_[1]; -} - -void LiteMat::Init(int width, int height, void *p_data, LDataType data_type) { - data_type_ = data_type; - InitElemSize(data_type); - width_ = width; - height_ = height; - dims_ = 2; - channel_ = 1; - if (!CheckLiteMat()) { - Release(); - return; - } - c_step_ = height_ * width_; - size_ = c_step_ * channel_ * elem_size_; - data_ptr_ = p_data; - ref_count_ = nullptr; - steps_[1] = elem_size_; - steps_[0] = width_ * steps_[1]; -} - -void LiteMat::Init(int width, int height, int channel, const LDataType &data_type, bool align_memory) { - Release(); - data_type_ = data_type; - InitElemSize(data_type); - width_ = width; - height_ = height; - dims_ = 3; - channel_ = channel; - if (!CheckLiteMat()) { - Release(); - return; - } - if (align_memory) { - c_step_ = ((height_ * width_ * elem_size_ + kAlign - 1) & (-kAlign)) / elem_size_; - } else { - c_step_ = height_ * width_; - } - size_ = c_step_ * channel_ * elem_size_; - data_ptr_ = AlignMalloc(size_); - ref_count_ = new int[1]; - *ref_count_ = 1; - - steps_[2] = elem_size_; - steps_[1] = channel * steps_[2]; - steps_[0] = width_ * steps_[1]; -} - -void LiteMat::Init(int width, int height, int channel, void *p_data, LDataType data_type) { - data_type_ = data_type; - InitElemSize(data_type); - width_ = width; - height_ = height; - dims_ = 3; - channel_ = channel; - if (!CheckLiteMat()) { - Release(); - return; - } - c_step_ = height_ * width_; - size_ = c_step_ * channel_ * elem_size_; - data_ptr_ = p_data; - ref_count_ = nullptr; - steps_[2] = elem_size_; - steps_[1] = channel * steps_[2]; - steps_[0] = width_ * steps_[1]; -} - -bool LiteMat::IsEmpty() const { return data_ptr_ == nullptr || c_step_ * channel_ == 0; } - -void LiteMat::Release() { - if (ref_count_ && (addRef(ref_count_, -1) == 1)) { - if (data_ptr_) { - AlignFree(data_ptr_); - } - delete[] ref_count_; - } - data_ptr_ = nullptr; - elem_size_ = 0; - width_ = 0; - height_ = 0; - channel_ = 0; - c_step_ = 0; - ref_count_ = nullptr; - size_ = 0; - setSteps(0, 0, 0); -} - -void *LiteMat::AlignMalloc(unsigned int size) { - unsigned int length = sizeof(void *) + kAlign - 1; - if (size > std::numeric_limits::max() - length) { - return nullptr; - } - void *p_raw = reinterpret_cast(malloc(size + length)); - if (p_raw) { - release_flag_ = true; - void **p_algin = reinterpret_cast((reinterpret_cast(p_raw) + length) & ~(kAlign - 1)); - p_algin[-1] = p_raw; - return p_algin; - } - return nullptr; -} - -void LiteMat::AlignFree(void *ptr) { - if (release_flag_) { - (void)free(reinterpret_cast(ptr)[-1]); - ptr = nullptr; - release_flag_ = false; - } -} - -inline void LiteMat::InitElemSize(LDataType data_type) { elem_size_ = data_type.SizeInBytes(); } - -bool LiteMat::CheckLiteMat() const { - if (width_ <= 0 || height_ <= 0 || channel_ <= 0 || elem_size_ <= 0) { - return false; - } - if (height_ != 1 && height_ > std::numeric_limits::max() / width_) { - return false; - } - int area = height_ * width_; - if (channel_ != 1 && channel_ > std::numeric_limits::max() / area) { - return false; - } - int size = area * channel_; - if (elem_size_ > std::numeric_limits::max() / size) { - return false; - } - return true; -} - -bool LiteMat::GetROI(int x, int y, int w, int h, LiteMat &m) { - if (x < 0 || y < 0 || x > width_ - w || h > height_ - y || w <= 0 || h <= 0) { - return false; - } - if (!m.IsEmpty()) { - m.Release(); - } - - if (ref_count_) { - addRef(ref_count_, 1); - } - - m.height_ = h; - m.width_ = w; - m.dims_ = dims_; - m.elem_size_ = elem_size_; - m.data_ptr_ = reinterpret_cast(data_ptr_) + y * steps_[0] + x * elem_size_ * channel_; - m.channel_ = channel_; - m.c_step_ = c_step_; - m.data_type_ = data_type_; - m.ref_count_ = ref_count_; - m.setSteps(steps_[0], steps_[1], steps_[2]); - return true; -} - -template -inline void SubtractImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { - for (int64_t i = 0; i < total_size; i++) { - dst[i] = src0[i] - src1[i]; - } -} - -template <> -inline void SubtractImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { - int64_t x = 0; -#ifdef ENABLE_NEON - const int64_t step = 32; - for (; x <= total_size - step; x += step) { - uint8x16_t v_src00 = vld1q_u8(src0 + x); - uint8x16_t v_src01 = vld1q_u8(src0 + x + 16); - uint8x16_t v_src10 = vld1q_u8(src1 + x); - uint8x16_t v_src11 = vld1q_u8(src1 + x + 16); - uint8x16_t v_dst; - - v_dst = vqsubq_u8(v_src00, v_src10); - vst1q_u8(dst + x, v_dst); - - v_dst = vqsubq_u8(v_src01, v_src11); - vst1q_u8(dst + x + 16, v_dst); - } -#endif - for (; x < total_size; x++) { - int32_t val = static_cast(src0[x]) - src1[x]; - dst[x] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void SubtractImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { - for (int64_t i = 0; i < total_size; i++) { - int32_t val = static_cast(src0[i]) - src1[i]; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void SubtractImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { - for (int64_t i = 0; i < total_size; i++) { - int64_t val = static_cast(src0[i]) - src1[i]; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -inline bool CheckSubstract(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (dst == nullptr) { - return false; - } - - if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { - return false; - } - - return src_a.data_type_ == src_b.data_type_; -} - -bool Subtract(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (!CheckSubstract(src_a, src_b, dst)) { - return false; - } - - if (dst->IsEmpty()) { - dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } - if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { - return false; - } - if (src_a.data_type_ != dst->data_type_) { - return false; - } - - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::BOOL) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT8) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - - return true; -} - -#ifdef ENABLE_NEON -inline float32x4_t reciprocal_simd(float32x4_t val) { - // get an initial estimate of 1/val - float32x4_t reciprocal = vrecpeq_f32(val); - - // use Newton-Raphson steps to refine the estimate - reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); - reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); - return reciprocal; -} - -inline float32x4_t round_simd(const float32x4_t &v) { - const int32x4_t signMask = vdupq_n_s32(1U << 31); - const int32x4_t half = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); - float32x4_t v_addition = vreinterpretq_f32_s32(vorrq_s32(half, vandq_s32(signMask, vreinterpretq_s32_f32(v)))); - return vaddq_f32(v, v_addition); -} -#endif - -template -inline void DivideImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { - for (int64_t i = 0; i < total_size; i++) { - dst[i] = src1[i] ? src0[i] / src1[i] : 0; - } -} - -template <> -inline void DivideImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { - int64_t x = 0; -#ifdef ENABLE_NEON - const int64_t step = 16; - for (; x <= total_size - step; x += step) { - __builtin_prefetch(reinterpret_cast(src0 + x) + 32 * 10); - __builtin_prefetch(reinterpret_cast(src1 + x) + 32 * 10); - - uint8x16_t v_a = vld1q_u8(src0 + x); - uint8x16_t v_b = vld1q_u8(src1 + x); - uint8x16_t v_mask = vtstq_u8(v_b, v_b); - - uint16x8_t va_l_16x8 = vmovl_u8(vget_low_u8(v_a)); - uint16x8_t va_h_16x8 = vmovl_u8(vget_high_u8(v_a)); - uint16x8_t vb_l_16x8 = vmovl_u8(vget_low_u8(v_b)); - uint16x8_t vb_h_16x8 = vmovl_u8(vget_high_u8(v_b)); - - float32x4_t va_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_l_16x8))); - float32x4_t va_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_l_16x8))); - float32x4_t va_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_h_16x8))); - float32x4_t va_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_h_16x8))); - float32x4_t vb_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_l_16x8))); - float32x4_t vb_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_l_16x8))); - float32x4_t vb_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_h_16x8))); - float32x4_t vb_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_h_16x8))); - - float32x4_t vb_ll_re_f32x4 = reciprocal_simd(vb_ll_f32x4); - float32x4_t vb_lh_re_f32x4 = reciprocal_simd(vb_lh_f32x4); - float32x4_t vb_hl_re_f32x4 = reciprocal_simd(vb_hl_f32x4); - float32x4_t vb_hh_re_f32x4 = reciprocal_simd(vb_hh_f32x4); - - float32x4_t dst_ll_f32x4 = round_simd(vmulq_f32(va_ll_f32x4, vb_ll_re_f32x4)); - float32x4_t dst_lh_f32x4 = round_simd(vmulq_f32(va_lh_f32x4, vb_lh_re_f32x4)); - float32x4_t dst_hl_f32x4 = round_simd(vmulq_f32(va_hl_f32x4, vb_hl_re_f32x4)); - float32x4_t dst_hh_f32x4 = round_simd(vmulq_f32(va_hh_f32x4, vb_hh_re_f32x4)); - - uint32x4_t dst_ll_32x4 = vcvtq_u32_f32(dst_ll_f32x4); - uint32x4_t dst_lh_32x4 = vcvtq_u32_f32(dst_lh_f32x4); - uint32x4_t dst_hl_32x4 = vcvtq_u32_f32(dst_hl_f32x4); - uint32x4_t dst_hh_32x4 = vcvtq_u32_f32(dst_hh_f32x4); - - uint16x4_t dst_ll_16x4 = vqmovn_u32(dst_ll_32x4); - uint16x4_t dst_lh_16x4 = vqmovn_u32(dst_lh_32x4); - uint16x4_t dst_hl_16x4 = vqmovn_u32(dst_hl_32x4); - uint16x4_t dst_hh_16x4 = vqmovn_u32(dst_hh_32x4); - - uint16x8_t dst_l_16x8 = vcombine_u16(dst_ll_16x4, dst_lh_16x4); - uint16x8_t dst_h_16x8 = vcombine_u16(dst_hl_16x4, dst_hh_16x4); - - uint8x8_t dst_l_8x8 = vqmovn_u16(dst_l_16x8); - uint8x8_t dst_h_8x8 = vqmovn_u16(dst_h_16x8); - uint8x16_t dst_8x16 = vcombine_u8(dst_l_8x8, dst_h_8x8); - - dst_8x16 = vandq_u8(dst_8x16, v_mask); - vst1q_u8(dst + x, dst_8x16); - } -#endif - for (; x < total_size; x++) { - int32_t val = src1[x] != 0 ? static_cast(std::round(src0[x] / src1[x])) : 0; - dst[x] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void DivideImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { - for (size_t i = 0; i < total_size; i++) { - int32_t val = src1[i] != 0 ? static_cast(std::round(src0[i] / src1[i])) : 0; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void DivideImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { - for (size_t i = 0; i < total_size; i++) { - int64_t val = src1[i] != 0 ? static_cast(std::round(src0[i] / src1[i])) : 0; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -inline bool CheckDivide(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (dst == nullptr) { - return false; - } - - if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { - return false; - } - - return src_a.data_type_ == src_b.data_type_; -} - -bool Divide(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (!CheckDivide(src_a, src_b, dst)) { - return false; - } - - if (dst->IsEmpty()) { - dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { - return false; - } else if (src_a.data_type_ != dst->data_type_) { - return false; - } - - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::INT8) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - return true; -} - -template -inline void MultiplyImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { - for (int64_t i = 0; i < total_size; i++) { - dst[i] = src0[i] * src1[i]; - } -} - -template <> -inline void MultiplyImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { - int64_t x = 0; -#ifdef ENABLE_NEON - const int64_t step = 32; - for (; x <= total_size - step; x += step) { - uint8x16_t v_src00 = vld1q_u8(src0 + x); - uint8x16_t v_src01 = vld1q_u8(src0 + x + 16); - uint8x16_t v_src10 = vld1q_u8(src1 + x); - uint8x16_t v_src11 = vld1q_u8(src1 + x + 16); - uint16x8_t v_dst_l, v_dst_h; - - v_dst_l = vmull_u8(vget_low_u8(v_src00), vget_low_u8(v_src10)); - v_dst_h = vmull_u8(vget_high_u8(v_src00), vget_high_u8(v_src10)); - vst1q_u8(dst + x, vcombine_u8(vqmovn_u16(v_dst_l), vqmovn_u16(v_dst_h))); - - v_dst_l = vmull_u8(vget_low_u8(v_src01), vget_low_u8(v_src11)); - v_dst_h = vmull_u8(vget_high_u8(v_src01), vget_high_u8(v_src11)); - vst1q_u8(dst + x + 16, vcombine_u8(vqmovn_u16(v_dst_l), vqmovn_u16(v_dst_h))); - } -#endif - for (; x < total_size; x++) { - int32_t val = src0[x] * src1[x]; - dst[x] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void MultiplyImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { - for (size_t i = 0; i < total_size; i++) { - int32_t val = src0[i] * src1[i]; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -template <> -inline void MultiplyImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { - for (size_t i = 0; i < total_size; i++) { - int64_t val = src0[i] * src1[i]; - dst[i] = std::max(std::numeric_limits::min(), - std::min(std::numeric_limits::max(), val)); - } -} - -inline bool CheckMultiply(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (dst == nullptr) { - return false; - } - - if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { - return false; - } - - return src_a.data_type_ == src_b.data_type_; -} - -bool Multiply(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (!CheckMultiply(src_a, src_b, dst)) { - return false; - } - if (dst->IsEmpty()) { - dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { - return false; - } else if (src_a.data_type_ != dst->data_type_) { - return false; - } - - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::INT8) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - return true; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h deleted file mode 100644 index 782d9bfb9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h +++ /dev/null @@ -1,481 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINI_MAT_H_ -#define MINI_MAT_H_ - -#include -#include - -#include "include/api/visible.h" - -namespace mindspore { -namespace dataset { -constexpr int kAlign = 16; -constexpr size_t kMaxDims = 3; - -template -struct Chn1 { - Chn1(T c1) : c1(c1) {} - T c1; -}; - -template -struct Chn2 { - Chn2(T c1, T c2) : c1(c1), c2(c2) {} - T c1; - T c2; -}; - -template -struct Chn3 { - Chn3(T c1, T c2, T c3) : c1(c1), c2(c2), c3(c3) {} - T c1; - T c2; - T c3; -}; - -template -struct Chn4 { - Chn4(T c1, T c2, T c3, T c4) : c1(c1), c2(c2), c3(c3), c4(c4) {} - T c1; - T c2; - T c3; - T c4; -}; - -/// \brief Struct representing the location of pixel. -/// \note Location usually starts from the left top of image. -/// \par Example -/// \code -/// // Define a point p points to the pixel at (X=10,Y=5). -/// Point p = Point(10, 5); -/// \endcode -struct Point { - float x; ///< X location of pixel. - float y; ///< Y location of pixel. - - Point() : x(0), y(0) {} ///< Constructor. - Point(float _x, float _y) : x(_x), y(_y) {} ///< Constructor. -}; - -typedef struct imageToolsImage { - int w; - int h; - int stride; - int dataType; - void *image_buff; -} imageToolsImage_t; - -using BOOL_C1 = Chn1; -using BOOL_C2 = Chn2; -using BOOL_C3 = Chn3; -using BOOL_C4 = Chn4; - -using UINT8_C1 = Chn1; -using UINT8_C2 = Chn2; -using UINT8_C3 = Chn3; -using UINT8_C4 = Chn4; - -using INT8_C1 = Chn1; -using INT8_C2 = Chn2; -using INT8_C3 = Chn3; -using INT8_C4 = Chn4; - -using UINT16_C1 = Chn1; -using UINT16_C2 = Chn2; -using UINT16_C3 = Chn3; -using UINT16_C4 = Chn4; - -using INT16_C1 = Chn1; -using INT16_C2 = Chn2; -using INT16_C3 = Chn3; -using INT16_C4 = Chn4; - -using UINT32_C1 = Chn1; -using UINT32_C2 = Chn2; -using UINT32_C3 = Chn3; -using UINT32_C4 = Chn4; - -using INT32_C1 = Chn1; -using INT32_C2 = Chn2; -using INT32_C3 = Chn3; -using INT32_C4 = Chn4; - -using UINT64_C1 = Chn1; -using UINT64_C2 = Chn2; -using UINT64_C3 = Chn3; -using UINT64_C4 = Chn4; - -using INT64_C1 = Chn1; -using INT64_C2 = Chn2; -using INT64_C3 = Chn3; -using INT64_C4 = Chn4; - -using FLOAT32_C1 = Chn1; -using FLOAT32_C2 = Chn2; -using FLOAT32_C3 = Chn3; -using FLOAT32_C4 = Chn4; - -using FLOAT64_C1 = Chn1; -using FLOAT64_C2 = Chn2; -using FLOAT64_C3 = Chn3; -using FLOAT64_C4 = Chn4; - -enum LPixelType { - BGR = 0, /**< Pixel in BGR type. */ - RGB = 1, /**< Pixel in RGB type. */ - RGBA = 2, /**< Pixel in RGBA type. */ - RGBA2GRAY = 3, /**< Convert image from RGBA to GRAY. */ - RGBA2BGR = 4, /**< Convert image from RGBA to BGR. */ - RGBA2RGB = 5, /**< Convert image from RGBA to RGB. */ - NV212BGR = 6, /**< Convert image from NV21 to BGR. */ - NV122BGR = 7, /**< Convert image from NV12 to BGR. */ -}; - -enum WARP_BORDER_MODE { WARP_BORDER_MODE_CONSTANT }; - -/// \brief Class representing the data type. -/// \note Supported data type list: -/// - LDataType::BOOL -/// - LDataType::INT8 -/// - LDataType::UINT8 -/// - LDataType::INT16 -/// - LDataType::INT32 -/// - LDataType::UINT32 -/// - LDataType::INT64 -/// - LDataType::UINT64 -/// - LDataType::FLOAT16 -/// - LDataType::FLOAT32 -/// - LDataType::FLOAT64 -/// - LDataType::DOUBLE -class DATASET_API LDataType { - public: - enum Type : uint8_t { - UNKNOWN = 0, /**< Unknown data type. */ - BOOL, /**< BOOL data type. */ - INT8, /**< INT8 data type. */ - UINT8, /**< UINT8 data type. */ - INT16, /**< INT16 data type. */ - UINT16, /**< UINT16 data type. */ - INT32, /**< INT32 data type. */ - UINT32, /**< UINT32 data type. */ - INT64, /**< INT64 data type. */ - UINT64, /**< UINT64 data type. */ - FLOAT16, /**< FLOAT16 data type. */ - FLOAT32, /**< FLOAT32 data type. */ - FLOAT64, /**< FLOAT64 data type. */ - DOUBLE, /**< DOUBLE data type. */ - NUM_OF_TYPES /**< number of types. */ - }; - - /// \brief Constructor. - LDataType() : type_(UNKNOWN) {} - - LDataType(Type d) : type_(d) {} - - /// \brief Destructor. - ~LDataType() = default; - - inline Type Value() const { return type_; } - - inline bool operator==(const LDataType &ps) const { return this->type_ == ps.type_; } - - inline bool operator!=(const LDataType &ps) const { return this->type_ != ps.type_; } - - /// \brief Function to return the length of data type. - /// \return Memory length of data type. - uint8_t SizeInBytes() const { - if (type_ < LDataType::NUM_OF_TYPES) { - return SIZE_IN_BYTES[type_]; - } else { - return 0; - } - } - - public: - static inline const uint8_t SIZE_IN_BYTES[] = { - 0, /**< Unknown size. */ - 1, /**< Size of BOOL. */ - 1, /**< Size of INT8. */ - 1, /**< Size of UINT8. */ - 2, /**< Size of INT16. */ - 2, /**< Size of UINT16. */ - 4, /**< Size of INT32. */ - 4, /**< Size of UINT32. */ - 8, /**< Size of INT64. */ - 8, /**< Size of UINT64. */ - 2, /**< Size of FLOAT16. */ - 4, /**< Size of FLOAT32. */ - 8, /**< Size of FLOAT64. */ - 8, /**< Size of DOUBLE. */ - }; - - Type type_; -}; - -/// \brief Basic class storing the image data. -class DATASET_API LiteMat { - public: - /// \brief Constructor. - LiteMat(); - - /// \brief Function to create an LiteMat object. - /// \param[in] width The width of the input object. - /// \param[in] data_type The data type of the input object. - explicit LiteMat(int width, LDataType data_type = LDataType::UINT8); - - /// \brief Function to create an LiteMat object. - /// \param[in] width The width of the input object. - /// \param[in] height The height of the input object. - /// \param[in] data_type The data type of the input object. - LiteMat(int width, int height, LDataType data_type = LDataType::UINT8); - - /// \brief Function to create an LiteMat object. - /// \param[in] width The width of the input object. - /// \param[in] height The height of the input object. - /// \param[in] p_data The pointer data of the input object. - /// \param[in] data_type The data type of the input object. - LiteMat(int width, int height, void *p_data, LDataType data_type = LDataType::UINT8); - - /// \brief Function to create an LiteMat object. - /// \param[in] width The width of the input object. - /// \param[in] height The height of the input object. - /// \param[in] channel The channel of the input object. - /// \param[in] data_type The data type of the input object. - LiteMat(int width, int height, int channel, LDataType data_type = LDataType::UINT8); - - /// \brief Function to create an LiteMat object. - /// \param[in] width The width of the input object. - /// \param[in] height The height of the input object. - /// \param[in] channel The channel of the input object. - /// \param[in] p_data The pointer data of the input object. - /// \param[in] data_type The data type of the input object. - LiteMat(int width, int height, int channel, void *p_data, LDataType data_type = LDataType::UINT8); - - /// \brief Destructor. - ~LiteMat(); - - LiteMat(const LiteMat &m); - - /// \brief Perform Init operation on given LiteMat - /// \param[in] width Set width for given LiteMat. - /// \param[in] data_type Set data type for given LiteMat. - void Init(int width, LDataType data_type = LDataType::UINT8); - - /// \brief Perform Init operation on given LiteMat - /// \param[in] width Set width for given LiteMat. - /// \param[in] height Set height for given LiteMat. - /// \param[in] data_type Set data type for given LiteMat. - void Init(int width, int height, LDataType data_type = LDataType::UINT8); - - /// \brief Perform Init operation on given LiteMat - /// \param[in] width Set width for given LiteMat. - /// \param[in] height Set height for given LiteMat. - /// \param[in] p_data Set pointer data for given LiteMat. - /// \param[in] data_type Set data type for given LiteMat. - void Init(int width, int height, void *p_data, LDataType data_type = LDataType::UINT8); - - /// \brief Perform Init operation on given LiteMat - /// \param[in] width Set width for given LiteMat. - /// \param[in] height Set height for given LiteMat. - /// \param[in] channel Set channel for given LiteMat. - /// \param[in] data_type Set data type for given LiteMat. - /// \param[in] align_memory Whether malloc align memory or not, default is true, - /// which is better for doing acceleration. - void Init(int width, int height, int channel, const LDataType &data_type = LDataType::UINT8, - bool align_memory = true); - - /// \brief Perform Init operation on given LiteMat - /// \param[in] width Set width for given LiteMat. - /// \param[in] height Set height for given LiteMat. - /// \param[in] channel Set channel for given LiteMat. - /// \param[in] p_data Set pointer data for given LiteMat. - /// \param[in] data_type Set data type for given LiteMat. - void Init(int width, int height, int channel, void *p_data, LDataType data_type = LDataType::UINT8); - - bool GetROI(int x, int y, int w, int h, LiteMat &dst); // NOLINT - - bool IsEmpty() const; - - void Release(); - - LiteMat &operator=(const LiteMat &m); - - template - operator T *() { - return reinterpret_cast(data_ptr_); - } - - template - operator const T *() const { - return reinterpret_cast(data_ptr_); - } - - template - inline T *ptr(int w) const { - if (w >= height_) { - return nullptr; - } - if (IsEmpty()) { - return nullptr; - } - return reinterpret_cast(reinterpret_cast(data_ptr_) + steps_[0] * w); - } - - private: - /// \brief Apply for memory alignment - /// \param[in] size The size of the requested memory alignment. - void *AlignMalloc(unsigned int size); - - /// \brief Free memory - /// \param[in] ptr Pointer to free memory. - void AlignFree(void *ptr); - - /// \brief Initialize the element size of different types of data. - /// \param[in] data_type Type of data. - void InitElemSize(LDataType data_type); - - /// \brief Add value of reference count. - /// \param[in] p The point of references count. - /// \param[in] value The value of new added references. - /// \return return reference count. - static int addRef(int *p, int value); - - /// \brief Set the step size of the pixels in the Litemat array. - /// \param[in] c0 The number used to set teh value of step[0]. - /// \param[in] c1 The number used to set teh value of step[1]. - /// \param[in] c2 The number used to set teh value of step[2]. - void setSteps(size_t c0, size_t c1, size_t c2); - - bool CheckLiteMat() const; - - public: - void *data_ptr_ = nullptr; - int elem_size_; - int width_; - int height_; - int channel_; - int c_step_; - int dims_; - size_t size_; - LDataType data_type_; - int *ref_count_; - size_t steps_[kMaxDims]{}; - bool release_flag_; -}; - -/// \brief Given image A and image B and calculate the difference of them (A - B). -/// This is an element by element operation by subtracting corresponding elements of inputs. -/// \param[in] src_a Input image data. -/// \param[in] src_b Input image data. -/// \param[in] dst The difference of input images. -/// \par Example -/// \code -/// std::vector mat1 = {3, 3, 3, 3}; -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(2, 2, 1, mat1.data(), LDataType::UINT8); -/// -/// std::vector mat2 = {2, 2, 2, 2}; -/// LiteMat lite_mat_src2; -/// lite_mat_src2.Init(2, 2, 1, mat2.data(), LDataType::UINT8); -/// -/// /* Calculate the difference of images */ -/// LiteMat diff; -/// Subtract(lite_mat_src, lite_mat_src2, &diff); -/// for (int i = 0; i < diff.height_; i++) { -/// for (int j = 0; j < diff.width_; j++) { -/// std::cout << std::to_string(diff.ptr(i)[j]) << ", "; -/// } -/// std::cout << std::endl; -/// } -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Subtract(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst); - -/// \brief Given image A and image B and calculate the division of them (A / B). -/// This is an element by element operation. -/// \param[in] src_a Input image data. -/// \param[in] src_b Input image data. -/// \param[in] dst The division of input images. -/// \par Example -/// \code -/// std::vector mat1 = {8, 8, 8, 8}; -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(2, 2, 1, mat1.data(), LDataType::UINT8); -/// -/// std::vector mat2 = {2, 2, 2, 2}; -/// LiteMat lite_mat_src2; -/// lite_mat_src2.Init(2, 2, 1, mat2.data(), LDataType::UINT8); -/// -/// /* Calculate the division of images */ -/// LiteMat div; -/// Divide(lite_mat_src, lite_mat_src2, &div); -/// for (int i = 0; i < div.height_; i++) { -/// for (int j = 0; j < div.width_; j++) { -/// std::cout << std::to_string(div.ptr(i)[j]) << ", "; -/// } -/// std::cout << std::endl; -/// } -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Divide(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst); - -/// \brief Given image A and image B and calculate the product of them (A * B). -/// This is an element by element operation by multiplying corresponding elements of inputs. -/// \param[in] src_a Input image data. -/// \param[in] src_b Input image data. -/// \param[in] dst The product of input images. -/// \par Example -/// \code -/// std::vector mat1 = {4, 4, 4, 4}; -/// LiteMat lite_mat_src; -/// lite_mat_src.Init(2, 2, 1, mat1.data(), LDataType::UINT8); -/// -/// std::vector mat2 = {2, 2, 2, 2}; -/// LiteMat lite_mat_src2; -/// lite_mat_src2.Init(2, 2, 1, mat2.data(), LDataType::UINT8); -/// -/// /* Calculate the product of images */ -/// LiteMat mut; -/// Multiply(lite_mat_src, lite_mat_src2, &mut); -/// for (int i = 0; i < mut.height_; i++) { -/// for (int j = 0; j < mut.width_; j++) { -/// std::cout << std::to_string(mut.ptr(i)[j]) << ", "; -/// } -/// std::cout << std::endl; -/// } -/// \endcode -/// \return Return true if transform successfully. -bool DATASET_API Multiply(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst); - -#define RETURN_FALSE_IF_LITEMAT_EMPTY(_m) \ - do { \ - if ((_m).IsEmpty()) { \ - return false; \ - } \ - } while (false) - -#define RETURN_IF_LITEMAT_EMPTY(_m) \ - do { \ - if ((_m).IsEmpty()) { \ - return; \ - } \ - } while (false) - -} // namespace dataset -} // namespace mindspore -#endif // MINI_MAT_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/warp_affine.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_cv/warp_affine.cc deleted file mode 100644 index 95d52e331..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_cv/warp_affine.cc +++ /dev/null @@ -1,578 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include -#include - -#include "lite_cv/lite_mat.h" -#include "lite_cv/image_process.h" - -constexpr int kBits = 5; -constexpr int kBits1 = 15; -constexpr int kTabSz = 1 << kBits; -constexpr int kTabSz2 = kTabSz * kTabSz; -constexpr int kRemapScale = 1 << 15; - -namespace mindspore { -namespace dataset { -static int16_t BWBlock_i[kTabSz2][2][2]; - -static double SrcValue(const double *src, const int &y, const int &x) { return (src + y * 3)[x]; } - -static double &DstValue(double *dst, const int &y, const int &x) { return (dst + y * 3)[x]; } - -static double GetDet3(double *src) { - double a1 = - SrcValue(src, 0, 0) * (SrcValue(src, 1, 1) * SrcValue(src, 2, 2) - SrcValue(src, 1, 2) * SrcValue(src, 2, 1)); - double a2 = - SrcValue(src, 0, 1) * (SrcValue(src, 1, 0) * SrcValue(src, 2, 2) - SrcValue(src, 1, 2) * SrcValue(src, 2, 0)); - double a3 = - SrcValue(src, 0, 2) * (SrcValue(src, 1, 0) * SrcValue(src, 2, 1) - SrcValue(src, 1, 1) * SrcValue(src, 2, 0)); - return a1 - a2 + a3; -} - -static uint8_t UIntToUChar(const uint32_t &v) { - return static_cast(v <= UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); -} - -static int16_t IntCastShort(const int &value) { - return static_cast(static_cast(value - SHRT_MIN) <= static_cast(USHRT_MAX) - ? value - : value > 0 ? SHRT_MAX : SHRT_MIN); -} - -static int16_t FloatToShort(float value) { return IntCastShort(static_cast(roundf(value))); } - -static void InitWBlockInter(float *wBlock, int wBlockSz) { - float scale = 1.f / static_cast(wBlockSz); - for (int i = 0; i < wBlockSz; i++, wBlock += 2) { - float value = static_cast(i) * scale; - wBlock[0] = 1.f - value; - wBlock[1] = value; - } -} - -static const void *InitWBlock() { - static bool initWB = false; - int16_t *iWBlock = nullptr; - int ks = 2; - - iWBlock = BWBlock_i[0][0]; - - if (!initWB) { - auto *_wblock = new float[8 * kTabSz]; - int i, j, h1, h2; - InitWBlockInter(_wblock, kTabSz); - for (i = 0; i < kTabSz; i++) { - for (j = 0; j < kTabSz; j++, iWBlock += ks * ks) { - int sum_i = 0; - for (h1 = 0; h1 < ks; h1++) { - float vy = _wblock[i * ks + h1]; - for (h2 = 0; h2 < ks; h2++) { - float v = vy * _wblock[j * ks + h2]; - sum_i += iWBlock[h1 * ks + h2] = FloatToShort(v * kRemapScale); - } - } - if (sum_i != kRemapScale) { - int df = sum_i - kRemapScale; - int ks2 = 1; - int tk1 = ks2; - int tk2 = ks2; - int mtk1 = ks2; - int mtk2 = ks2; - for (h1 = ks2; h1 < ks2 + 2; h1++) { - for (h2 = ks2; h2 < ks2 + 2; h2++) { - if (iWBlock[h1 * ks + h2] < iWBlock[mtk1 * ks + mtk2]) { - mtk1 = h1, mtk2 = h2; - } else if (iWBlock[h1 * ks + h2] > iWBlock[tk1 * ks + tk2]) { - tk1 = h1, tk2 = h2; - } - } - } - if (df < 0) { - iWBlock[tk1 * ks + tk2] = (int16_t)(iWBlock[tk1 * ks + tk2] - df); - } else { - iWBlock[mtk1 * ks + mtk2] = (int16_t)(iWBlock[mtk1 * ks + mtk2] - df); - } - } - } - } - iWBlock -= kTabSz2 * ks * ks; - delete[] _wblock; - initWB = true; - } - return (const void *)iWBlock; -} - -static uint8_t CastToFixed(int v) { return UIntToUChar(((v + (1 << (kBits1 - 1))) >> kBits1)); } - -static int BorderPolate(int value, int length, PaddBorderType borderType) { - if ((unsigned)value < (unsigned)length) { - return value; - } else if (borderType == 0) { - value = -1; - } - return value; -} - -static void RemapBilinearNotCur1C(int dx, const int16_t *HW, const uint16_t *FHW, const int16_t *wblock, - size_t src_step, const uint8_t *src_ptr, uint8_t *dst_ptr) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - const uint8_t *t_src_ptr = src_ptr + shy * src_step + shx; - *dst_ptr = CastToFixed(static_cast(t_src_ptr[0] * w_ptr[0] + t_src_ptr[1] * w_ptr[1] + - t_src_ptr[src_step] * w_ptr[2] + t_src_ptr[src_step + 1] * w_ptr[3])); -} - -static void RemapBilinearNotCur2C(int dx, const int16_t *HW, const uint16_t *FHW, const int16_t *wblock, - size_t src_step, const uint8_t *src_ptr, uint8_t *dst_ptr) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - const uint8_t *t_src_ptr = src_ptr + shy * src_step + shx * 2; - int v0 = t_src_ptr[0] * w_ptr[0] + t_src_ptr[2] * w_ptr[1] + t_src_ptr[src_step] * w_ptr[2] + - t_src_ptr[src_step + 2] * w_ptr[3]; - int v1 = t_src_ptr[1] * w_ptr[0] + t_src_ptr[3] * w_ptr[1] + t_src_ptr[src_step + 1] * w_ptr[2] + - t_src_ptr[src_step + 3] * w_ptr[3]; - dst_ptr[0] = CastToFixed(v0); - dst_ptr[1] = CastToFixed(v1); -} - -static void RemapBilinearNotCur3C(int dx, const int16_t *HW, const uint16_t *FHW, const int16_t *wblock, - size_t src_step, const uint8_t *src_ptr, uint8_t *dst_ptr) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - const uint8_t *t_src_ptr = src_ptr + shy * src_step + shx * 3; - int v0 = t_src_ptr[0] * w_ptr[0] + t_src_ptr[3] * w_ptr[1] + t_src_ptr[src_step] * w_ptr[2] + - t_src_ptr[src_step + 3] * w_ptr[3]; - int v1 = t_src_ptr[1] * w_ptr[0] + t_src_ptr[4] * w_ptr[1] + t_src_ptr[src_step + 1] * w_ptr[2] + - t_src_ptr[src_step + 4] * w_ptr[3]; - int v2 = t_src_ptr[2] * w_ptr[0] + t_src_ptr[5] * w_ptr[1] + t_src_ptr[src_step + 2] * w_ptr[2] + - t_src_ptr[src_step + 5] * w_ptr[3]; - dst_ptr[0] = CastToFixed(v0); - dst_ptr[1] = CastToFixed(v1); - dst_ptr[2] = CastToFixed(v2); -} - -static void RemapBilinearNotCur4C(int dx, const int16_t *HW, const uint16_t *FHW, const int16_t *wblock, - size_t src_step, const uint8_t *src_ptr, uint8_t *dst_ptr) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - const uint8_t *t_src_ptr = src_ptr + shy * src_step + shx * 4; - int v0 = t_src_ptr[0] * w_ptr[0] + t_src_ptr[4] * w_ptr[1] + t_src_ptr[src_step] * w_ptr[2] + - t_src_ptr[src_step + 4] * w_ptr[3]; - int v1 = t_src_ptr[1] * w_ptr[0] + t_src_ptr[5] * w_ptr[1] + t_src_ptr[src_step + 1] * w_ptr[2] + - t_src_ptr[src_step + 5] * w_ptr[3]; - dst_ptr[0] = CastToFixed(v0); - dst_ptr[1] = CastToFixed(v1); - v0 = t_src_ptr[2] * w_ptr[0] + t_src_ptr[6] * w_ptr[1] + t_src_ptr[src_step + 2] * w_ptr[2] + - t_src_ptr[src_step + 6] * w_ptr[3]; - v1 = t_src_ptr[3] * w_ptr[0] + t_src_ptr[7] * w_ptr[1] + t_src_ptr[src_step + 3] * w_ptr[2] + - t_src_ptr[src_step + 7] * w_ptr[3]; - dst_ptr[2] = CastToFixed(v0); - dst_ptr[3] = CastToFixed(v1); -} - -static void RemapBilinearNotCurMoreC(int dx, const int16_t *HW, const uint16_t *FHW, const int16_t *wblock, - size_t src_step, int cn, const uint8_t *src_ptr, uint8_t *dst_ptr) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - const uint8_t *t_src_ptr = src_ptr + shy * src_step + shx * cn; - for (int k = 0; k < cn; k++) { - int v0 = t_src_ptr[k] * w_ptr[0] + t_src_ptr[k + cn] * w_ptr[1] + t_src_ptr[src_step + k] * w_ptr[2] + - t_src_ptr[src_step + k + cn] * w_ptr[3]; - dst_ptr[k] = CastToFixed(v0); - } -} - -static void RemapBilinearCur1C(const LiteMat &_src, int dx, const int16_t *HW, const uint16_t *FHW, - const int16_t *wblock, size_t src_step, const uint8_t *src_ptr, uint8_t *dst_ptr, - PaddBorderType borderType, const std::vector &borderValue) { - if (borderValue.empty()) { - return; - } - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - if (borderType == PADD_BORDER_CONSTANT && (shx >= _src.width_ || shx + 1 < 0 || shy >= _src.height_ || shy + 1 < 0)) { - dst_ptr[0] = borderValue[0]; - } else { - int sv0; - int sv1; - int su0; - int su1; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - - sv0 = BorderPolate(shx, _src.width_, borderType); - sv1 = BorderPolate(shx + 1, _src.width_, borderType); - su0 = BorderPolate(shy, _src.height_, borderType); - su1 = BorderPolate(shy + 1, _src.height_, borderType); - uint8_t v0 = sv0 >= 0 && su0 >= 0 ? src_ptr[su0 * src_step + sv0] : borderValue[0]; - uint8_t v1 = sv1 >= 0 && su0 >= 0 ? src_ptr[su0 * src_step + sv1] : borderValue[0]; - uint8_t v2 = sv0 >= 0 && su1 >= 0 ? src_ptr[su1 * src_step + sv0] : borderValue[0]; - uint8_t v3 = sv1 >= 0 && su1 >= 0 ? src_ptr[su1 * src_step + sv1] : borderValue[0]; - dst_ptr[0] = CastToFixed(static_cast(v0 * w_ptr[0] + v1 * w_ptr[1] + v2 * w_ptr[2] + v3 * w_ptr[3])); - } -} - -static void RemapBilinearCurMoreC(const LiteMat &_src, int dx, const int16_t *HW, const uint16_t *FHW, - const int16_t *wblock, size_t src_step, int cn, const uint8_t *src_ptr, - uint8_t *dst_ptr, PaddBorderType borderType, - const std::vector &borderValue) { - int shx = HW[dx * 2]; - int shy = HW[dx * 2 + 1]; - if (borderValue.size() < cn || borderValue.empty()) { - return; - } - if (borderType == PADD_BORDER_CONSTANT && (shx >= _src.width_ || shx + 1 < 0 || shy >= _src.height_ || shy + 1 < 0)) { - for (int k = 0; k < cn; k++) { - dst_ptr[k] = borderValue[k]; - } - } else { - int sv0; - int sv1; - int su0; - int su1; - const int16_t *w_ptr = wblock + FHW[dx] * 4; - sv0 = BorderPolate(shx, _src.width_, borderType); - sv1 = BorderPolate(shx + 1, _src.width_, borderType); - su0 = BorderPolate(shy, _src.height_, borderType); - su1 = BorderPolate(shy + 1, _src.height_, borderType); - const uint8_t *v0 = sv0 >= 0 && su0 >= 0 ? src_ptr + su0 * src_step + sv0 * cn : &borderValue[0]; - const uint8_t *v1 = sv1 >= 0 && su0 >= 0 ? src_ptr + su0 * src_step + sv1 * cn : &borderValue[0]; - const uint8_t *v2 = sv0 >= 0 && su1 >= 0 ? src_ptr + su1 * src_step + sv0 * cn : &borderValue[0]; - const uint8_t *v3 = sv1 >= 0 && su1 >= 0 ? src_ptr + su1 * src_step + sv1 * cn : &borderValue[0]; - - for (int k = 0; k < cn; k++) { - dst_ptr[k] = - CastToFixed(static_cast(v0[k] * w_ptr[0] + v1[k] * w_ptr[1] + v2[k] * w_ptr[2] + v3[k] * w_ptr[3])); - } - } -} - -static void RemapBilinear(const LiteMat &_src, LiteMat &_dst, const LiteMat &_hw, const LiteMat &_fhw, // NOLINT - const void *_wblock, const PaddBorderType borderType, - const std::vector &borderValue) { - const int cn = _src.channel_; - const auto *wblock = static_cast(_wblock); - const uint8_t *src_ptr = _src.ptr(0); - size_t src_step = _src.steps_[0]; - unsigned src_width = std::max(_src.width_ - 1, 0); - unsigned src_height = std::max(_src.height_ - 1, 0); - - for (int dy = 0; dy < _dst.height_; dy++) { - auto *dst_ptr = _dst.ptr(dy); - const int16_t *HW = _hw.ptr(dy); - const uint16_t *FHW = _fhw.ptr(dy); - int tt = 0; - bool prevLine = false; - - for (int dx = 0; dx <= _dst.width_; dx++) { - bool curLine = - dx < _dst.width_ ? (unsigned)HW[dx * 2] < src_width && (unsigned)HW[dx * 2 + 1] < src_height : !prevLine; - if (curLine == prevLine) { - continue; - } - - int H1 = dx; - dx = tt; - tt = H1; - prevLine = curLine; - - if (!curLine) { - int length = 0; - dst_ptr += length * cn; - dx += length; - - if (cn == 1) { - for (; dx < H1; dx++, dst_ptr++) { - RemapBilinearNotCur1C(dx, HW, FHW, wblock, src_step, src_ptr, dst_ptr); - } - } else if (cn == 2) { - for (; dx < H1; dx++, dst_ptr += 2) { - RemapBilinearNotCur2C(dx, HW, FHW, wblock, src_step, src_ptr, dst_ptr); - } - } else if (cn == 3) { - for (; dx < H1; dx++, dst_ptr += 3) { - RemapBilinearNotCur3C(dx, HW, FHW, wblock, src_step, src_ptr, dst_ptr); - } - } else if (cn == 4) { - for (; dx < H1; dx++, dst_ptr += 4) { - RemapBilinearNotCur4C(dx, HW, FHW, wblock, src_step, src_ptr, dst_ptr); - } - } else { - for (; dx < H1; dx++, dst_ptr += cn) { - RemapBilinearNotCurMoreC(dx, HW, FHW, wblock, src_step, cn, src_ptr, dst_ptr); - } - } - } else { - if (cn == 1) { - for (; dx < H1; dx++, dst_ptr++) { - RemapBilinearCur1C(_src, dx, HW, FHW, wblock, src_step, src_ptr, dst_ptr, borderType, borderValue); - } - } else { - for (; dx < H1; dx++, dst_ptr += cn) { - RemapBilinearCurMoreC(_src, dx, HW, FHW, wblock, src_step, cn, src_ptr, dst_ptr, borderType, borderValue); - } - } - } - } - } -} - -static void Remap(const LiteMat &src, LiteMat &dst, LiteMat &map1, const LiteMat &map2, // NOLINT - const PaddBorderType borderType, const std::vector &borderValue) { - int x, y, x1, y1; - const int buf_size = 1 << 14; - int dst_height = std::min(128, dst.height_); - int dst_width = std::min(buf_size / dst_height, dst.width_); - dst_height = std::min(buf_size / dst_width, dst.height_); - - const void *wblock = InitWBlock(); - - LiteMat _xyblock(dst_width, dst_height, 2, LDataType::INT16); - LiteMat _ablock(dst_width, dst_height, 1, LDataType::UINT16); - for (y = 0; y < dst.height_; y += dst_height) { - for (x = 0; x < dst.width_; x += dst_width) { - int bheight = std::min(dst_height, dst.height_ - y); - int bwidth = std::min(dst_width, dst.width_ - x); - LiteMat lite_part; - dst.GetROI(x, y, bwidth, bheight, lite_part); - - LiteMat xy_ptr; - _xyblock.GetROI(0, 0, bwidth, bheight, xy_ptr); - LiteMat a_ptr; - _ablock.GetROI(0, 0, bwidth, bheight, a_ptr); - - for (y1 = 0; y1 < bheight; y1++) { - auto *t_a_ptr = a_ptr.ptr(y1); - - map1.GetROI(x, y, bwidth, bheight, xy_ptr); - - const uint16_t *sa_ptr = map2.ptr(y + y1) + x; - x1 = 0; - for (; x1 < bwidth; x1++) { - t_a_ptr[x1] = (uint16_t)(sa_ptr[x1] & (kTabSz2 - 1)); - } - } - RemapBilinear(src, lite_part, xy_ptr, a_ptr, wblock, borderType, borderValue); - } - } -} - -bool WarpAffineBilinear(const LiteMat &src, LiteMat &dst, const LiteMat &M, int dst_w, int dst_h, // NOLINT - PaddBorderType borderType, std::vector &borderValue) { // NOLINT - if (dst_w <= 0 || dst_h <= 0) { - return false; - } - - if (!(M.height_ == 2 && M.width_ == 3)) { - return false; - } - if (borderType != PADD_BORDER_CONSTANT) { - return false; - } - if (borderValue.size() != src.channel_) { - return false; - } - if (dst.IsEmpty()) { - (void)dst.Init(dst_w, dst_h, src.channel_, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } else if (dst.height_ != dst_h || dst.width_ != dst_w || dst.channel_ != src.channel_ || - dst.data_type_ != LDataType::UINT8) { - return false; - } - - // 0, 1, 2, 3, 4, 5 is the Affine matrix index of IM - double IM[6]; - const double *M_Ptr = M; - for (int i = 0; i < 6; i++) { - IM[i] = M_Ptr[i]; - } - - double D = IM[0] * IM[4] - IM[1] * IM[3]; - D = std::fabs(D) > std::numeric_limits::epsilon() ? 1.0f / D : 0; - double A11 = IM[4] * D, A22 = IM[0] * D; - IM[0] = A11; - IM[1] *= -D; - IM[3] *= -D; - IM[4] = A22; - double b1 = -IM[0] * IM[2] - IM[1] * IM[5]; - double b2 = -IM[3] * IM[2] - IM[4] * IM[5]; - IM[2] = b1; - IM[5] = b2; - - int *_a = new int[dst.width_ * 2]; - int *a = &_a[0], *b = a + dst.width_; - const int SCALE = 1 << 10; - const int B_SIZE = 64; - auto *WH = new int16_t[B_SIZE * B_SIZE * 2]; - int16_t A_Ptr[B_SIZE * B_SIZE]; - int r_delta = SCALE / kTabSz / 2; - int x, y, x1, y1; - for (x = 0; x < dst.width_; x++) { - a[x] = static_cast(round(IM[0] * x * SCALE)); - b[x] = static_cast(round(IM[3] * x * SCALE)); - } - int t_bh0 = std::min(B_SIZE / 2, dst.height_); - int t_bw0 = std::min(B_SIZE * B_SIZE / t_bh0, dst.width_); - t_bh0 = std::min(B_SIZE * B_SIZE / t_bw0, dst.height_); - - for (y = 0; y < dst.height_; y += t_bh0) { - for (x = 0; x < dst.width_; x += t_bw0) { - int t_bw = std::min(t_bw0, dst.width_ - x); - int t_bh = std::min(t_bh0, dst.height_ - y); - LiteMat _HW(t_bw, t_bh, 2, WH, LDataType::INT16); - LiteMat lite_part; - dst.GetROI(x, y, t_bw, t_bh, lite_part); - - for (y1 = 0; y1 < t_bh; y1++) { - int16_t *t_xy = WH + static_cast(y1 * t_bw * 2); - int X0 = static_cast(round((IM[1] * (y + y1) + IM[2]) * SCALE) + r_delta); - int Y0 = static_cast(round((IM[4] * (y + y1) + IM[5]) * SCALE) + r_delta); - int16_t *t_a = A_Ptr + y1 * t_bw; - x1 = 0; - for (; x1 < t_bw; x1++) { - int X = (X0 + a[x + x1]) >> (10 - kBits); - int Y = (Y0 + b[x + x1]) >> (10 - kBits); - t_xy[x1 * 2] = IntCastShort(X >> kBits); - t_xy[x1 * 2 + 1] = IntCastShort(Y >> kBits); - t_a[x1] = (int16_t)((Y & (kTabSz - 1)) * kTabSz + (X & (kTabSz - 1))); - } - } - - LiteMat _matA(t_bw, t_bh, 1, A_Ptr, LDataType::UINT16); - Remap(src, lite_part, _HW, _matA, borderType, borderValue); - } - } - delete[] WH; - delete[] _a; - return true; -} - -static void PerspectiveInvert(double *src, double *dst) { - double value = GetDet3(src); - if (std::fabs(value) > std::numeric_limits::epsilon()) { - value = 1. / value; - double v[9]; - - v[0] = (SrcValue(src, 1, 1) * SrcValue(src, 2, 2) - SrcValue(src, 1, 2) * SrcValue(src, 2, 1)) * value; - v[1] = (SrcValue(src, 0, 2) * SrcValue(src, 2, 1) - SrcValue(src, 0, 1) * SrcValue(src, 2, 2)) * value; - v[2] = (SrcValue(src, 0, 1) * SrcValue(src, 1, 2) - SrcValue(src, 0, 2) * SrcValue(src, 1, 1)) * value; - - v[3] = (SrcValue(src, 1, 2) * SrcValue(src, 2, 0) - SrcValue(src, 1, 0) * SrcValue(src, 2, 2)) * value; - v[4] = (SrcValue(src, 0, 0) * SrcValue(src, 2, 2) - SrcValue(src, 0, 2) * SrcValue(src, 2, 0)) * value; - v[5] = (SrcValue(src, 0, 2) * SrcValue(src, 1, 0) - SrcValue(src, 0, 0) * SrcValue(src, 1, 2)) * value; - - v[6] = (SrcValue(src, 1, 0) * SrcValue(src, 2, 1) - SrcValue(src, 1, 1) * SrcValue(src, 2, 0)) * value; - v[7] = (SrcValue(src, 0, 1) * SrcValue(src, 2, 0) - SrcValue(src, 0, 0) * SrcValue(src, 2, 1)) * value; - v[8] = (SrcValue(src, 0, 0) * SrcValue(src, 1, 1) - SrcValue(src, 0, 1) * SrcValue(src, 1, 0)) * value; - - DstValue(dst, 0, 0) = v[0]; - DstValue(dst, 0, 1) = v[1]; - DstValue(dst, 0, 2) = v[2]; - DstValue(dst, 1, 0) = v[3]; - DstValue(dst, 1, 1) = v[4]; - DstValue(dst, 1, 2) = v[5]; - DstValue(dst, 2, 0) = v[6]; - DstValue(dst, 2, 1) = v[7]; - DstValue(dst, 2, 2) = v[8]; - } -} - -bool WarpPerspectiveBilinear(const LiteMat &src, LiteMat &dst, const LiteMat &M, int dst_w, int dst_h, // NOLINT - PaddBorderType borderType, std::vector &borderValue) { // NOLINT - if (dst_w <= 0 || dst_h <= 0) { - return false; - } - if (!(M.height_ == 3 && M.width_ == 3)) { - return false; - } - if (borderType != PADD_BORDER_CONSTANT) { - return false; - } - if (borderValue.size() != src.channel_) { - return false; - } - if (dst.IsEmpty()) { - (void)dst.Init(dst_w, dst_h, src.channel_, LDataType::UINT8); - RETURN_FALSE_IF_LITEMAT_EMPTY(dst); - } else if (dst.height_ != dst_h || dst.width_ != dst_w || dst.channel_ != src.channel_) { - return false; - } else if (dst.data_type_ != LDataType::UINT8) { - return false; - } else { - } - - double IM[9]; - const double *M_Ptr = M; - for (int i = 0; i < 9; i++) { - IM[i] = M_Ptr[i]; - } - PerspectiveInvert(IM, IM); - const int B_SZ = 32; - int16_t HW[B_SZ * B_SZ * 2]; - int16_t TA[B_SZ * B_SZ]; - int x; - int y; - int y1; - int width = dst.width_; - int height = dst.height_; - - int bheight = std::min(B_SZ / 2, height); - int bwidth = std::min(B_SZ * B_SZ / bheight, width); - bheight = std::min(B_SZ * B_SZ / bwidth, height); - for (y = 0; y < dst.height_; y += bheight) { - for (x = 0; x < width; x += bwidth) { - int tw = std::min(bwidth, width - x); - int th = std::min(bheight, dst.height_ - y); - - LiteMat _HW(tw, th, 2, HW, LDataType::INT16); - LiteMat lite_part; - dst.GetROI(x, y, tw, th, lite_part); - - for (y1 = 0; y1 < th; y1++) { - int16_t *xy = HW + y1 * tw * 2; - double XV = IM[0] * x + IM[1] * (y + y1) + IM[2]; - double YV = IM[3] * x + IM[4] * (y + y1) + IM[5]; - double WV = IM[6] * x + IM[7] * (y + y1) + IM[8]; - - int16_t *t_a = TA + y1 * tw; - for (int x1 = 0; x1 < tw; x1++) { - double W = WV + IM[6] * x1; - W = (std::fabs(W) > std::numeric_limits::epsilon()) ? kTabSz / W : 0; - double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (XV + IM[0] * x1) * W)); // NOLINT - double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (YV + IM[3] * x1) * W)); // NOLINT - int X = static_cast(round(fX)); - int Y = static_cast(round(fY)); - - xy[x1 * 2] = IntCastShort(X >> kBits); - xy[x1 * 2 + 1] = IntCastShort(Y >> kBits); - t_a[x1] = (int16_t)((Y & (kTabSz - 1)) * kTabSz + (X & (kTabSz - 1))); - } - } - LiteMat _matA(tw, th, 1, TA, LDataType::UINT16); - Remap(src, lite_part, _HW, _matA, borderType, borderValue); - } - } - return true; -} - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.cc deleted file mode 100644 index dd6b0ac17..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.cc +++ /dev/null @@ -1,1219 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" - -#include -#include -#include -#include - -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -#include -#include -#include -#endif - -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#endif -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h" -#include "mindspore-lite/minddata/dataset/kernels/image/lite_cv/lite_mat.h" -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -#include "mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/random.h" - -constexpr int64_t hw_shape = 2; -constexpr int64_t hwc_rank = 3; - -#define MAX_INT_PRECISION 16777216 // float int precision is 16777216 -namespace mindspore { -namespace dataset { -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -bool IsNonEmptyPNG(const std::shared_ptr &input) { - const unsigned char kPngMagic[] = "\x89\x50\x4E\x47"; - constexpr dsize_t kPngMagicLen = 4; - return input->SizeInBytes() > kPngMagicLen && memcmp(input->GetBuffer(), kPngMagic, kPngMagicLen) == 0; -} - -Status Rescale(const std::shared_ptr &input, std::shared_ptr *output, float rescale, float shift) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rescale: load image failed."); - } - cv::Mat input_image = input_cv->mat(); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), DataType(DataType::DE_FLOAT32), &output_cv)); - try { - input_image.convertTo(output_cv->mat(), CV_32F, rescale, shift); - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Rescale: " + std::string(e.what())); - } - return Status::OK(); -} - -Status SwapRedAndBlue(std::shared_ptr input, std::shared_ptr *output) { - try { - RETURN_IF_NOT_OK(ValidateImage(input, "SwapRedBlue", {3, 5, 11})); - std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); - CHECK_FAIL_RETURN_UNEXPECTED( - input_cv->shape().Size() > kChannelIndexHWC, - "SwapRedAndBlue: rank of input data should be greater than:" + std::to_string(kChannelIndexHWC) + - ", but got:" + std::to_string(input_cv->shape().Size())); - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - if (input_cv->shape().Size() != kDefaultImageRank || num_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("SwapRedBlue: image shape should be in format, but got:" + - input_cv->shape().ToString()); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_BGR2RGB)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("SwapRedBlue: " + std::string(e.what())); - } -} -#endif - -bool IsNonEmptyJPEG(const std::shared_ptr &input) { - const unsigned char *kJpegMagic = (unsigned char *)"\xFF\xD8\xFF"; - constexpr size_t kJpegMagicLen = 3; - return input->SizeInBytes() > kJpegMagicLen && memcmp(input->GetBuffer(), kJpegMagic, kJpegMagicLen) == 0; -} - -static void JpegInitSource(j_decompress_ptr cinfo) {} - -static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) { - if (cinfo->src->bytes_in_buffer == 0) { - // Under ARM platform raise runtime_error may cause core problem, - // so we catch runtime_error and just return FALSE. - try { - ERREXIT(cinfo, JERR_INPUT_EMPTY); - } catch (const std::exception &e) { - return FALSE; - } - return FALSE; - } - return TRUE; -} - -static void JpegTermSource(j_decompress_ptr cinfo) {} - -static void JpegSkipInputData(j_decompress_ptr cinfo, int64_t jump) { - if (jump < 0) { - return; - } - if (static_cast(jump) > cinfo->src->bytes_in_buffer) { - cinfo->src->bytes_in_buffer = 0; - return; - } else { - cinfo->src->bytes_in_buffer -= jump; - cinfo->src->next_input_byte += jump; - } -} - -void JpegSetSource(j_decompress_ptr cinfo, const void *data, int64_t datasize) { - cinfo->src = static_cast( - (*cinfo->mem->alloc_small)(reinterpret_cast(cinfo), JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr))); - cinfo->src->init_source = JpegInitSource; - cinfo->src->fill_input_buffer = JpegFillInputBuffer; -#if defined(_WIN32) || defined(_WIN64) || defined(ENABLE_ARM32) - // the following line skips CI because it uses underlying C type - cinfo->src->skip_input_data = reinterpret_cast(JpegSkipInputData); // NOLINT. -#else - cinfo->src->skip_input_data = JpegSkipInputData; -#endif - cinfo->src->resync_to_restart = jpeg_resync_to_restart; - cinfo->src->term_source = JpegTermSource; - cinfo->src->bytes_in_buffer = datasize; - cinfo->src->next_input_byte = static_cast(data); -} - -static Status JpegReadScanlines(jpeg_decompress_struct *const cinfo, int max_scanlines_to_read, JSAMPLE *buffer, - int buffer_size, int crop_w, int crop_w_aligned, int offset, int stride) { - // scanlines will be read to this buffer first, must have the number - // of components equal to the number of components in the image - int64_t scanline_size = crop_w_aligned * cinfo->output_components; - std::vector scanline(scanline_size); - JSAMPLE *scanline_ptr = &scanline[0]; - while (cinfo->output_scanline < static_cast(max_scanlines_to_read)) { - unsigned int num_lines_read = 0; - try { - num_lines_read = jpeg_read_scanlines(cinfo, &scanline_ptr, 1); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Decode: jpeg_read_scanlines error."); - } - if (cinfo->out_color_space == JCS_CMYK && num_lines_read > 0) { - for (int i = 0; i < crop_w; ++i) { - const int cmyk_pixel = 4 * i + offset; - const int c = scanline_ptr[cmyk_pixel]; - const int m = scanline_ptr[cmyk_pixel + 1]; - const int y = scanline_ptr[cmyk_pixel + 2]; - const int k = scanline_ptr[cmyk_pixel + 3]; - int r, g, b; - if (cinfo->saw_Adobe_marker) { - r = (k * c) / kMaxPixelValue; - g = (k * m) / kMaxPixelValue; - b = (k * y) / kMaxPixelValue; - } else { - r = (kMaxPixelValue - c) * (kMaxPixelValue - k) / kMaxPixelValue; - g = (kMaxPixelValue - m) * (kMaxPixelValue - k) / kMaxPixelValue; - b = (kMaxPixelValue - y) * (kMaxPixelValue - k) / kMaxPixelValue; - } - constexpr int buffer_rgb_val_size = 3; - constexpr int channel_red = 0; - constexpr int channel_green = 1; - constexpr int channel_blue = 2; - buffer[buffer_rgb_val_size * i + channel_red] = r; - buffer[buffer_rgb_val_size * i + channel_green] = g; - buffer[buffer_rgb_val_size * i + channel_blue] = b; - } - } else if (num_lines_read > 0) { - auto copy_status = memcpy_s(buffer, buffer_size, scanline_ptr + offset, stride); - if (copy_status != 0) { - jpeg_destroy_decompress(cinfo); - RETURN_STATUS_UNEXPECTED("Decode: memcpy_s failed"); - } - } else { - jpeg_destroy_decompress(cinfo); - std::string err_msg = "Decode: failed to decompress image."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - buffer += stride; - buffer_size = buffer_size - stride; - } - return Status::OK(); -} - -static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { - switch (cinfo->num_components) { - case 1: - // we want to output 3 components if it's grayscale - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 3: - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 4: - // Need to manually convert to RGB - cinfo->out_color_space = JCS_CMYK; - return Status::OK(); - default: - jpeg_destroy_decompress(cinfo); - std::string err_msg = "Decode: failed to decompress image."; - RETURN_STATUS_UNEXPECTED(err_msg); - } -} - -void JpegErrorExitCustom(j_common_ptr cinfo) { - char jpeg_last_error_msg[JMSG_LENGTH_MAX]; - (*(cinfo->err->format_message))(cinfo, jpeg_last_error_msg); - throw std::runtime_error(jpeg_last_error_msg); -} - -Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int crop_x, int crop_y, - int crop_w, int crop_h) { - struct jpeg_decompress_struct cinfo {}; - auto DestroyDecompressAndReturnError = [&cinfo](const std::string &err) { - jpeg_destroy_decompress(&cinfo); - RETURN_STATUS_UNEXPECTED(err); - }; - struct JpegErrorManagerCustom jerr {}; - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = JpegErrorExitCustom; - try { - jpeg_create_decompress(&cinfo); - JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); - (void)jpeg_read_header(&cinfo, TRUE); - RETURN_IF_NOT_OK(JpegSetColorSpace(&cinfo)); - jpeg_calc_output_dimensions(&cinfo); - } catch (const std::exception &e) { - return DestroyDecompressAndReturnError(e.what()); - } - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_w) > crop_x, "invalid crop width"); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_h) > crop_y, "invalid crop height"); - if (crop_x == 0 && crop_y == 0 && crop_w == 0 && crop_h == 0) { - crop_w = static_cast(cinfo.output_width); - crop_h = static_cast(cinfo.output_height); - } else if (crop_w == 0 || static_cast(crop_w + crop_x) > cinfo.output_width || crop_h == 0 || - static_cast(crop_h + crop_y) > cinfo.output_height) { - return DestroyDecompressAndReturnError("Decode: invalid crop size"); - } - const int mcu_size = cinfo.min_DCT_scaled_size; - CHECK_FAIL_RETURN_UNEXPECTED(mcu_size != 0, "Invalid data."); - unsigned int crop_x_aligned = (crop_x / mcu_size) * mcu_size; - unsigned int crop_w_aligned = crop_w + crop_x - crop_x_aligned; - try { - (void)jpeg_start_decompress(&cinfo); - jpeg_crop_scanline(&cinfo, &crop_x_aligned, &crop_w_aligned); - } catch (const std::exception &e) { - return DestroyDecompressAndReturnError(e.what()); - } - JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); - // three number of output components, always convert to RGB and output - constexpr int kOutNumComponents = 3; - TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); - const int buffer_size = static_cast(output_tensor->SizeInBytes()); - JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, - "Invalid crop height."); - const int max_scanlines_to_read = static_cast(skipped_scanlines) + crop_h; - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, - "Invalid crop width."); - const int stride = crop_w * kOutNumComponents; - // offset is calculated for scanlines read from the image, therefore - // has the same number of components as the image - const int offset = (crop_x - static_cast(crop_x_aligned)) * cinfo.output_components; - RETURN_IF_NOT_OK( - JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); - *output = output_tensor; - jpeg_destroy_decompress(&cinfo); - return Status::OK(); -} - -static LDataType GetLiteCVDataType(const DataType &data_type) { - if (data_type == DataType::DE_UINT8) { - return LDataType::UINT8; - } else if (data_type == DataType::DE_FLOAT32) { - return LDataType::FLOAT32; - } else { - return LDataType::UNKNOWN; - } -} - -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -Status DecodeCv(const std::shared_ptr &input, std::shared_ptr *output) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: load image failed."); - } - try { - cv::Mat img_mat = cv::imdecode(input_cv->mat(), cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION); - if (img_mat.data == nullptr) { - std::string err = "Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err); - } - cv::cvtColor(img_mat, img_mat, static_cast(cv::COLOR_BGR2RGB)); - std::shared_ptr output_cv; - const dsize_t rank_num = 3; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(img_mat, rank_num, &output_cv)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Decode: " + std::string(e.what())); - } -} -#endif - -Status Decode(const std::shared_ptr &input, std::shared_ptr *output) { - if (IsNonEmptyJPEG(input)) { - return JpegCropAndDecode(input, output); - } else { -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) - return DecodeCv(input, output); -#else - RETURN_STATUS_UNEXPECTED("Decode: Decode only supports jpeg for android"); -#endif - } -} - -Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h) { - if (input->Rank() != 3 && input->Rank() != 2) { - RETURN_STATUS_UNEXPECTED("Crop: image shape is not or "); - } - - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("Crop: image datatype is not float32 or uint8"); - } - - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - y) > h, "Invalid crop height."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x) > w, "Invalid crop width."); - // account for integer overflow - if (y < 0 || (y + h) > input->shape()[0] || (y + h) < 0) { - RETURN_STATUS_UNEXPECTED( - "Crop: invalid y coordinate value for crop" - "y coordinate value exceeds the boundary of the image."); - } - // account for integer overflow - if (x < 0 || (x + w) > input->shape()[1] || (x + w) < 0) { - RETURN_STATUS_UNEXPECTED( - "Crop: invalid x coordinate value for crop" - "x coordinate value exceeds the boundary of the image."); - } - - try { - LiteMat lite_mat_rgb; - TensorShape shape{h, w}; - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - if (input->Rank() == 2) { - lite_mat_rgb.Init(input_width, input_height, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else { // rank == 3 - lite_mat_rgb.Init(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - shape = shape.AppendDim(input_channel); - } - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_rgb.IsEmpty(), "Crop: Init image tensor failed, return empty tensor."); - - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); - - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - LiteMat lite_mat_cut; - - lite_mat_cut.Init(w, h, lite_mat_rgb.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_cut.IsEmpty(), "Crop: Init image tensor failed, return empty tensor."); - - bool ret = Crop(lite_mat_rgb, lite_mat_cut, x, y, w, h); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Crop: image crop failed."); - - *output = output_tensor; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Crop: " + std::string(e.what())); - } - return Status::OK(); -} - -Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height) { - struct jpeg_decompress_struct cinfo {}; - struct JpegErrorManagerCustom jerr {}; - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = JpegErrorExitCustom; - try { - jpeg_create_decompress(&cinfo); - JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); - (void)jpeg_read_header(&cinfo, TRUE); - jpeg_calc_output_dimensions(&cinfo); - } catch (const std::exception &e) { - jpeg_destroy_decompress(&cinfo); - RETURN_STATUS_UNEXPECTED(e.what()); - } - *img_height = static_cast(cinfo.output_height); - *img_width = static_cast(cinfo.output_width); - jpeg_destroy_decompress(&cinfo); - return Status::OK(); -} - -Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &vec_mean, const std::vector &vec_std) { - if (input->Rank() != 3) { - RETURN_STATUS_UNEXPECTED("Normalize: image shape is not ."); - } - - if (input->type() != DataType::DE_UINT8 && input->type() != DataType::DE_FLOAT32) { - RETURN_STATUS_UNEXPECTED("Normalize: image datatype is not uint8 or float32."); - } - - try { - LiteMat lite_mat_norm; - bool ret = false; - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - - if (input->type() == DataType::DE_UINT8) { - LiteMat lite_mat_float; - // change input to float - ret = ConvertTo(lite_mat_rgb, lite_mat_float, 1.0); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Normalize: convert to float datatype failed."); - ret = SubStractMeanNormalize(lite_mat_float, lite_mat_norm, vec_mean, vec_std); - } else { // float32 - ret = SubStractMeanNormalize(lite_mat_rgb, lite_mat_norm, vec_mean, vec_std); - } - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Normalize: normalize failed."); - - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(input->shape(), DataType(DataType::DE_FLOAT32), - static_cast(lite_mat_norm.data_ptr_), &output_tensor)); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Normalize: " + std::string(e.what())); - } - return Status::OK(); -} - -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -int GetCVInterpolationMode(InterpolationMode mode) { - switch (mode) { - case InterpolationMode::kLinear: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - case InterpolationMode::kCubic: - return static_cast(cv::InterpolationFlags::INTER_CUBIC); - case InterpolationMode::kArea: - return static_cast(cv::InterpolationFlags::INTER_AREA); - case InterpolationMode::kNearestNeighbour: - return static_cast(cv::InterpolationFlags::INTER_NEAREST); - default: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - } -} - -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Resize: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Resize", input_cv->Rank())); - - cv::Mat in_image = input_cv->mat(); - const uint32_t kResizeShapeLimits = 1000; - // resize image too large or too small, 1000 is arbitrarily chosen here to prevent open cv from segmentation fault - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.rows, - "Resize: in_image rows out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.cols, - "Resize: in_image cols out of bounds."); - if (output_height > in_image.rows * kResizeShapeLimits || output_width > in_image.cols * kResizeShapeLimits) { - RETURN_STATUS_ERROR( - StatusCode::kMDShapeMisMatch, - "Resize: the resizing width or height is too big, it's 1000 times bigger than the original image, got output " - "height: " + - std::to_string(output_height) + ", width: " + std::to_string(output_width) + - ", and original image size:" + std::to_string(in_image.rows) + ", " + std::to_string(in_image.cols)); - } - if (output_height == 0 || output_width == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDShapeMisMatch, - "Resize: the input value of 'resize' is invalid, width or height is zero."); - } - - if (mode == InterpolationMode::kCubicPil) { - if (input_cv->shape().Size() != kDefaultImageChannel || - input_cv->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("Resize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + - input_cv->shape().ToString()); - } - - LiteMat im_in; - LiteMat im_out; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({output_height, output_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - im_out.Init(output_width, output_height, static_cast(input_cv->shape()[kChannelIndexHWC]), - reinterpret_cast(buffer), LDataType::UINT8); - im_in.Init(static_cast(input_cv->shape()[1]), static_cast(input_cv->shape()[0]), - static_cast(input_cv->shape()[kChannelIndexHWC]), input_cv->mat().data, LDataType::UINT8); - CHECK_FAIL_RETURN_UNEXPECTED(!im_out.IsEmpty(), "Resize: Init image tensor failed, return empty tensor."); - CHECK_FAIL_RETURN_UNEXPECTED(!im_in.IsEmpty(), "Resize: Init image tensor failed, return empty tensor."); - if (ResizeCubic(im_in, im_out, output_width, output_height) == false) { - RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); - } - *output = output_tensor; - return Status::OK(); - } - try { - TensorShape shape{output_height, output_width}; - if (input_cv->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input_cv->shape()[kChannelIndexHWC]); - shape = shape.AppendDim(num_channels); - } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); - - auto cv_mode = GetCVInterpolationMode(mode); - cv::resize(in_image, output_cv->mat(), cv::Size(output_width, output_height), fx, fy, cv_mode); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("Resize: " + std::string(e.what())); - } -} - -#else -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { - if (mode != InterpolationMode::kLinear) { - RETURN_STATUS_UNEXPECTED("Resize: Only Liner interpolation is supported currently."); - } - if (input->Rank() != 3 && input->Rank() != 2) { - RETURN_STATUS_UNEXPECTED("Resize: input image is not in shape of or "); - } - if (input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("Resize: image datatype is not uint8."); - } - // resize image too large or too small - const int height_width_scale_limit = 1000; - if (output_height == 0 || output_height > input->shape()[0] * height_width_scale_limit || output_width == 0 || - output_width > input->shape()[1] * height_width_scale_limit) { - std::string err_msg = - "Resize: the resizing width or height 1) is too big, it's up to " - "1000 times the original image; 2) can not be 0."; - return Status(StatusCode::kMDShapeMisMatch, err_msg); - } - try { - LiteMat lite_mat_rgb; - TensorShape shape{output_height, output_width}; - if (input->Rank() == 2) { - lite_mat_rgb.Init(input->shape()[1], input->shape()[0], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else { // rank == 3 - lite_mat_rgb.Init(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - int num_channels = input->shape()[2]; - shape = shape.AppendDim(num_channels); - } - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_rgb.IsEmpty(), "Resize: Init image tensor failed, return empty tensor."); - - LiteMat lite_mat_resize; - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); - - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - - lite_mat_resize.Init(output_width, output_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_resize.IsEmpty(), "Resize: Init image tensor failed, return empty tensor."); - - bool ret = ResizeBilinear(lite_mat_rgb, lite_mat_resize, output_width, output_height); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Resize: " + std::string(e.what())); - } - return Status::OK(); -} -#endif - -Status ResizePreserve(const TensorRow &inputs, int32_t height, int32_t width, int32_t img_orientation, - TensorRow *outputs) { - constexpr int64_t size = 3; - outputs->resize(size); - CHECK_FAIL_RETURN_UNEXPECTED(inputs.size() > 0, - "Invalid input, should be greater than 0, but got " + std::to_string(inputs.size())); - const std::shared_ptr &input = inputs[0]; - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be greater than 3 dimensions."); - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_src(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - - LiteMat lite_mat_dst; - std::shared_ptr image_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, DataType(DataType::DE_FLOAT32), &image_tensor)); - uint8_t *buffer = reinterpret_cast(&(*image_tensor->begin())); - lite_mat_dst.Init(width, height, input_channel, reinterpret_cast(buffer), LDataType::FLOAT32); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_dst.IsEmpty(), "Resize: Init image tensor failed, return empty tensor."); - - float ratioShiftWShiftH[3] = {0}; - float invM[2][3] = {{0, 0, 0}, {0, 0, 0}}; - bool ret = - ResizePreserveARWithFiller(lite_mat_src, lite_mat_dst, height, width, &ratioShiftWShiftH, &invM, img_orientation); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); - - std::shared_ptr ratio_tensor; - TensorShape ratio_shape = TensorShape({3}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(ratio_shape, DataType(DataType::DE_FLOAT32), - reinterpret_cast(&ratioShiftWShiftH), &ratio_tensor)); - - std::shared_ptr invM_tensor; - TensorShape invM_shape = TensorShape({2, 3}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(invM_shape, DataType(DataType::DE_FLOAT32), - reinterpret_cast(&invM), &invM_tensor)); - - (*outputs)[0] = image_tensor; - (*outputs)[1] = ratio_tensor; - (*outputs)[2] = invM_tensor; - return Status::OK(); -} - -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output) { - if (input->Rank() != hwc_rank) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: input image is not in shape of "); - } - if (input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: image datatype is not uint8."); - } - - try { - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[1]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_convert; - std::shared_ptr output_tensor; - constexpr auto kInputChannel = 3; - TensorShape new_shape = TensorShape({input_height, input_width, kInputChannel}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_convert.Init(input_width, input_height, kInputChannel, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_convert.IsEmpty(), - "RgbToBgr: Init image tensor failed, return empty tensor."); - - bool ret = - ConvertRgbToBgr(lite_mat_rgb, GetLiteCVDataType(input->type()), input_width, input_height, lite_mat_convert); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToBgr: RGBToBGR failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); - } - return Status::OK(); -} - -Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output) { - if (input->Rank() != 3) { - RETURN_STATUS_UNEXPECTED("RgbToGray: input image is not in shape of "); - } - if (input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("RgbToGray: image datatype is not uint8."); - } - - try { - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_convert; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({input_height, input_width, 1}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_convert.Init(input_width, input_height, 1, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_convert.IsEmpty(), - "RgbToBgr: Init image tensor failed, return empty tensor."); - - bool ret = - ConvertRgbToGray(lite_mat_rgb, GetLiteCVDataType(input->type()), input_width, input_height, lite_mat_convert); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToGray: RGBToGRAY failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToGray: " + std::string(e.what())); - } - return Status::OK(); -} - -Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, - const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, - uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { - if (input->Rank() != 3) { - RETURN_STATUS_UNEXPECTED("Pad: input image is not in shape of "); - } - - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("Pad: image datatype is not uint8 or float32."); - } - - if (pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0) { - RETURN_STATUS_UNEXPECTED( - "Pad: " - "the top, bottom, left, right of pad must be greater than 0."); - } - - try { - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_pad; - - std::shared_ptr output_tensor; - - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_) > pad_left, - "Invalid pad width."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_ + pad_left) > pad_right, - "Invalid pad width."); - int pad_width = lite_mat_rgb.width_ + pad_left + pad_right; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_) > pad_top, - "Invalid pad height."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_ + pad_top) > pad_bottom, - "Invalid pad height."); - int pad_height = lite_mat_rgb.height_ + pad_top + pad_bottom; - TensorShape new_shape = TensorShape({pad_height, pad_width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - - lite_mat_pad.Init(pad_width, pad_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_pad.IsEmpty(), "Pad: Init image tensor failed, return empty tensor."); - - bool ret = Pad(lite_mat_rgb, lite_mat_pad, pad_top, pad_bottom, pad_left, pad_right, - PaddBorderType::PADD_BORDER_CONSTANT, fill_r, fill_g, fill_b); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Pad: pad failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Pad: " + std::string(e.what())); - } - return Status::OK(); -} - -static Status RotateAngleWithOutMirror(const std::shared_ptr &input, std::shared_ptr *output, - const uint64_t orientation) { - try { - int height = 0; - int width = 0; - double M[6] = {}; - - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - - // The 2D affine transformation matrix consists of 6 parameters (a, b, c, d, e, f) - // 0, 1, 2, 3, 4, 5 is the 6 parameters - if (orientation == 3) { - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - M[0] = -1.0f; - M[1] = 0.0f; - M[2] = lite_mat_rgb.width_ - 1; - M[3] = 0.0f; - M[4] = -1.0f; - M[5] = lite_mat_rgb.height_ - 1; - } else if (orientation == 6) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = -1.0f; - M[2] = lite_mat_rgb.height_ - 1; - M[3] = 1.0f; - M[4] = 0.0f; - M[5] = 0.0f; - } else if (orientation == 8) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = 1.0f; - M[2] = 0.0f; - M[3] = -1.0f; - M[4] = 0.0f; - M[5] = static_cast(lite_mat_rgb.width_) - 1.0f; - } else { - } - - std::vector dsize; - dsize.push_back(width); - dsize.push_back(height); - LiteMat lite_mat_affine; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_affine.IsEmpty(), "Rotate: Init image tensor failed, return empty tensor."); - - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); - } - return Status::OK(); -} - -static Status RotateAngleWithMirror(const std::shared_ptr &input, std::shared_ptr *output, - const uint64_t orientation) { - try { - int height = 0; - int width = 0; - double M[6] = {}; - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - - // The 2D affine transformation matrix consists of 6 parameters (a, b, c, d, e, f) - // 0, 1, 2, 3, 4, 5 is the 6 parameters - if (orientation == 2) { - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - M[0] = -1.0f; - M[1] = 0.0f; - M[2] = lite_mat_rgb.width_ - 1; - M[3] = 0.0f; - M[4] = 1.0f; - M[5] = 0.0f; - } else if (orientation == 5) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = 1.0f; - M[2] = 0.0f; - M[3] = 1.0f; - M[4] = 0.0f; - M[5] = 0.0f; - } else if (orientation == 7) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = -1.0f; - M[2] = lite_mat_rgb.height_ - 1; - M[3] = -1.0f; - M[4] = 0.0f; - M[5] = lite_mat_rgb.width_ - 1; - } else if (orientation == 4) { - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - M[0] = 1.0f; - M[1] = 0.0f; - M[2] = 0.0f; - M[3] = 0.0f; - M[4] = -1.0f; - M[5] = lite_mat_rgb.height_ - 1; - } else { - } - std::vector dsize; - dsize.push_back(width); - dsize.push_back(height); - LiteMat lite_mat_affine; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_affine.IsEmpty(), "Rotate: Init image tensor failed, return empty tensor."); - - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); - } - return Status::OK(); -} - -static bool IsMirror(int orientation) { - if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) { - return true; - } - return false; -} -// rotate the image by EXIF orientation -Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, const uint64_t orientation) { - if (input->Rank() != hw_shape && input->Rank() != hwc_rank) { - RETURN_STATUS_UNEXPECTED("Rotate: input image is not in shape of or "); - } - - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("Rotate: image datatype is not float32 or uint8."); - } - - if (!IsMirror(static_cast(orientation))) { - return RotateAngleWithOutMirror(input, output, orientation); - } else { - return RotateAngleWithMirror(input, output, orientation); - } -} - -Status GetAffineMatrix(const std::shared_ptr &input, std::vector *matrix, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear) { - CHECK_FAIL_RETURN_UNEXPECTED(translation.size() >= 2, "AffineOp::Compute translation_ size should >= 2"); - float_t translation_x = translation[0]; - float_t translation_y = translation[1]; - float_t degrees_tmp = 0.0; - RETURN_IF_NOT_OK(DegreesToRadians(degrees, °rees_tmp)); - float_t shear_x = shear[0]; - float_t shear_y = shear[1]; - RETURN_IF_NOT_OK(DegreesToRadians(shear_x, &shear_x)); - RETURN_IF_NOT_OK(DegreesToRadians(-1 * shear_y, &shear_y)); - - // Apply Affine Transformation - // T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] - // C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1] - // RSS is rotation with scale and shear matrix - // RSS(a, s, (sx, sy)) = - // = R(a) * S(s) * SHy(sy) * SHx(sx) - // = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] - // [ s*sin(a - sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] - // [ 0 , 0 , 1 ] - // - // where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: - // SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] - // [0, 1 ] [-tan(s), 1] - // - // Thus, the affine matrix is M = T * C * RSS * C^-1 - - // image is hwc, rows = shape()[0] - float_t cx = (static_cast(input->shape()[1]) - 1.0F) / 2.0F; - float_t cy = (static_cast(input->shape()[0]) - 1.0F) / 2.0F; - - CHECK_FAIL_RETURN_UNEXPECTED(cos(shear_y) != 0.0, "AffineOp: cos(shear_y) should not be zero."); - - // Calculate RSS - *matrix = std::vector{ - static_cast(scale * cos(degrees_tmp + shear_y) / cos(shear_y)), - static_cast(scale * (-1 * cos(degrees_tmp + shear_y) * tan(shear_x) / cos(shear_y) - sin(degrees_tmp))), - 0, - static_cast(scale * sin(degrees_tmp + shear_y) / cos(shear_y)), - static_cast(scale * (-1 * sin(degrees_tmp + shear_y) * tan(shear_x) / cos(shear_y) + cos(degrees_tmp))), - 0}; - // Compute T * C * RSS * C^-1 - // Compute T * C * RSS * C^-1 - (*matrix)[2] = (1 - (*matrix)[0]) * cx - (*matrix)[1] * cy + translation_x; - (*matrix)[5] = (1 - (*matrix)[4]) * cy - (*matrix)[3] * cx + translation_y; - return Status::OK(); -} - -Status Affine(const std::shared_ptr &input, std::shared_ptr *output, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value) { - try { - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be 3."); - if (interpolation != InterpolationMode::kLinear) { - MS_LOG(WARNING) << "Only Bilinear interpolation supported for now"; - } - std::vector matrix; - RETURN_IF_NOT_OK(GetAffineMatrix(input, &matrix, degrees, translation, scale, shear)); - int height = 0; - int width = 0; - CHECK_FAIL_RETURN_UNEXPECTED(matrix.size() <= 6, "Invalid mat shape."); - double M[6] = {}; - for (size_t i = 0; i < matrix.size(); i++) { - M[i] = static_cast(matrix[i]); - } - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_rgb(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - std::vector dsize; - dsize.push_back(width); - dsize.push_back(height); - LiteMat lite_mat_affine; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_affine.IsEmpty(), "Affine: Init image tensor failed, return empty tensor."); - - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, - UINT8_C3(fill_value[kRIndex], fill_value[kGIndex], fill_value[kBIndex])); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Affine: affine failed."); - - *output = output_tensor; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); - } -} - -Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_x, - int32_t kernel_y, float sigma_x, float sigma_y) { - try { - LiteMat lite_mat_input; - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - if (input->Rank() == 3) { - if (input->shape()[2] != 1 && input->shape()[2] != 3) { - RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in channel of 1 or 3"); - } - lite_mat_input = LiteMat(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else if (input->Rank() == 2) { - lite_mat_input = - LiteMat(input_width, input_height, const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else { - RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in shape of or "); - } - - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - LiteMat lite_mat_output; - lite_mat_output.Init(lite_mat_input.width_, lite_mat_input.height_, lite_mat_input.channel_, - reinterpret_cast(buffer), GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_output.IsEmpty(), - "GaussianBlur: Init image tensor failed, return empty tensor."); - - bool ret = GaussianBlur(lite_mat_input, lite_mat_output, {kernel_x, kernel_y}, static_cast(sigma_x), - static_cast(sigma_y)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "GaussianBlur: GaussianBlur failed."); - *output = output_tensor; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("GaussianBlur: " + std::string(e.what())); - } -} - -Status ImageNumChannels(const std::shared_ptr &image, dsize_t *channels) { - if (image->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED( - "GetImageNumChannels: invalid parameter, image should have at least two dimensions, but got: " + - std::to_string(image->Rank())); - } else if (image->Rank() == kMinImageRank) { - *channels = 1; - } else { - *channels = image->shape()[-1]; - } - return Status::OK(); -} - -Status ValidateImage(const std::shared_ptr &image, const std::string &op_name, - const std::set &valid_dtype, const std::set &valid_rank, - const std::set &valid_channel) { - // Validate image dtype - if (!valid_dtype.empty()) { - auto dtype = image->type(); - if (valid_dtype.find(dtype.value()) == valid_dtype.end()) { - std::string err_msg = op_name + ": the data type of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in type of " + DataTypeSetToString(valid_dtype); - err_msg += ". But got type " + dtype.ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - // Validate image rank - auto rank = image->Rank(); - if (!valid_rank.empty()) { - if (valid_rank.find(rank) == valid_rank.end()) { - std::string err_msg = op_name + ": the dimension of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in dimension of " + NumberSetToString(valid_rank); - if (valid_rank == std::set({kMinImageRank, kDefaultImageRank})) { - err_msg += ", in shape of or "; - } else if (valid_rank == std::set({kMinImageRank})) { - err_msg += ", in shape of "; - } else if (valid_rank == std::set({kDefaultImageRank})) { - err_msg += ", in shape of "; - } - err_msg += ". But got dimension " + std::to_string(rank) + "."; - if (rank == 1) { - err_msg += " You may need to perform Decode first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - } else { - if (rank < kMinImageRank) { - std::string err_msg = - op_name + ": the image tensor should have at least two dimensions. You may need to perform Decode first."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - // Validate image channel - if (!valid_channel.empty()) { - dsize_t channel = 1; - RETURN_IF_NOT_OK(ImageNumChannels(image, &channel)); - if (valid_channel.find(channel) == valid_channel.end()) { - std::string err_msg = op_name + ": the channel of image tensor does not match the requirement of operator."; - err_msg += " Expecting tensor in channel of " + NumberSetToString(valid_channel); - err_msg += ". But got channel " + std::to_string(channel) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - return Status::OK(); -} - -Status ImageSize(const std::shared_ptr &image, std::vector *size) { - RETURN_UNEXPECTED_IF_NULL(size); - *size = std::vector(kMinImageRank); - if (image->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED("GetImageSize: invalid parameter, image should have at least two dimensions, but got: " + - std::to_string(image->Rank())); - } else if (image->Rank() == kMinImageRank) { - (*size)[0] = image->shape()[0]; - (*size)[1] = image->shape()[1]; - } else { - const int32_t kHeightIndex = -3; - const int32_t kWidthIndex = -2; - (*size)[0] = image->shape()[kHeightIndex]; - (*size)[1] = image->shape()[kWidthIndex]; - } - return Status::OK(); -} - -Status ValidateImageRank(const std::string &op_name, int32_t rank) { - if (rank != 2 && rank != 3) { - std::string err_msg = op_name + ": image shape is not or , but got rank:" + std::to_string(rank); - if (rank == 1) { - err_msg = err_msg + ", may need to do Decode operation first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status HwcToChw(const std::shared_ptr &input, std::shared_ptr *output) { - try { - if (input->Rank() <= 3) { - int input_height = static_cast(input->shape()[0]); - int input_width = static_cast(input->shape()[1]); - int input_channel = static_cast(input->shape()[2]); - LiteMat lite_mat_hwc(input_width, input_height, input_channel, - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_chw; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({input_channel, input_height, input_width}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_chw.Init(input_height, input_channel, input_width, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - CHECK_FAIL_RETURN_UNEXPECTED(!lite_mat_chw.IsEmpty(), "HwcToChw: Init image tensor failed, return empty tensor."); - - bool ret = HWC2CHW(lite_mat_hwc, lite_mat_chw); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "HwcToChw: HwcToChw failed."); - *output = output_tensor; - } else { - RETURN_STATUS_UNEXPECTED("HwcToChw: input image is not in shape of or "); - } - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("HwcToChw: " + std::string(e.what())); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h b/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h deleted file mode 100644 index 238d0c3fb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_LITE_IMAGE_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_LITE_IMAGE_UTILS_H_ - -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#endif - -#include "./jpeglib.h" -#include "./jerror.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/lite_cv/image_process.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -constexpr dsize_t kChannelIndexHWC = 2; // images are hwc, so index 2 represents number of channels -constexpr dsize_t kChannelIndexCHW = 0; // images are chw, so index 0 represents number of channels -constexpr int32_t kMaxBitValue = 255; // max bit value after decode is 256 -constexpr dsize_t kMinImageChannel = 1; // image ops support minimum of 1 channel -constexpr dsize_t kDefaultImageChannel = 3; // images are 3 channels in general -constexpr dsize_t kMaxImageChannel = 4; // image ops support maximum of 4 channel -constexpr float kHalf = 0.5; // to get the half of a value -constexpr dsize_t kMinJpegQuality = 1; // the minimum quality for JPEG -constexpr dsize_t kMaxJpegQuality = 100; // the maximum quality for JPEG -constexpr dsize_t kDefaultImageRank = 3; // images are hwc channels in general -constexpr dsize_t kMinImageRank = 2; // images have at least 2 dimensions -constexpr int32_t kMaxPixelValue = 255; -constexpr dsize_t kHeightIndex = 0; // index of height of HWC images -constexpr dsize_t kWidthIndex = 1; // index of width of HWC images -constexpr dsize_t kRIndex = 0; // index of red channel in RGB format -constexpr dsize_t kGIndex = 1; // index of green channel in RGB format -constexpr dsize_t kBIndex = 2; // index of blue channel in RGB format - -void JpegErrorExitCustom(j_common_ptr cinfo); - -struct JpegErrorManagerCustom { - // "public" fields - struct jpeg_error_mgr pub; - // for return to caller - jmp_buf setjmp_buffer; -}; - -#if defined(ENABLE_CLOUD_FUSION_INFERENCE) -bool IsNonEmptyPNG(const std::shared_ptr &input); - -/// \brief Returns Rescaled image -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param rescale: rescale parameter -/// \param shift: shift parameter -/// \param output: Rescaled image Tensor of same input shape and type DE_FLOAT32 -Status Rescale(const std::shared_ptr &input, std::shared_ptr *output, float rescale, float shift); - -/// \brief Swap the red and blue pixels (RGB <-> BGR) -/// \param input: Tensor of shape and any OpenCv compatible type, see CVTensor. -/// \param output: Swapped image of same shape and type -Status SwapRedAndBlue(std::shared_ptr input, std::shared_ptr *output); -#endif - -bool IsNonEmptyJPEG(const std::shared_ptr &input); - -void JpegSetSource(j_decompress_ptr c_info, const void *data, int64_t data_size); - -Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int x = 0, int y = 0, - int w = 0, int h = 0); - -/// \brief Returns cropped ROI of an image -/// \param[in] input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param[in] x Starting horizontal position of ROI -/// \param[in] y Starting vertical position of ROI -/// \param[in] w Width of the ROI -/// \param[in] h Height of the ROI -/// \param[out] output: Cropped image Tensor of shape or and same input type. -Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h); - -/// \brief Returns Decoded image -/// Supported images: -/// BMP JPEG JPG PNG TIFF -/// supported by opencv, if user need more image analysis capabilities, please compile opencv particularlly. -/// \param[in] input CVTensor containing the not decoded image 1D bytes -/// \param[out] output Decoded image Tensor of shape and type DE_UINT8. Pixel order is RGB -Status Decode(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Get jpeg image width and height -/// \param[in] input CVTensor containing the not decoded image 1D bytes -/// \param[in] img_width The jpeg image width -/// \param[in] img_height The jpeg image height -Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height); - -/// \brief Returns Normalized image -/// \param[in] input Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. -/// \param[in] mean Tensor of shape <3> and type DE_FLOAT32 which are mean of each channel in RGB order -/// \param[in] std Tensor of shape <3> and type DE_FLOAT32 which are std of each channel in RGB order -/// \param[out] output Normalized image Tensor of same input shape and type DE_FLOAT32 -Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &vec_mean, const std::vector &vec_std); - -/// \brief Returns Resized image. -/// \param[in] input -/// \param[in] output_height Height of output -/// \param[in] output_width Width of output -/// \param[in] fx Horizontal scale -/// \param[in] fy Vertical scale -/// \param[in] InterpolationMode The interpolation mode -/// \param[out] output Resized image of shape or -/// and same type as input -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx = 0.0, double fy = 0.0, - InterpolationMode mode = InterpolationMode::kLinear); - -/// \brief Returns Resized image. -/// \param[in] inputs input TensorRow -/// \param[in] height Height of output -/// \param[in] width Width of output -/// \param[in] img_orientation Angle method of image rotation -/// \param[out] outputs Resized image of shape and same type as input -Status ResizePreserve(const TensorRow &inputs, int32_t height, int32_t width, int32_t img_orientation, - TensorRow *outputs); - -/// \brief Take in a 3 channel image in RBG to BGR -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Take in a 3 channel image in RBG to GRAY -/// \param[in] input The input image -/// \param[out] output The output image -/// \return Status code -Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output); - -/// \brief Pads the input image and puts the padded image in the output -/// \param[in] input: input Tensor -/// \param[out] output: padded Tensor -/// \param[in] pad_top Amount of padding done in top -/// \param[in] pad_bottom Amount of padding done in bottom -/// \param[in] pad_left Amount of padding done in left -/// \param[in] pad_right Amount of padding done in right -/// \param[in] border_types The interpolation to be done in the border -/// \param[in] fill_r Red fill value for pad -/// \param[in] fill_g Green fill value for pad -/// \param[in] fill_b Blue fill value for pad -Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, - const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, - uint8_t fill_r = 0, uint8_t fill_g = 0, uint8_t fill_b = 0); - -/// \brief Rotate the input image by orientation -/// \param[in] input Input Tensor -/// \param[out] output Rotated Tensor -/// \param[in] orientation The orientation of EXIF -Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, uint64_t orientation); - -/// \brief Get an affine matrix that applies affine transformation -/// \param[in] input Input Tensor -/// \param[in] matrix The transformation matrix -/// \param[in] degrees Range of the rotation degrees -/// \param[in] translation The horizontal and vertical translations -/// \param[in] scale The scaling factor -/// \param[in] shear The shear angle -Status GetAffineMatrix(const std::shared_ptr &input, std::vector *matrix, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear); - -/// \brief Geometrically transform the input image -/// \param[in] input Input Tensor -/// \param[out] output Transformed Tensor -/// \param[in] degrees Range of the rotation degrees -/// \param[in] translation The horizontal and vertical translations -/// \param[in] scale The scaling factor -/// \param[in] shear The shear angle -/// \param[in] interpolation The interpolation mode -/// \param[in] fill_value Fill value for pad -Status Affine(const std::shared_ptr &input, std::shared_ptr *output, float_t degrees, - const std::vector &translation, float_t scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value); - -/// \brief Filter the input image with a Gaussian kernel -/// \param[in] input Input Tensor -/// \param[out] output Transformed Tensor -/// \param[in] kernel_size_x Gaussian kernel size of width -/// \param[in] kernel_size_y Gaussian kernel size of height -/// \param[in] sigma_x Gaussian kernel standard deviation of width -/// \param[in] sigma_y Gaussian kernel standard deviation of height -Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_size_x, - int32_t kernel_size_y, float sigma_x, float sigma_y); - -/// \brief Get the size of input image. -/// \param[in] image Tensor of the image. -/// \param[out] size Size of the image as [height, width]. -/// \return The status code. -Status ImageSize(const std::shared_ptr &image, std::vector *size); - -/// \brief Validate image Dtype, rank and channel. -/// \param[in] image Image tensor to be validated. -/// \param[in] op_name operator name. -/// \param[in] valid_dtype Valid date type of the image tensor. Default: {}, means not to check date type. -/// \param[in] valid_rank Valid dimension of the image tensor. Default: {}, means not to check dimension. -/// \param[in] valid_channel Valid channel of the image tensor. Default: {}, means not to check channel. -Status ValidateImage(const std::shared_ptr &image, const std::string &op_name, - const std::set &valid_dtype = {}, const std::set &valid_rank = {}, - const std::set &valid_channel = {}); - -/// \brief Validate image rank. -/// \param[in] op_name operator name. -/// \param[in] rank refers to the rank of input image shape. -Status ValidateImageRank(const std::string &op_name, int32_t rank); - -/// \brief Swaps the channels in the image, i.e. converts HWC to CHW -/// \param input: Tensor of shape or and any OpenCv compatible type, see CVTensor. -/// \param output: Tensor of shape or and same input type. -Status HwcToChw(const std::shared_ptr &input, std::shared_ptr *output); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_LITE_IMAGE_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/math_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/math_utils.cc deleted file mode 100644 index a944dab1e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/math_utils.cc +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" - -#include -#include -#include - -namespace mindspore { -namespace dataset { -Status ComputeUpperAndLowerPercentiles(std::vector *hist, int32_t hi_p, int32_t low_p, int32_t *hi, - int32_t *lo) { - CHECK_FAIL_RETURN_UNEXPECTED(hist != nullptr, "hist is nullptr"); - CHECK_FAIL_RETURN_UNEXPECTED(hi != nullptr, "hi is nullptr"); - CHECK_FAIL_RETURN_UNEXPECTED(lo != nullptr, "lo is nullptr"); - try { - int32_t n = std::accumulate(hist->begin(), hist->end(), 0); - constexpr float kMaxPerc = 100.0; - auto cut = static_cast(static_cast(low_p) / kMaxPerc * static_cast(n)); - for (int32_t lb = 0; lb < static_cast(hist->size()) && cut > 0; lb++) { - if (cut > (*hist)[lb]) { - cut -= (*hist)[lb]; - (*hist)[lb] = 0; - } else { - (*hist)[lb] -= cut; - cut = 0; - } - } - cut = static_cast(static_cast(hi_p) / kMaxPerc * static_cast(n)); - for (auto ub_iter = hist->end() - 1; ub_iter >= hist->begin() && cut > 0; ub_iter--) { - if (cut > *ub_iter) { - cut -= *ub_iter; - *ub_iter = 0; - } else { - *ub_iter -= cut; - cut = 0; - } - } - *lo = 0; - *hi = static_cast(hist->size()) - 1; - for (; (*lo) < (*hi) && !(*hist)[*lo]; (*lo)++) { - } - for (; (*hi) >= 0 && !(*hist)[*hi]; (*hi)--) { - } - } catch (const std::exception &e) { - std::string err_message = "AutoContrast: ComputeUpperAndLowerPercentiles failed: "; - err_message += e.what(); - RETURN_STATUS_UNEXPECTED(err_message); - } - return Status::OK(); -} - -Status DegreesToRadians(float_t degrees, float_t *radians_target) { - CHECK_FAIL_RETURN_UNEXPECTED(radians_target != nullptr, "radians_target is nullptr"); - *radians_target = CV_PI * degrees / 180.0; - return Status::OK(); -} - -Status GenerateRealNumber(float_t a, float_t b, std::mt19937 *rnd, float_t *result) { - CHECK_FAIL_RETURN_UNEXPECTED(rnd != nullptr, "rnd is nullptr"); - CHECK_FAIL_RETURN_UNEXPECTED(result != nullptr, "result is nullptr"); - try { - std::uniform_real_distribution distribution{a, b}; - *result = distribution(*rnd); - } catch (const std::exception &e) { - std::string err_message = "RandomAffine: GenerateRealNumber failed: "; - err_message += e.what(); - RETURN_STATUS_UNEXPECTED(err_message); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/math_utils.h b/mindspore-lite/minddata/dataset/kernels/image/math_utils.h deleted file mode 100644 index 116cf1121..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/math_utils.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MATH_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MATH_UTILS_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" - -#define CV_PI 3.1415926535897932384626433832795 - -namespace mindspore { -namespace dataset { -/// \brief Returns lower and upper pth percentiles of the input histogram. -/// \param[in] hist: Input histogram (mutates the histogram for computation purposes) -/// \param[in] hi_p: Right side percentile -/// \param[in] low_p: Left side percentile -/// \param[out] hi: Value at high end percentile -/// \param[out] lo: Value at low end percentile -Status ComputeUpperAndLowerPercentiles(std::vector *hist, int32_t hi_p, int32_t low_p, int32_t *hi, - int32_t *lo); - -/// \brief Converts degrees input to radians. -/// \param[in] degrees: Input degrees -/// \param[out] radians_target: Radians output -Status DegreesToRadians(float_t degrees, float_t *radians_target); - -/// \brief Generates a random real number in [a,b). -/// \param[in] a: Start of range -/// \param[in] b: End of range -/// \param[in] rnd: Random device -/// \param[out] result: Random number in range [a,b) -Status GenerateRealNumber(float_t a, float_t b, std::mt19937 *rnd, float_t *result); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MATH_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.cc b/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.cc deleted file mode 100644 index dd564f6ea..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.cc +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -constexpr size_t kExpectedImageShapeSize = 4; -constexpr size_t kMaxLabelShapeSize = 3; -constexpr size_t kMinLabelShapeSize = 2; -constexpr size_t dimension_one = 1; -constexpr size_t dimension_two = 2; -constexpr size_t dimension_three = 3; -constexpr int64_t value_one = 1; -constexpr int64_t value_three = 3; - -MixUpBatchOp::MixUpBatchOp(float alpha) : alpha_(alpha) {} - -Status MixUpBatchOp::ComputeLabels(const std::shared_ptr &label, std::shared_ptr *out_labels, - std::vector *rand_indx, const std::vector &label_shape, float lam, - size_t images_size) { - CHECK_FAIL_RETURN_UNEXPECTED( - images_size <= static_cast(std::numeric_limits::max()), - "The \'images_size\' must not be more than \'INT64_MAX\', but got: " + std::to_string(images_size)); - for (int64_t i = 0; i < static_cast(images_size); i++) { - rand_indx->push_back(i); - } - std::shuffle(rand_indx->begin(), rand_indx->end(), random_generator_); - - std::shared_ptr float_label; - RETURN_IF_NOT_OK(TypeCast(label, &float_label, DataType(DataType::DE_FLOAT32))); - RETURN_IF_NOT_OK(TypeCast(label, out_labels, DataType(DataType::DE_FLOAT32))); - - int64_t row_labels = label_shape.size() == kMaxLabelShapeSize ? label_shape[1] : 1; - int64_t num_classes = label_shape.size() == kMaxLabelShapeSize ? label_shape[dimension_two] : label_shape[1]; - - for (int64_t i = 0; i < label_shape[0]; i++) { - for (int64_t j = 0; j < row_labels; j++) { - for (int64_t k = 0; k < num_classes; k++) { - std::vector first_index = - label_shape.size() == kMaxLabelShapeSize ? std::vector{i, j, k} : std::vector{i, k}; - std::vector second_index = label_shape.size() == kMaxLabelShapeSize - ? std::vector{(*rand_indx)[static_cast(i)], j, k} - : std::vector{(*rand_indx)[static_cast(i)], k}; - float first_value; - float second_value; - RETURN_IF_NOT_OK(float_label->GetItemAt(&first_value, first_index)); - RETURN_IF_NOT_OK(float_label->GetItemAt(&second_value, second_index)); - RETURN_IF_NOT_OK((*out_labels)->SetItemAt(first_index, lam * first_value + (1 - lam) * second_value)); - } - } - } - return Status::OK(); -} - -Status MixUpBatchOp::Compute(const TensorRow &input, TensorRow *output) { - constexpr int64_t input_size = 2; - if (input.size() < input_size) { - RETURN_STATUS_UNEXPECTED("MixUpBatch: size of input data should be 2 (including images or labels), but got: " + - std::to_string(input.size()) + ", check 'input_columns' when call this operator."); - } - - std::vector> images; - std::vector image_shape = input.at(0)->shape().AsVector(); - std::vector label_shape = input.at(1)->shape().AsVector(); - - // Check inputs - if (image_shape.size() != kExpectedImageShapeSize || image_shape[0] != label_shape[0]) { - RETURN_STATUS_UNEXPECTED("MixUpBatch: rank of image shape should be: " + std::to_string(kExpectedImageShapeSize) + - ", but got: " + std::to_string(image_shape.size()) + - ", make sure image shape are or and batched before calling MixUpBatch."); - } - - CHECK_FAIL_RETURN_UNEXPECTED(input.at(1)->type().IsNumeric(), - "MixUpBatch: invalid label type, label must be in a numeric type, but got: " + - input.at(1)->type().ToString() + ". You may need to perform OneHot first."); - if (label_shape.size() != kMinLabelShapeSize && label_shape.size() != kMaxLabelShapeSize) { - RETURN_STATUS_UNEXPECTED( - "MixUpBatch: wrong labels shape. " - "The second column (labels) must have a shape of NC or NLC where N is the batch size, " - "L is the number of labels in each row, and C is the number of classes. " - "labels must be in one-hot format and in a batch, but got rank: " + - std::to_string(label_shape.size())); - } - if ((image_shape[dimension_one] != value_one && image_shape[dimension_one] != value_three) && - (image_shape[dimension_three] != value_one && image_shape[dimension_three] != value_three)) { - RETURN_STATUS_UNEXPECTED("MixUpBatch: images shape should in or , got shape:" + - input.at(0)->shape().ToString()); - } - - // Move images into a vector of CVTensors - RETURN_IF_NOT_OK(BatchTensorToCVTensorVector(input.at(0), &images)); - - // Calculating lambda - // If x1 is a random variable from Gamma(a1, 1) and x2 is a random variable from Gamma(a2, 1) - // then x = x1 / (x1+x2) is a random variable from Beta(a1, a2) - std::gamma_distribution distribution1(alpha_, 1); - std::gamma_distribution distribution2(alpha_, 1); - float x1 = distribution1(random_generator_); - float x2 = distribution2(random_generator_); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x1) > x2, - "multiplication out of bounds, with multipliers: " + std::to_string(x1) + " and " + - std::to_string(x2) + - ", which result in the out of bounds product:" + std::to_string(x1 * x2)); - CHECK_FAIL_RETURN_UNEXPECTED(std::fabs(x1 + x2) > std::numeric_limits::epsilon(), - "addition of variable(x1 and x2) of Gamma should not be 0."); - float lam = x1 / (x1 + x2); - - // Calculate random labels - std::vector rand_indx; - std::shared_ptr out_labels; - - // Compute labels - RETURN_IF_NOT_OK(ComputeLabels(input.at(1), &out_labels, &rand_indx, label_shape, lam, images.size())); - - // Compute images - for (int64_t i = 0; i < images.size(); i++) { - TensorShape remaining({-1}); - uchar *start_addr_of_index = nullptr; - std::shared_ptr out; - RETURN_IF_NOT_OK(input.at(0)->StartAddrOfIndex({rand_indx[i], 0, 0, 0}, &start_addr_of_index, &remaining)); - RETURN_IF_NOT_OK(input.at(0)->CreateFromMemory( - TensorShape({image_shape[dimension_one], image_shape[dimension_two], image_shape[dimension_three]}), - input.at(0)->type(), start_addr_of_index, &out)); - std::shared_ptr rand_image = CVTensor::AsCVTensor(std::move(out)); - if (!rand_image->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] MixUpBatch: allocate memory failed."); - } - images[i]->mat() = lam * images[i]->mat() + (1 - lam) * rand_image->mat(); - } - - // Move the output into a TensorRow - std::shared_ptr output_image; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(input.at(0)->shape(), input.at(0)->type(), &output_image)); - for (int64_t i = 0; i < images.size(); i++) { - RETURN_IF_NOT_OK(output_image->InsertTensor({i}, images[i])); - } - output->push_back(output_image); - output->push_back(out_labels); - - return Status::OK(); -} - -void MixUpBatchOp::Print(std::ostream &out) const { - out << "MixUpBatchOp: " - << "alpha: " << alpha_ << "\n"; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h b/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h deleted file mode 100644 index e00df80ac..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MIXUP_BATCH_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MIXUP_BATCH_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class MixUpBatchOp : public RandomTensorOp { - public: - explicit MixUpBatchOp(float alpha); - - ~MixUpBatchOp() override = default; - - void Print(std::ostream &out) const override; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kMixUpBatchOp; } - - private: - // a helper function to shorten the main Compute function - Status ComputeLabels(const std::shared_ptr &label, std::shared_ptr *out_labels, - std::vector *rand_indx, const std::vector &label_shape, float lam, - size_t images_size); - - float alpha_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_MIXUP_BATCH_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/normalize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/normalize_op.cc deleted file mode 100644 index 9212bd2ce..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/normalize_op.cc +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/normalize_op.h" - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -NormalizeOp::NormalizeOp(std::vector mean, std::vector std, bool is_hwc) - : mean_(std::move(mean)), std_(std::move(std)), is_hwc_(is_hwc) {} - -Status NormalizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // Doing the Normalization - auto input_shape = input->shape(); - dsize_t rank = input_shape.Rank(); - if (rank < kMinImageRank) { - std::string err_msg = "Normalize: input tensor should have at least 2 dimensions, but got: " + std::to_string(rank); - RETURN_STATUS_UNEXPECTED(err_msg); - } else if (rank <= kDefaultImageRank) { - // [H, W] or [H, W, C] -#ifndef ENABLE_ANDROID - return Normalize(input, output, mean_, std_, is_hwc_); -#else - return Normalize(input, output, mean_, std_); -#endif - } else { - // reshape [..., H, W, C] to [N, H, W, C] - dsize_t num_batch = input->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and normalize N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (const auto &input_hwc : input_vector_hwc) { - std::shared_ptr normalize; -#ifndef ENABLE_ANDROID - RETURN_IF_NOT_OK(Normalize(input_hwc, &normalize, mean_, std_, is_hwc_)); -#else - RETURN_IF_NOT_OK(Normalize(input_hwc, &normalize, mean_, std_)); -#endif - output_vector_hwc.push_back(normalize); - } - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, &(*output))); - RETURN_IF_NOT_OK((*output)->Reshape(input_shape)); - return Status::OK(); - } -} - -void NormalizeOp::Print(std::ostream &out) const { - out << "NormalizeOp, mean: "; - for (const auto &m : mean_) { - out << m << ", "; - } - out << "}" << std::endl << "std: "; - for (const auto &s : std_) { - out << s << ", "; - } - out << "}" << std::endl << "is_hwc: " << is_hwc_; - out << "}" << std::endl; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/normalize_op.h b/mindspore-lite/minddata/dataset/kernels/image/normalize_op.h deleted file mode 100644 index 5472a27c9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/normalize_op.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class NormalizeOp : public TensorOp { - public: - NormalizeOp(std::vector mean, std::vector std, bool is_hwc); - - ~NormalizeOp() override = default; - - void Print(std::ostream &out) const override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kNormalizeOp; } - - private: - std::vector mean_; - std::vector std_; - bool is_hwc_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.cc b/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.cc deleted file mode 100644 index 5ef81a5cb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -NormalizePadOp::NormalizePadOp(std::vector mean, std::vector std, std::string dtype, bool is_hwc) - : mean_(std::move(mean)), std_(std::move(std)), dtype_(std::move(dtype)), is_hwc_(is_hwc) {} - -Status NormalizePadOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // Doing the Normalization + pad - return NormalizePad(input, output, mean_, std_, dtype_, is_hwc_); -} - -void NormalizePadOp::Print(std::ostream &out) const { - out << "NormalizePadOp, mean: "; - for (const auto &m : mean_) { - out << m << ", "; - } - out << "}" << std::endl << "std: "; - for (const auto &s : std_) { - out << s << ", "; - } - out << "}" << std::endl << "is_hwc: " << is_hwc_; - out << "}" << std::endl; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h b/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h deleted file mode 100644 index 3c4af5632..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_PAD_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_PAD_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class NormalizePadOp : public TensorOp { - public: - NormalizePadOp(std::vector mean, std::vector std, std::string dtype = "float32", bool is_hwc = true); - - ~NormalizePadOp() override = default; - - void Print(std::ostream &out) const override; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kNormalizePadOp; } - - private: - std::vector mean_; - std::vector std_; - std::string dtype_; - bool is_hwc_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_NORMALIZE_PAD_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/pad_op.cc b/mindspore-lite/minddata/dataset/kernels/image/pad_op.cc deleted file mode 100644 index a8e7301a0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/pad_op.cc +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/pad_op.h" - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const BorderType PadOp::kDefBorderType = BorderType::kConstant; -const uint8_t PadOp::kDefFillR = 0; -const uint8_t PadOp::kDefFillG = 0; -const uint8_t PadOp::kDefFillB = 0; - -PadOp::PadOp(int32_t pad_top, int32_t pad_bottom, int32_t pad_left, int32_t pad_right, BorderType padding_mode, - uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : pad_top_(pad_top), - pad_bottom_(pad_bottom), - pad_left_(pad_left), - pad_right_(pad_right), - boarder_type_(padding_mode), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} - -Status PadOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Pad(input, output, pad_top_, pad_bottom_, pad_left_, pad_right_, boarder_type_, fill_r_, fill_g_, fill_b_); -} - -Status PadOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({-1, -1, 3}); // we don't know what is output image size, but we know it should be 3 channels - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Pad: inputs cannot be empty."); - if (inputs[0].Rank() == 1) { - outputs.emplace_back(out); - } - CHECK_FAIL_RETURN_UNEXPECTED( - !outputs.empty(), - "Pad: invalid input shape, expected 1D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/pad_op.h b/mindspore-lite/minddata/dataset/kernels/image/pad_op.h deleted file mode 100644 index 9ed549907..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/pad_op.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class PadOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const BorderType kDefBorderType; - static const uint8_t kDefFillR; - static const uint8_t kDefFillG; - static const uint8_t kDefFillB; - - // Constructor for PadOp. - // @param pad_top number of pixels to pad the top of image with. - // @param pad_bottom number of pixels to pad the bottom of the image with. - // @param pad_left number of pixels to pad the left of the image with. - // @param pad_right number of pixels to pad the right of the image with. - // @param padding_mode BorderType enum, the type of boarders that we are using. - // @param fill_r R value for the color to pad with. - // @param fill_g G value for the color to pad with. - // @param fill_b B value for the color to pad with. - PadOp(int32_t pad_top, int32_t pad_bottom, int32_t pad_left, int32_t pad_right, BorderType padding_mode, - uint8_t fill_r = kDefFillR, uint8_t fill_g = kDefFillG, uint8_t fill_b = kDefFillB); - - ~PadOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kPadOp; } - - private: - int32_t pad_top_; - int32_t pad_bottom_; - int32_t pad_left_; - int32_t pad_right_; - BorderType boarder_type_; - uint8_t fill_r_; - uint8_t fill_g_; - uint8_t fill_b_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.cc b/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.cc deleted file mode 100644 index 446ea0617..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -PadToSizeOp::PadToSizeOp(std::vector size, std::vector offset, std::vector fill_value, - BorderType padding_mode) - : size_(std::move(size)), - offset_(std::move(offset)), - fill_value_(std::move(fill_value)), - boarder_type_(padding_mode) {} - -template -std::string SizeToString(const std::vector &size) { - std::string init; - std::string err_msg = std::accumulate(size.begin(), size.end(), init, [](const std::string &str, T val) { - if (str.empty()) { - return std::to_string(val); - } else { - return str + ", " + std::to_string(val); - } - }); - return "(" + err_msg + ")"; -} - -Status PadToSizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "PadToSize", {1, 2, 3, 4, 5, 6, 10, 11, 12}, {2, 3}, {1, 3})); - std::vector image_size; - RETURN_IF_NOT_OK(ImageSize(input, &image_size)); - CHECK_FAIL_RETURN_SYNTAX_ERROR( - image_size[0] <= size_[0] && image_size[1] <= size_[1], - "PadToSize: the target size to pad should be no less than the original image size, but got target size " + - SizeToString(size_) + " and original size " + SizeToString(image_size) + "."); - int32_t pad_top, pad_bottom, pad_left, pad_right; - if (offset_.empty()) { - pad_top = static_cast(static_cast(size_[0] - image_size[0]) * kHalf); - pad_left = static_cast(static_cast(size_[1] - image_size[1]) * kHalf); - } else if (offset_.size() == 1) { - pad_top = offset_[0]; - pad_left = offset_[0]; - } else { - pad_top = offset_[0]; - pad_left = offset_[1]; - } - pad_bottom = size_[0] - static_cast(image_size[0]) - pad_top; - pad_right = size_[1] - static_cast(image_size[1]) - pad_left; - CHECK_FAIL_RETURN_SYNTAX_ERROR(pad_bottom >= 0 && pad_right >= 0, - "PadToSize: the sum of offset and original image size should be no more than the " - "target size to pad, but got offset " + - SizeToString(std::vector{pad_top, pad_left}) + " plus original size " + - SizeToString(image_size) + " bigger than " + SizeToString(size_)); - return Pad(input, output, pad_top, pad_bottom, pad_left, pad_right, boarder_type_, fill_value_[kRIndex], - fill_value_[kGIndex], fill_value_[kBIndex]); -} - -Status PadToSizeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out({size_[0], size_[1]}); - if (inputs[0].Rank() == kMinImageRank) { - outputs = {out}; - } else if (inputs[0].Rank() == kDefaultImageRank) { - outputs = {out.AppendDim(kDefaultImageChannel)}; - } else { - RETURN_STATUS_UNEXPECTED("PadToSize: input tensor should be in shape of or , but got dimension: " + - std::to_string(inputs[0].Rank())); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h b/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h deleted file mode 100644 index 70166ccca..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_TO_SIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_TO_SIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class PadToSizeOp : public TensorOp { - public: - PadToSizeOp(std::vector size, std::vector offset, std::vector fill_value, - BorderType padding_mode); - - ~PadToSizeOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kPadToSizeOp; } - - private: - std::vector size_; - std::vector offset_; - std::vector fill_value_; - BorderType boarder_type_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PAD_TO_SIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/perspective_op.cc b/mindspore-lite/minddata/dataset/kernels/image/perspective_op.cc deleted file mode 100644 index 84d3b4855..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/perspective_op.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/perspective_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -PerspectiveOp::PerspectiveOp(const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation) - : start_points_(start_points), end_points_(end_points), interpolation_(interpolation) {} - -Status PerspectiveOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Perspective(input, output, start_points_, end_points_, interpolation_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/perspective_op.h b/mindspore-lite/minddata/dataset/kernels/image/perspective_op.h deleted file mode 100644 index 104b25e58..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/perspective_op.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PERSPECTIVE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PERSPECTIVE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class PerspectiveOp : public TensorOp { - public: - /// Constructor - PerspectiveOp(const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation); - - ~PerspectiveOp() override = default; - - std::string Name() const override { return kPerspectiveOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - protected: - std::vector> start_points_; - std::vector> end_points_; - InterpolationMode interpolation_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_PERSPECTIVE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/posterize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/posterize_op.cc deleted file mode 100644 index f9c6587a8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/posterize_op.cc +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/posterize_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -PosterizeOp::PosterizeOp(uint8_t bit) : bit_(bit) {} - -Status PosterizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Posterize(input, output, bit_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/posterize_op.h b/mindspore-lite/minddata/dataset/kernels/image/posterize_op.h deleted file mode 100644 index a62a1549c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/posterize_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_POSTERIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_POSTERIZE_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class PosterizeOp : public TensorOp { - public: - /// \brief Constructor - /// \param[in] bit: bits to use - explicit PosterizeOp(uint8_t bit); - - ~PosterizeOp() override = default; - - std::string Name() const override { return kPosterizeOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - private: - uint8_t bit_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_POSTERIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.cc deleted file mode 100644 index a1736dbeb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -RandAugmentOp::RandAugmentOp(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, - InterpolationMode interpolation, std::vector fill_value) - : num_ops_(num_ops), - magnitude_(magnitude), - num_magnitude_bins_(num_magnitude_bins), - interpolation_(interpolation), - fill_value_(std::move(fill_value)) {} - -Status RandAugmentOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - // Input correctness judgment - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "RandAugment", {3}, {3}, {3})); - - std::vector image_size; - RETURN_IF_NOT_OK(ImageSize(input, &image_size)); - std::shared_ptr img = input; - Space space = GetSpace(num_magnitude_bins_, image_size); - auto space_size = static_cast(space.size()); - std::vector op_name_list; - std::for_each(space.begin(), space.end(), - [&op_name_list](const std::map, bool>>::value_type &p) { - op_name_list.push_back(p.first); - }); - - for (int i = 0; i < num_ops_; ++i) { - int32_t op_index = RandInt(0, space_size); - std::string op_name = op_name_list[op_index]; - std::vector magnitudes = std::get<0>(space[op_name]); - bool sign = std::get<1>(space[op_name]); - float magnitude = 0.0; - if (magnitudes.size() != 1) { - magnitude = magnitudes[magnitude_]; - } - const int kRandUpperBound = 2; - int32_t random_number = RandInt(0, kRandUpperBound); - if (sign && random_number) { - magnitude *= -1.0; - } - RETURN_IF_NOT_OK(ApplyAugment(img, &img, op_name, magnitude, interpolation_, fill_value_)); - } - *output = img; - return Status::OK(); -} - -Space RandAugmentOp::GetSpace(int32_t num_bins, const std::vector &image_size) { - Space space = {{"Identity", {{0}, false}}, - {"ShearX", {Linspace(0.0, 0.3, num_bins), true}}, - {"ShearY", {Linspace(0.0, 0.3, num_bins), true}}, - {"TranslateX", {Linspace(0.0, 150.0F / 331 * static_cast(image_size[1]), num_bins), true}}, - {"TranslateY", {Linspace(0.0, 150.0F / 331 * static_cast(image_size[0]), num_bins), true}}, - {"Rotate", {Linspace(0.0, 30, num_bins), true}}, - {"Brightness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Color", {Linspace(0.0, 0.9, num_bins), true}}, - {"Contrast", {Linspace(0.0, 0.9, num_bins), true}}, - {"Sharpness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Posterize", - {Linspace(0.0, static_cast(num_bins) - 1.F, num_bins, - -4.0F / (static_cast(num_bins) - 1.f), 8, true), - false}}, - {"Solarize", {Linspace(255.0, 0.0, num_bins), false}}, - {"AutoContrast", {{0}, false}}, - {"Equalize", {{0}, false}}}; - return space; -} - -int32_t RandAugmentOp::RandInt(int32_t low, int32_t high) { - std::uniform_int_distribution dis(low, high); - return dis(random_generator_) % (high - low) + low; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h b/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h deleted file mode 100644 index 73c53c7c9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RAND_AUGMENT_OP_H_ -#define MINDSPORE_MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RAND_AUGMENT_OP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -typedef std::map, bool>> Space; - -namespace mindspore { -namespace dataset { -class RandAugmentOp : public RandomTensorOp { - public: - RandAugmentOp(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, InterpolationMode interpolation, - std::vector fill_value); - - ~RandAugmentOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandAugmentOp; } - - private: - static Space GetSpace(int32_t num_bins, const std::vector &image_size); - - int32_t RandInt(int32_t low, int32_t high); - - int num_ops_; - int magnitude_; - int num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RAND_AUGMENT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc deleted file mode 100644 index b021c1a56..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status RandomAdjustSharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - // Check input - if (input->Rank() != kMinImageRank && input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("RandomAdjustSharpness: image shape is not or , got rank: " + - std::to_string(input->Rank())); - } - CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, - "RandomAdjustSharpness: Cannot convert from OpenCV type, unknown CV type. Currently " - "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); - - if (distribution_(random_generator_)) { - return AdjustSharpness(input, output, degree_); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h deleted file mode 100644 index 1c531e4eb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ADJUST_SHARPNESS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ADJUST_SHARPNESS_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomAdjustSharpnessOp : public RandomTensorOp { - public: - RandomAdjustSharpnessOp(float degree, float prob) : degree_(degree), distribution_(prob) {} - - ~RandomAdjustSharpnessOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomAdjustSharpnessOp &so) { - so.Print(out); - return out; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomAdjustSharpnessOp; } - - private: - float degree_; - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ADJUST_SHARPNESS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.cc deleted file mode 100644 index fbcd7e1c6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h" - -#include -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -const std::vector RandomAffineOp::kDegreesRange = {0.0, 0.0}; -const std::vector RandomAffineOp::kTranslationPercentages = {0.0, 0.0, 0.0, 0.0}; -const std::vector RandomAffineOp::kScaleRange = {1.0, 1.0}; -const std::vector RandomAffineOp::kShearRanges = {0.0, 0.0, 0.0, 0.0}; -const InterpolationMode RandomAffineOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; -const std::vector RandomAffineOp::kFillValue = {0, 0, 0}; - -RandomAffineOp::RandomAffineOp(std::vector degrees, std::vector translate_range, - std::vector scale_range, std::vector shear_ranges, - InterpolationMode interpolation, std::vector fill_value) - : degrees_range_(std::move(degrees)), - translate_range_(std::move(translate_range)), - scale_range_(std::move(scale_range)), - shear_ranges_(std::move(shear_ranges)), - interpolation_(interpolation), - fill_value_(std::move(fill_value)) {} - -Status RandomAffineOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - CHECK_FAIL_RETURN_UNEXPECTED(translate_range_.size() == 4, "RandomAffine: the translate range size is not 4."); - CHECK_FAIL_RETURN_UNEXPECTED(degrees_range_.size() == 2, "RandomAffine: the degrees range size is not 2."); - CHECK_FAIL_RETURN_UNEXPECTED(scale_range_.size() == 2, "RandomAffine: the scale range size is not 2."); - CHECK_FAIL_RETURN_UNEXPECTED(shear_ranges_.size() == 4, "RandomAffine: the shear ranges size is not 4."); - - dsize_t height = input->shape()[0]; - dsize_t width = input->shape()[1]; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[0])) > width, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[1])) > width, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[2])) > height, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[3])) > height, - "RandomAffineOp: multiplication out of bounds."); - float_t min_dx = translate_range_[0] * static_cast(width); - float_t max_dx = translate_range_[1] * static_cast(width); - float_t min_dy = translate_range_[2] * static_cast(height); - float_t max_dy = translate_range_[3] * static_cast(height); - float_t degrees = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(degrees_range_[0], degrees_range_[1], &random_generator_, °rees)); - float_t translation_x = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(min_dx, max_dx, &random_generator_, &translation_x)); - float_t translation_y = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(min_dy, max_dy, &random_generator_, &translation_y)); - float_t scale = 1.0; - RETURN_IF_NOT_OK(GenerateRealNumber(scale_range_[0], scale_range_[1], &random_generator_, &scale)); - float_t shear_x = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[0], shear_ranges_[1], &random_generator_, &shear_x)); - float_t shear_y = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[2], shear_ranges_[3], &random_generator_, &shear_y)); - // assign to base class variables - degrees = fmod(degrees, 360.0F); - std::vector translation = {translation_x, translation_y}; - std::vector shear = {shear_x, shear_y}; - return Affine(input, output, degrees, translation, scale, shear, interpolation_, fill_value_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h deleted file mode 100644 index b2e70ef5e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AFFINE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AFFINE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomAffineOp : public RandomTensorOp { - public: - /// Default values, also used by python_bindings.cc - static const std::vector kDegreesRange; - static const std::vector kTranslationPercentages; - static const std::vector kScaleRange; - static const std::vector kShearRanges; - static const InterpolationMode kDefInterpolation; - static const std::vector kFillValue; - - explicit RandomAffineOp(std::vector degrees, std::vector translate_range = kTranslationPercentages, - std::vector scale_range = kScaleRange, - std::vector shear_ranges = kShearRanges, - InterpolationMode interpolation = kDefInterpolation, - std::vector fill_value = kFillValue); - - ~RandomAffineOp() override = default; - - std::string Name() const override { return kRandomAffineOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - private: - std::vector degrees_range_; // min_degree, max_degree - std::vector translate_range_; // maximum x translation percentage, maximum y translation percentage - std::vector scale_range_; // min_scale, max_scale - std::vector shear_ranges_; // min_x_shear, max_x_shear, min_y_shear, max_y_shear - InterpolationMode interpolation_; // interpolation - std::vector fill_value_; // fill_value -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AFFINE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.cc deleted file mode 100644 index 6e3909201..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status RandomAutoContrastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // Check input - if (input->Rank() != kMinImageRank && input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("RandomAutoContrast: image shape is not or , got rank: " + - std::to_string(input->Rank())); - } - if (input->Rank() == kDefaultImageRank) { - if (input->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED( - "RandomAutoContrast: image shape is incorrect, expected num of channels is 3, " - "but got: " + - std::to_string(input->shape()[kChannelIndexHWC])); - } - } - CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, - "RandomAutoContrast: Cannot convert from OpenCV type, unknown CV type. Currently " - "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); - if (distribution_(random_generator_)) { - return AutoContrast(input, output, cutoff_, ignore_); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h deleted file mode 100644 index 20aae6c68..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AUTO_CONTRAST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AUTO_CONTRAST_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomAutoContrastOp : public RandomTensorOp { - public: - RandomAutoContrastOp(float cutoff, const std::vector &ignore, float prob) - : cutoff_(cutoff), ignore_(ignore), distribution_(prob) {} - - ~RandomAutoContrastOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomAutoContrastOp &so) { - so.Print(out); - return out; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomAutoContrastOp; } - - private: - float cutoff_; - std::vector ignore_; - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_AUTO_CONTRAST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.cc deleted file mode 100644 index 32af5d40a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomColorAdjustOp::RandomColorAdjustOp(float s_bright_factor, float e_bright_factor, float s_contrast_factor, - float e_contrast_factor, float s_saturation_factor, float e_saturation_factor, - float s_hue_factor, float e_hue_factor) - : bright_factor_start_(s_bright_factor), - bright_factor_end_(e_bright_factor), - contrast_factor_start_(s_contrast_factor), - contrast_factor_end_(e_contrast_factor), - saturation_factor_start_(s_saturation_factor), - saturation_factor_end_(e_saturation_factor), - hue_factor_start_(s_hue_factor), - hue_factor_end_(e_hue_factor) {} - -Status RandomColorAdjustOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "RandomColorAdjust", {}, {3}, {3})); - - // randomly select an augmentation to apply to the input image until all the transformations run - std::vector params_vector = {"brightness", "contrast", "saturation", "hue"}; - - std::shuffle(params_vector.begin(), params_vector.end(), random_generator_); - - *output = std::static_pointer_cast(input); - // determine if certain augmentation needs to be executed: - for (const auto ¶m : params_vector) { - // case switch - if (param == "brightness") { - if (CmpFloat(bright_factor_start_, bright_factor_end_) && CmpFloat(bright_factor_start_, 1.0f)) { - MS_LOG(DEBUG) << "Not running brightness."; - } else { - // adjust the brightness of an image - float random_factor = - std::uniform_real_distribution(bright_factor_start_, bright_factor_end_)(random_generator_); - RETURN_IF_NOT_OK(AdjustBrightness(*output, output, random_factor)); - } - } else if (param == "contrast") { - if (CmpFloat(contrast_factor_start_, contrast_factor_end_) && CmpFloat(contrast_factor_start_, 1.0f)) { - MS_LOG(DEBUG) << "Not running contrast."; - } else { - float random_factor = - std::uniform_real_distribution(contrast_factor_start_, contrast_factor_end_)(random_generator_); - RETURN_IF_NOT_OK(AdjustContrast(*output, output, random_factor)); - } - } else if (param == "saturation") { - // adjust the Saturation of an image - if (CmpFloat(saturation_factor_start_, saturation_factor_end_) && CmpFloat(saturation_factor_start_, 1.0f)) { - MS_LOG(DEBUG) << "Not running saturation."; - } else { - float random_factor = - std::uniform_real_distribution(saturation_factor_start_, saturation_factor_end_)(random_generator_); - RETURN_IF_NOT_OK(AdjustSaturation(*output, output, random_factor)); - } - } else if (param == "hue") { - if (CmpFloat(hue_factor_start_, hue_factor_end_) && CmpFloat(hue_factor_start_, 0.0f)) { - MS_LOG(DEBUG) << "Not running hue."; - } else { - // adjust the Hue of an image - float random_factor = - std::uniform_real_distribution(hue_factor_start_, hue_factor_end_)(random_generator_); - RETURN_IF_NOT_OK(AdjustHue(*output, output, random_factor)); - } - } - } - // now after we do all the transformations, the last one is fine - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h deleted file mode 100644 index a256a23b6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_COLOR_ADJUST_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_COLOR_ADJUST_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomColorAdjustOp : public RandomTensorOp { - public: - // Constructor for RandomColorAdjustOp. - // @param s_bright_factor brightness change range start value. - // @param e_bright_factor brightness change range end value. - // @param s_contrast_factor contrast change range start value. - // @param e_contrast_factor contrast change range start value. - // @param s_saturation_factor saturation change range end value. - // @param e_saturation_factor saturation change range end value. - // @param s_hue_factor hue change factor start value, this should be greater than -0.5. - // @param e_hue_factor hue change factor start value, this should be less than 0.5. - // @param seed optional seed to pass in to the constructor. - // @details the randomly chosen degree is uniformly distributed. - RandomColorAdjustOp(float s_bright_factor, float e_bright_factor, float s_contrast_factor, float e_contrast_factor, - float s_saturation_factor, float e_saturation_factor, float s_hue_factor, float e_hue_factor); - - ~RandomColorAdjustOp() override = default; - - // Overrides the base class compute function. - // Calls multiple transform functions in ImageUtils, this function takes an input tensor. - // and transforms its data using openCV, the output memory is manipulated to contain the result. - // @return Status The status code returned. - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomColorAdjustOp; } - - private: - /// \brief Compare two floating point variables. Return true if they are same / very close. - static inline bool CmpFloat(const float &a, const float &b, float epsilon = 0.0000000001f) { - return (std::fabs(a - b) < epsilon); - } - - float bright_factor_start_; - float bright_factor_end_; - float contrast_factor_start_; - float contrast_factor_end_; - float saturation_factor_start_; - float saturation_factor_end_; - float hue_factor_start_; - float hue_factor_end_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_COLOR_ADJUST_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_color_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_color_op.cc deleted file mode 100644 index ecefa1e66..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_color_op.cc +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_color_op.h" - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -RandomColorOp::RandomColorOp(float t_lb, float t_ub) : dist_(t_lb, t_ub), t_lb_(t_lb), t_ub_(t_ub) {} - -Status RandomColorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (input->Rank() != kDefaultImageRank || input->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED( - "RandomColor: image shape is not or channel is not 3, got rank: " + std::to_string(input->Rank()) + - ", and channel: " + std::to_string(input->shape()[kChannelIndexHWC])); - } - // 0.5 pixel precision assuming an 8 bit image - const auto eps = 0.00195; - const auto t = dist_(random_generator_); - if (abs(t - 1.0) < eps) { - // Just return input? Can we do it given that input would otherwise get consumed in CVTensor constructor anyway? - *output = input; - return Status::OK(); - } - auto cvt_in = CVTensor::AsCVTensor(input); - auto m1 = cvt_in->mat(); - cv::Mat gray; - // gray is allocated without using the allocator - cv::cvtColor(m1, gray, cv::COLOR_RGB2GRAY); - // luminosity is not preserved, consider using weights. - cv::Mat temp[3] = {gray, gray, gray}; - cv::Mat cv_out; - cv::merge(temp, 3, cv_out); - std::shared_ptr cvt_out; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(cv_out, cvt_in->Rank(), &cvt_out)); - if (abs(t - 0.0) < eps) { - // return grayscale - *output = std::static_pointer_cast(cvt_out); - return Status::OK(); - } - try { - // return blended image. addWeighted takes care of overflow for uint8_t - cv::addWeighted(m1, t, cvt_out->mat(), 1 - t, 0, cvt_out->mat()); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RandomColorOp: cv::addWeighted " + std::string(e.what())); - } - *output = std::static_pointer_cast(cvt_out); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_color_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_color_op.h deleted file mode 100644 index 418cbb083..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_color_op.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_COLOR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_COLOR_OP_H_ - -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \class RandomColorOp random_color_op.h -/// \brief Blends an image with its grayscale version with random weights -/// t and 1 - t generated from a given range. -/// If the range is trivial then the weights are determinate and -/// t equals the bound of the interval -class RandomColorOp : public RandomTensorOp { - public: - RandomColorOp() = default; - - ~RandomColorOp() override = default; - - /// \brief Constructor - /// \param[in] t_lb lower bound for the random weights - /// \param[in] t_ub upper bound for the random weights - RandomColorOp(float t_lb, float t_ub); - - /// \brief the main function performing computations - /// \param[in] in 2- or 3- dimensional tensor representing an image - /// \param[out] out 2- or 3- dimensional tensor representing an image - /// with the same dimensions as in - Status Compute(const std::shared_ptr &in, std::shared_ptr *out) override; - - /// \brief returns the name of the op - std::string Name() const override { return kRandomColorOp; } - - private: - std::uniform_real_distribution dist_; - float t_lb_; - float t_ub_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_COLOR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.cc deleted file mode 100644 index 38230e151..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.cc +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomCropAndResizeOp::RandomCropAndResizeOp(int32_t target_height, int32_t target_width, float scale_lb, - float scale_ub, float aspect_lb, float aspect_ub, - InterpolationMode interpolation, int32_t max_attempts) - : target_height_(target_height), - target_width_(target_width), - rnd_scale_(scale_lb, scale_ub), - rnd_aspect_(log(aspect_lb), log(aspect_ub)), - interpolation_(interpolation), - aspect_lb_(aspect_lb), - aspect_ub_(aspect_ub), - max_iter_(max_attempts) {} - -Status RandomCropAndResizeOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - - for (size_t i = 0; i < input.size(); i++) { - if (input[i]->Rank() < kMinImageRank) { - RETURN_STATUS_UNEXPECTED("RandomResizedCrop: input tensor should have at least 2 dimensions, but got: " + - std::to_string(input[i]->Rank())); - } - if (i < input.size() - 1) { - std::vector size; - std::vector next_size; - RETURN_IF_NOT_OK(ImageSize(input[i], &size)); - RETURN_IF_NOT_OK(ImageSize(input[i + 1], &next_size)); - if (size[0] != next_size[0] || size[1] != next_size[1]) { - RETURN_STATUS_UNEXPECTED( - "RandomCropAndResizeOp: Input tensor in different columns of each row must have the same size."); - } - } - } - output->resize(input.size()); - int x = 0; - int y = 0; - int crop_height = 0; - int crop_width = 0; - for (size_t i = 0; i < input.size(); i++) { - auto input_shape = input[i]->shape(); - std::vector size; - RETURN_IF_NOT_OK(ImageSize(input[i], &size)); - int h_in = static_cast(size[0]); - int w_in = static_cast(size[1]); - if (i == 0) { - RETURN_IF_NOT_OK(GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); - } - if (input[i]->Rank() <= kDefaultImageRank) { - RETURN_IF_NOT_OK(CropAndResize(input[i], &(*output)[i], x, y, crop_height, crop_width, target_height_, - target_width_, interpolation_)); - } else if (input[i]->Rank() > kDefaultImageRank) { - dsize_t num_batch = input[i]->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input[i]->Reshape(new_shape)); - // split [N, H, W, C] to N [H, W, C], and Resize N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input[i], &input_vector_hwc)); - for (const auto &input_hwc : input_vector_hwc) { - std::shared_ptr output_img; - RETURN_IF_NOT_OK(CropAndResize(input_hwc, &output_img, x, y, crop_height, crop_width, target_height_, - target_width_, interpolation_)); - output_vector_hwc.push_back(output_img); - } - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, &(*output)[i])); - auto output_shape = ComputeOutputShape(input_shape); - RETURN_IF_NOT_OK((*output)[i]->Reshape(output_shape)); - } - } - return Status::OK(); -} - -TensorShape RandomCropAndResizeOp::ComputeOutputShape(const TensorShape &input) const { - auto out_shape_vec = input.AsVector(); - auto size = out_shape_vec.size(); - int32_t kHeightIdx = -3; - int32_t kWidthIdx = -2; - out_shape_vec[size + kHeightIdx] = target_height_; - out_shape_vec[size + kWidthIdx] = target_width_; - TensorShape out = TensorShape(out_shape_vec); - return out; -} - -Status RandomCropAndResizeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out = TensorShape{target_height_, target_width_}; - if (inputs[0].Rank() == 2) { - (void)outputs.emplace_back(out); - } - if (inputs[0].Rank() == 3) { - (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); - } else if (inputs[0].Rank() > kDefaultImageRank) { - auto out_shape = ComputeOutputShape(inputs[0]); - (void)outputs.emplace_back(out_shape); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), - "RandomCropAndResize: input tensor should have at least 2 dimensions, but got: " + - std::to_string(inputs[0].Rank())); - return Status::OK(); -} - -Status RandomCropAndResizeOp::GetCropBox(int h_in, int w_in, int *x, int *y, int *crop_height, int *crop_width) { - CHECK_FAIL_RETURN_UNEXPECTED(crop_height != nullptr, "crop_height is nullptr."); - CHECK_FAIL_RETURN_UNEXPECTED(crop_width != nullptr, "crop_width is nullptr."); - *crop_width = w_in; - *crop_height = h_in; - CHECK_FAIL_RETURN_UNEXPECTED(w_in != 0, "RandomCropAndResize: Width of input cannot be 0."); - CHECK_FAIL_RETURN_UNEXPECTED(h_in != 0, "RandomCropAndResize: Height of input cannot be 0."); - CHECK_FAIL_RETURN_UNEXPECTED( - aspect_lb_ > 0, - "RandomCropAndResize: 'ratio'(aspect) lower bound must be greater than 0, but got:" + std::to_string(aspect_lb_)); - for (int32_t i = 0; i < max_iter_; i++) { - double const sample_scale = rnd_scale_(random_generator_); - // In case of non-symmetrical aspect ratios, use uniform distribution on a logarithmic sample_scale. - // Note rnd_aspect_ is already a random distribution of the input aspect ratio in logarithmic sample_scale. - double const sample_aspect = exp(rnd_aspect_(random_generator_)); - - CHECK_FAIL_RETURN_UNEXPECTED( - (std::numeric_limits::max() / h_in) > w_in, - "RandomCropAndResizeOp: multiplication out of bounds, check image width and image height first."); - CHECK_FAIL_RETURN_UNEXPECTED( - static_cast((std::numeric_limits::max() / h_in) / w_in) > sample_scale, - "RandomCropAndResizeOp: multiplication out of bounds, check image width, image height and sample scale first."); - CHECK_FAIL_RETURN_UNEXPECTED( - static_cast((std::numeric_limits::max() / h_in) / w_in) / sample_scale > sample_aspect, - "RandomCropAndResizeOp: multiplication out of bounds, check image width, image " - "height, sample scale and sample aspect first."); - *crop_width = static_cast(std::round(std::sqrt(h_in * w_in * sample_scale * sample_aspect))); - *crop_height = static_cast(std::round(*crop_width / sample_aspect)); - - // forbidden crop_width or crop_height is zero - if (*crop_width <= 0) { - *crop_width = 1; - } - if (*crop_height <= 0) { - *crop_height = 1; - } - - if (*crop_width <= w_in && *crop_height <= h_in) { - std::uniform_int_distribution<> rd_x(0, w_in - *crop_width); - std::uniform_int_distribution<> rd_y(0, h_in - *crop_height); - *x = rd_x(random_generator_); - *y = rd_y(random_generator_); - return Status::OK(); - } - } - double const img_aspect = static_cast(w_in) / h_in; - if (img_aspect < aspect_lb_) { - *crop_width = w_in; - *crop_height = static_cast(std::round(*crop_width / static_cast(aspect_lb_))); - } else { - if (img_aspect > aspect_ub_) { - *crop_height = h_in; - *crop_width = static_cast(std::round(*crop_height * static_cast(aspect_ub_))); - } else { - *crop_width = w_in; - *crop_height = h_in; - } - } - constexpr float crop_ratio = 2.0; - // forbidden crop_width or crop_height is zero - if (*crop_width <= 0) { - *crop_width = 1; - } - if (*crop_height <= 0) { - *crop_height = 1; - } - - *x = static_cast(std::round(static_cast(w_in - *crop_width) / crop_ratio)); - *y = static_cast(std::round(static_cast(h_in - *crop_height) / crop_ratio)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h deleted file mode 100644 index f32ba9500..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomCropAndResizeOp : public RandomTensorOp { - public: - RandomCropAndResizeOp(int32_t target_height, int32_t target_width, float scale_lb, float scale_ub, float aspect_lb, - float aspect_ub, InterpolationMode interpolation, int32_t max_attempts); - - RandomCropAndResizeOp() = default; - - ~RandomCropAndResizeOp() override = default; - - void Print(std::ostream &out) const override { - out << "RandomCropAndResize: " << target_height_ << " " << target_width_; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - TensorShape ComputeOutputShape(const TensorShape &input) const; - - Status GetCropBox(int h_in, int w_in, int *x, int *y, int *crop_height, int *crop_width); - - std::string Name() const override { return kRandomCropAndResizeOp; } - - uint32_t NumInput() override { return 1; } - - uint32_t NumOutput() override { return 1; } - - protected: - int32_t target_height_; - int32_t target_width_; - std::uniform_real_distribution rnd_scale_; - std::uniform_real_distribution rnd_aspect_; - InterpolationMode interpolation_; - int32_t max_iter_; - double aspect_lb_; - double aspect_ub_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc deleted file mode 100644 index 0eb46ef43..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomCropAndResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - RETURN_IF_NOT_OK(ValidateImageRank("RandomCropAndResizeWithBBox", static_cast(input[0]->shape().Size()))); - - const int output_count = 2; - output->resize(output_count); - (*output)[1] = input[1]; // move boxes over to output - - size_t bboxCount = input[1]->shape()[0]; // number of rows in bbox tensor - int h_in = static_cast(input[0]->shape()[0]); - int w_in = static_cast(input[0]->shape()[1]); - int x = 0; - int y = 0; - int crop_height = 0; - int crop_width = 0; - - RETURN_IF_NOT_OK(RandomCropAndResizeOp::GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); - - int maxX = x + crop_width; // max dims of selected CropBox on image - int maxY = y + crop_height; - - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &bboxCount, x, y, maxX, maxY)); // IMAGE_UTIL - RETURN_IF_NOT_OK(CropAndResize(input[0], &(*output)[0], x, y, crop_height, crop_width, target_height_, target_width_, - interpolation_)); - - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, target_width_, target_height_, - crop_width, crop_height)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h deleted file mode 100644 index 1b7095444..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_WITH_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_WITH_BBOX_OP_H_ - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h" - -namespace mindspore { -namespace dataset { -class RandomCropAndResizeWithBBoxOp : public RandomCropAndResizeOp { - public: - // Constructor for RandomCropAndResizeWithBBoxOp, with default value and passing to base class constructor - RandomCropAndResizeWithBBoxOp(int32_t target_height, int32_t target_width, float scale_lb, float scale_ub, - float aspect_lb, float aspect_ub, InterpolationMode interpolation, int32_t max_attempts) - : RandomCropAndResizeOp(target_height, target_width, scale_lb, scale_ub, aspect_lb, aspect_ub, interpolation, - max_attempts) {} - - ~RandomCropAndResizeWithBBoxOp() override = default; - - void Print(std::ostream &out) const override { - out << "RandomCropAndResizeWithBBox: " << RandomCropAndResizeOp::target_height_ << " " - << RandomCropAndResizeOp::target_width_; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomCropAndResizeWithBBoxOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_AND_RESIZE_WITH_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc deleted file mode 100644 index ee5b13f17..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/kernels/image/decode_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -RandomCropDecodeResizeOp::RandomCropDecodeResizeOp(int32_t target_height, int32_t target_width, float scale_lb, - float scale_ub, float aspect_lb, float aspect_ub, - InterpolationMode interpolation, int32_t max_attempts) - : RandomCropAndResizeOp(target_height, target_width, scale_lb, scale_ub, aspect_lb, aspect_ub, interpolation, - max_attempts) {} - -Status RandomCropDecodeResizeOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - const auto output_count = input.size(); - output->resize(output_count); - int x = 0; - int y = 0; - int crop_height = 0; - int crop_width = 0; - TensorRow decoded; - decoded.resize(output_count); - for (size_t i = 0; i < input.size(); i++) { - if (input[i] == nullptr) { - RETURN_STATUS_UNEXPECTED("RandomCropDecodeResize: input image is empty since got nullptr."); - } - if (!IsNonEmptyJPEG(input[i])) { - DecodeOp op(true); - RETURN_IF_NOT_OK(op.Compute(input[i], &decoded[i])); - RETURN_IF_NOT_OK(RandomCropAndResizeOp::Compute(decoded, output)); - } else { - int h_in = 0; - int w_in = 0; - RETURN_IF_NOT_OK(GetJpegImageInfo(input[i], &w_in, &h_in)); - if (i == 0) { - RETURN_IF_NOT_OK(GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); - } - std::shared_ptr decoded_tensor = nullptr; - RETURN_IF_NOT_OK(JpegCropAndDecode(input[i], &decoded_tensor, x, y, crop_width, crop_height)); - RETURN_IF_NOT_OK(Resize(decoded_tensor, &(*output)[i], target_height_, target_width_, 0.0, 0.0, interpolation_)); - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h deleted file mode 100644 index 45bcef689..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_DECODE_RESIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_DECODE_RESIZE_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomCropDecodeResizeOp : public RandomCropAndResizeOp { - public: - RandomCropDecodeResizeOp(int32_t target_height, int32_t target_width, float scale_lb, float scale_ub, float aspect_lb, - float aspect_ub, InterpolationMode interpolation, int32_t max_attempts); - - explicit RandomCropDecodeResizeOp(const RandomCropAndResizeOp &rhs) : RandomCropAndResizeOp(rhs) {} - - ~RandomCropDecodeResizeOp() override = default; - - void Print(std::ostream &out) const override { - out << Name() << ": " << RandomCropAndResizeOp::target_height_ << " " << RandomCropAndResizeOp::target_width_; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomCropDecodeResizeOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_DECODE_RESIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.cc deleted file mode 100644 index 6213bc409..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.cc +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomCropOp::RandomCropOp(int32_t crop_height, int32_t crop_width, int32_t pad_top, int32_t pad_bottom, - int32_t pad_left, int32_t pad_right, bool pad_if_needed, BorderType padding_mode, - uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : crop_height_(crop_height), - crop_width_(crop_width), - pad_top_(pad_top), - pad_bottom_(pad_bottom), - pad_left_(pad_left), - pad_right_(pad_right), - pad_if_needed_(pad_if_needed), - border_type_(padding_mode), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} - -Status RandomCropOp::ImagePadding(const std::shared_ptr &input, std::shared_ptr *pad_image, - int32_t *t_pad_top, int32_t *t_pad_bottom, int32_t *t_pad_left, int32_t *t_pad_right, - int32_t *padded_image_w, int32_t *padded_image_h, bool *crop_further) { - *t_pad_top = pad_top_; - *t_pad_bottom = pad_bottom_; - *t_pad_left = pad_left_; - *t_pad_right = pad_right_; - - constexpr int64_t max_ratio = 3; - CHECK_FAIL_RETURN_UNEXPECTED( - pad_top_ < input->shape()[0] * max_ratio && pad_bottom_ < input->shape()[0] * max_ratio && - pad_left_ < input->shape()[1] * max_ratio && pad_right_ < input->shape()[1] * max_ratio, - "RandomCrop: padding size is three times bigger than the image size, padding top: " + std::to_string(pad_top_) + - ", padding bottom: " + std::to_string(pad_bottom_) + ", padding pad_left_: " + std::to_string(pad_left_) + - ", padding padding right:" + std::to_string(pad_right_) + ", image shape: " + std::to_string(input->shape()[0]) + - ", " + std::to_string(input->shape()[1])); - - RETURN_IF_NOT_OK( - Pad(input, pad_image, pad_top_, pad_bottom_, pad_left_, pad_right_, border_type_, fill_r_, fill_g_, fill_b_)); - CHECK_FAIL_RETURN_UNEXPECTED( - (*pad_image)->shape().Size() >= 2, - "RandomCrop: invalid shape of image after pad, got rank: " + std::to_string((*pad_image)->shape().Size())); - - *padded_image_h = static_cast((*pad_image)->shape()[0]); - *padded_image_w = static_cast((*pad_image)->shape()[1]); - - if (*padded_image_h == crop_height_ && *padded_image_w == crop_width_) { - *crop_further = false; // no need for further crop - return Status::OK(); - } else if (pad_if_needed_) { - // check the dimensions of the image for padding, if we do need padding, then we change the pad values - if (*padded_image_h < crop_height_) { - RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, crop_height_ - *padded_image_h, crop_height_ - *padded_image_h, 0, 0, - border_type_, fill_r_, fill_g_, fill_b_)); - - // update pad total above/below - t_pad_top += ((ptrdiff_t)crop_height_ - *padded_image_h); - t_pad_bottom += ((ptrdiff_t)crop_height_ - *padded_image_h); - } - if (*padded_image_w < crop_width_) { - RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, 0, 0, crop_width_ - *padded_image_w, crop_width_ - *padded_image_w, - border_type_, fill_r_, fill_g_, fill_b_)); - // update pad total left/right - t_pad_left += ((ptrdiff_t)crop_width_ - *padded_image_w); - t_pad_right += ((ptrdiff_t)crop_width_ - *padded_image_w); - } - *padded_image_h = static_cast((*pad_image)->shape()[0]); - *padded_image_w = static_cast((*pad_image)->shape()[1]); - } - - if (crop_height_ == 0 || crop_width_ == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDShapeMisMatch, - "RandomCrop: invalid crop size, crop width or crop height is not allowed to be zero."); - } - if (*padded_image_h < crop_height_ || *padded_image_w < crop_width_ || crop_height_ == 0 || crop_width_ == 0) { - RETURN_STATUS_ERROR(StatusCode::kMDShapeMisMatch, - "RandomCrop: invalid crop size, crop size is bigger than the image dimensions, " - "got crop height: " + - std::to_string(crop_height_) + ", crop width: " + std::to_string(crop_width_)); - } - return Status::OK(); -} - -void RandomCropOp::GenRandomXY(int32_t *x, int32_t *y, int32_t padded_image_w, int32_t padded_image_h) { - // GenCropPoints for cropping - *x = std::uniform_int_distribution(0, padded_image_w - crop_width_)(random_generator_); - *y = std::uniform_int_distribution(0, padded_image_h - crop_height_)(random_generator_); -} - -Status RandomCropOp::RandomCropImg(const std::shared_ptr &input, std::shared_ptr *output, int32_t *x, - int32_t *y, int32_t index) { - std::shared_ptr pad_image = nullptr; - int32_t t_pad_top = 0; - int32_t t_pad_bottom = 0; - int32_t t_pad_left = 0; - int32_t t_pad_right = 0; - int32_t padded_image_w = 0; - int32_t padded_image_h = 0; - bool crop_further = true; // whether image needs further cropping based on new size & requirements - - RETURN_IF_NOT_OK( // error code sent back directly - ImagePadding(input, &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, &padded_image_w, - &padded_image_h, &crop_further)); - if (!crop_further) { - *output = pad_image; - return Status::OK(); - } - if (index == 0) { - GenRandomXY(x, y, padded_image_w, padded_image_h); - } - RETURN_IF_NOT_OK(Crop(pad_image, output, *x, *y, crop_width_, crop_height_)); - - return Status::OK(); -} - -Status RandomCropOp::ConstructShape(const TensorShape &in_shape, std::shared_ptr *out_shape) const { - auto in_shape_vec = in_shape.AsVector(); - const int h_index = -3; - const int w_index = -2; - in_shape_vec[in_shape_vec.size() + h_index] = crop_height_; - in_shape_vec[in_shape_vec.size() + w_index] = crop_width_; - - *out_shape = std::make_shared(in_shape_vec); - - return Status::OK(); -} - -Status RandomCropOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - - for (const auto &image : input) { - if (image->shape().Rank() < kMinImageRank) { - std::string err_msg = - "RandomCropOp: input tensor should have at least 2 dimensions, but got: " + std::to_string(image->Rank()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - - if (input.size() > 1) { - for (size_t i = 0; i < input.size() - 1; i++) { - if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) { - RETURN_STATUS_UNEXPECTED( - "RandomCrop: Input images in different column must have the same shape, check the output shape in " - "specified 'input_columns' before call this operation."); - } - } - } - - const auto output_count = input.size(); - output->resize(output_count); - int32_t x = 0; - int32_t y = 0; - for (size_t i = 0; i < input.size(); i++) { - if (input[i]->shape().Rank() <= kDefaultImageRank) { // keep original logic untained - RETURN_IF_NOT_OK(RandomCropImg(input[i], &(*output)[i], &x, &y, i)); - } else { // deal with videos - // reshape input to hwc - auto input_shape = input[i]->shape(); - dsize_t num_batch = input[i]->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input[i]->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and center crop N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input[i], &input_vector_hwc)); - - // perform randomCrop - for (int32_t idx = 0; idx < num_batch; idx++) { - std::shared_ptr random_crop; - RETURN_IF_NOT_OK(RandomCropImg(input_vector_hwc[idx], &random_crop, &x, &y, i)); - output_vector_hwc.push_back(random_crop); - } - - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, &(*output)[i])); - - // reshape output before return, only height and width are changed - std::shared_ptr output_shape_new; - RETURN_IF_NOT_OK(ConstructShape(input_shape, &output_shape_new)); - RETURN_IF_NOT_OK((*output)[i]->Reshape(*output_shape_new)); - } - } - - return Status::OK(); -} - -Status RandomCropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - TensorShape out = TensorShape{crop_height_, crop_width_}; - if (inputs[0].Rank() == kMinImageRank) { - (void)outputs.emplace_back(out); - } else if (inputs[0].Rank() == kDefaultImageRank) { - (void)outputs.emplace_back(out.AppendDim(inputs[0][kChannelIndexHWC])); - } else if (inputs[0].Rank() > kDefaultImageRank) { - std::shared_ptr output_shape_new; - RETURN_IF_NOT_OK(ConstructShape(inputs[0], &output_shape_new)); - (void)outputs.emplace_back(*output_shape_new); - } - if (!outputs.empty()) { - return Status::OK(); - } - RETURN_STATUS_UNEXPECTED("RandomCrop: invalid input shape, expected 2D or 3D input, but got input dimension is:" + - std::to_string(inputs[0].Rank())); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h deleted file mode 100644 index 7fbe9b106..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomCropOp : public RandomTensorOp { - public: - RandomCropOp(int32_t crop_height, int32_t crop_width, int32_t pad_top, int32_t pad_bottom, int32_t pad_left, - int32_t pad_right, bool pad_if_needed, BorderType padding_mode, uint8_t fill_r, uint8_t fill_g, - uint8_t fill_b); - - ~RandomCropOp() override = default; - - void Print(std::ostream &out) const override { out << Name() << ": " << crop_height_ << " " << crop_width_; } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - // Function breaks out the compute function's image padding functionality and makes available to other Ops - // Using this class as a base - re-structured to allow for RandomCropWithBBox Augmentation Op - // @param input: Input is the original Image - // @param pad_image: Pointer to new Padded image - // @param t_pad_top: Total Top Padding - Based on input and value calculated in function if required - // @param t_pad_bottom: Total bottom Padding - Based on input and value calculated in function if required - // @param t_pad_left: Total left Padding - Based on input and value calculated in function if required - // @param t_pad_right: Total right Padding - Based on input and value calculated in function if required - // @param padded_image_w: Final Width of the 'pad_image' - // @param padded_image_h: Final Height of the 'pad_image' - // @param crop_further: Whether image required cropping after padding - False if new padded image matches required - // dimensions - Status ImagePadding(const std::shared_ptr &input, std::shared_ptr *pad_image, int32_t *t_pad_top, - int32_t *t_pad_bottom, int32_t *t_pad_left, int32_t *t_pad_right, int32_t *padded_image_w, - int32_t *padded_image_h, bool *crop_further); - - // Function breaks X,Y generation functionality out of original compute function and makes available to other Ops - void GenRandomXY(int32_t *x, int32_t *y, int32_t padded_image_w, int32_t padded_image_h); - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kRandomCropOp; } - - uint32_t NumInput() override { return 1; } - - uint32_t NumOutput() override { return 1; } - - protected: - int32_t crop_height_ = 0; - int32_t crop_width_ = 0; - - private: - Status RandomCropImg(const std::shared_ptr &input, std::shared_ptr *output, int32_t *x, int32_t *y, - int32_t index); - - Status ConstructShape(const TensorShape &in_shape, std::shared_ptr *out_shape) const; - - int32_t pad_top_ = 0; - int32_t pad_bottom_ = 0; - int32_t pad_left_ = 0; - int32_t pad_right_ = 0; - bool pad_if_needed_ = false; - BorderType border_type_; - uint8_t fill_r_ = 0; - uint8_t fill_g_ = 0; - uint8_t fill_b_ = 0; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc deleted file mode 100644 index aff3ee7cc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomCropWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - - std::shared_ptr input_image; - std::shared_ptr input_bbox; - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input[0], &input_image)); - RETURN_IF_NOT_OK(Tensor::CreateFromTensor(input[1], &input_bbox)); - TensorRow input_copy; - input_copy.emplace_back(std::move(input_image)); - input_copy.emplace_back(std::move(input_bbox)); - - std::shared_ptr pad_image = nullptr; - int32_t t_pad_top = 0; - int32_t t_pad_bottom = 0; - int32_t t_pad_left = 0; - int32_t t_pad_right = 0; - size_t boxCount = input_copy[1]->shape()[0]; // number of rows - - int32_t padded_image_h = 0; - int32_t padded_image_w = 0; - const int output_count = 2; - output->resize(output_count); - (*output)[1] = input_copy[1]; // since some boxes may be removed - - bool crop_further = true; // Whether further cropping will be required or not, true unless required size matches - RETURN_IF_NOT_OK( // Error passed back to caller - RandomCropOp::ImagePadding(input_copy[0], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, - &padded_image_w, &padded_image_h, &crop_further)); - - // update bounding boxes with new values based on relevant image padding - if (t_pad_left != 0 || t_pad_top != 0) { - RETURN_IF_NOT_OK(BoundingBox::PadBBoxes(&(*output)[1], boxCount, t_pad_top, t_pad_left)); - } - if (!crop_further) { - // no further cropping required - (*output)[0] = pad_image; - (*output)[1] = input_copy[1]; - return Status::OK(); - } - - int x, y; - RandomCropOp::GenRandomXY(&x, &y, padded_image_w, padded_image_h); - int maxX = x + RandomCropOp::crop_width_; // max dims of selected CropBox on image - int maxY = y + RandomCropOp::crop_height_; - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &boxCount, x, y, maxX, maxY)); - return Crop(pad_image, &(*output)[0], x, y, RandomCropOp::crop_width_, RandomCropOp::crop_height_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h deleted file mode 100644 index 71e2ace0a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_WITH_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_WITH_BBOX_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h" - -namespace mindspore { -namespace dataset { -class RandomCropWithBBoxOp : public RandomCropOp { - public: - // Constructor for RandomCropWithBBoxOp, with default value and passing to base class constructor - RandomCropWithBBoxOp(int32_t crop_height, int32_t crop_width, int32_t pad_top, int32_t pad_bottom, int32_t pad_left, - int32_t pad_right, bool pad_if_needed, BorderType padding_mode, uint8_t fill_r, uint8_t fill_g, - uint8_t fill_b) - : RandomCropOp(crop_height, crop_width, pad_top, pad_bottom, pad_left, pad_right, pad_if_needed, padding_mode, - fill_r, fill_g, fill_b) {} - - ~RandomCropWithBBoxOp() override = default; - - void Print(std::ostream &out) const override { - out << Name() << ": " << RandomCropOp::crop_height_ << " " << RandomCropOp::crop_width_; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomCropWithBBoxOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_CROP_WITH_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.cc deleted file mode 100644 index 85f6c61b6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.cc +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomEqualizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // Check input - RETURN_IF_NOT_OK(ValidateImageRank("RandomEqualize", input->Rank())); - if (input->Rank() == kDefaultImageRank) { - int num_channels = static_cast(input->shape()[kChannelIndexHWC]); - if (num_channels != kMinImageChannel && num_channels != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED("RandomEqualize: input image is not in channel of 1 or 3, but got: " + - std::to_string(input->shape()[kChannelIndexHWC])); - } - } - CHECK_FAIL_RETURN_UNEXPECTED( - input->type() == DataType(DataType::DE_UINT8), - "RandomEqualize: input image is not in type of uint8, but got: " + input->type().ToString()); - if (distribution_(random_generator_)) { - return Equalize(input, output); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h deleted file mode 100644 index f954b93c2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_EQUALIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_EQUALIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomEqualizeOp : public RandomTensorOp { - public: - explicit RandomEqualizeOp(float prob) : distribution_(prob) {} - - ~RandomEqualizeOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomEqualizeOp &so) { - so.Print(out); - return out; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomEqualizeOp; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_EQUALIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.cc deleted file mode 100644 index 8fde34869..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.cc +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomHorizontalFlipOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - const auto output_count = input.size(); - output->resize(output_count); - - for (const auto &image : input) { - RETURN_IF_NOT_OK(ValidateImage(image, "RandomHorizontalFlip", {1, 2, 3, 4, 5, 6, 10, 11, 12})); - } - - if (distribution_(random_generator_)) { - for (dsize_t i = 0; i < output_count; ++i) { - auto input_shape = input[i]->shape(); - dsize_t rank = input_shape.Rank(); - if (rank <= kDefaultImageRank) { - // [H, W] or [H, W, C] - RETURN_IF_NOT_OK(HorizontalFlip(input[i], &(*output)[i])); - } else { - // reshape [..., H, W, C] to [N, H, W, C] - dsize_t num_batch = input[i]->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input[i]->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and flip N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input[i], &input_vector_hwc)); - for (const auto &input_hwc : input_vector_hwc) { - std::shared_ptr flip; - RETURN_IF_NOT_OK(HorizontalFlip(input_hwc, &flip)); - output_vector_hwc.push_back(flip); - } - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, &(*output)[i])); - RETURN_IF_NOT_OK((*output)[i]->Reshape(input_shape)); - } - } - return Status::OK(); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h deleted file mode 100644 index 2d04115b9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomHorizontalFlipOp : public RandomTensorOp { - public: - explicit RandomHorizontalFlipOp(float prob) : distribution_(prob) {} - - ~RandomHorizontalFlipOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomHorizontalFlipOp &so) { - so.Print(out); - return out; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomHorizontalFlipOp; } - - uint32_t NumInput() override { return 1; } - - uint32_t NumOutput() override { return 1; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc deleted file mode 100644 index b08d95ba1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h" - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomHorizontalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - RETURN_IF_NOT_OK(ValidateImageDtype("RandomHorizontalFlipWithBBox", input[0]->type())); - RETURN_IF_NOT_OK(ValidateImageRank("RandomHorizontalFlipWithBBox", input[0]->Rank())); - - if (distribution_(random_generator_)) { - // To test bounding boxes algorithm, create random bboxes from image dims - size_t num_of_boxes = input[1]->shape()[0]; // set to give number of bboxes - float img_center = static_cast(input[0]->shape()[1]) / 2.F; // get the center of the image - for (int i = 0; i < num_of_boxes; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); - // do the flip - BoundingBox::bbox_float diff = img_center - bbox->x(); // get distance from min_x to center - BoundingBox::bbox_float refl_min_x = diff + img_center; // get reflection of min_x - BoundingBox::bbox_float new_min_x = - refl_min_x - bbox->width(); // subtract from the reflected min_x to get the new one - bbox->SetX(new_min_x); - RETURN_IF_NOT_OK(bbox->WriteToTensor(input[1], i)); - } - (*output).resize(2); - // move input to output pointer of bounding boxes - (*output)[1] = input[1]; - // perform HorizontalFlip on the image - std::shared_ptr input_cv = CVTensor::AsCVTensor(input[0]); - return HorizontalFlip(std::static_pointer_cast(input_cv), &(*output)[0]); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h deleted file mode 100644 index 2ff91c643..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_BBOX_OP_H_ - -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomHorizontalFlipWithBBoxOp : public RandomTensorOp { - public: - explicit RandomHorizontalFlipWithBBoxOp(float probability) : distribution_(probability) {} - - ~RandomHorizontalFlipWithBBoxOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomHorizontalFlipWithBBoxOp &so) { - so.Print(out); - return out; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomHorizontalFlipWithBBoxOp; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_HORIZONTAL_FLIP_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.cc deleted file mode 100644 index 88f8dfbf0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.cc +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status RandomInvertOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // check input - if (input->Rank() != kDefaultImageRank) { - RETURN_STATUS_UNEXPECTED("RandomInvert: image shape is not , got rank: " + std::to_string(input->Rank())); - } - if (input->shape()[kChannelIndexHWC] != kDefaultImageChannel) { - RETURN_STATUS_UNEXPECTED( - "RandomInvert: image shape is incorrect, expected num of channels is 3, " - "but got:" + - std::to_string(input->shape()[kChannelIndexHWC])); - } - CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, - "RandomInvert: Cannot convert from OpenCV type, unknown CV type. Currently " - "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); - if (distribution_(random_generator_)) { - return Invert(input, output); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h deleted file mode 100644 index e2ccfb01d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_INVERT_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_INVERT_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/invert_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomInvertOp : public RandomTensorOp { - public: - explicit RandomInvertOp(float prob) : distribution_(prob) {} - - ~RandomInvertOp() override = default; - - // Provide stream operator for displaying it - friend std::ostream &operator<<(std::ostream &out, const RandomInvertOp &so) { - so.Print(out); - return out; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomInvertOp; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_INVERT_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.cc deleted file mode 100644 index d71644658..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status RandomLightingOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - // check input dimension, it should be greater than 2 - RETURN_IF_NOT_OK(ValidateLowRank("RandomLighting", input, kMinImageRank, "")); - RETURN_IF_NOT_OK(ValidateTensorNumeric("RandomLighting", input)); - - float rnd_r = dist_(random_generator_); - float rnd_g = dist_(random_generator_); - float rnd_b = dist_(random_generator_); - return RandomLighting(input, output, rnd_r, rnd_g, rnd_b); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h deleted file mode 100644 index e1f9b882b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_LIGHTING_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_LIGHTING_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomLightingOp : public RandomTensorOp { - public: - explicit RandomLightingOp(float alpha) : dist_(0, alpha) {} - - ~RandomLightingOp() override = default; - - Status Compute(const std::shared_ptr &in, std::shared_ptr *out) override; - - std::string Name() const override { return kRandomLightingOp; } - - private: - std::normal_distribution dist_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RANDOM_LIGHTING_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.cc deleted file mode 100644 index 1e18005f5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -RandomPosterizeOp::RandomPosterizeOp(const std::vector &bit_range) : bit_range_(bit_range) {} - -Status RandomPosterizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - uint8_t bits = - (bit_range_[0] == bit_range_[1]) - ? bit_range_[0] - : static_cast(std::uniform_int_distribution(bit_range_[0], bit_range_[1])(random_generator_)); - return Posterize(input, output, bits); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h deleted file mode 100644 index 0e58ebdf2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_POSTERIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_POSTERIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class RandomPosterizeOp : public RandomTensorOp { - public: - /// \brief Constructor - /// \param[in] bit_range: Minimum and maximum bits in range - explicit RandomPosterizeOp(const std::vector &bit_range); - - ~RandomPosterizeOp() override = default; - - std::string Name() const override { return kRandomPosterizeOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - /// Member variables - private: - std::vector bit_range_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_POSTERIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.cc deleted file mode 100644 index b9ef7e172..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomResizeOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - const auto output_count = input.size(); - output->resize(output_count); - auto interpolation_random_resize = static_cast(distribution_(random_generator_)); - std::shared_ptr resize_op = std::make_shared(size1_, size2_, interpolation_random_resize); - for (size_t i = 0; i < input.size(); i++) { - RETURN_IF_NOT_OK(resize_op->Compute(input[i], &(*output)[i])); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h deleted file mode 100644 index 06e577034..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomResizeOp : public RandomTensorOp { - public: - RandomResizeOp(int32_t size_1, int32_t size_2) : size1_(size_1), size2_(size_2) {} - - ~RandomResizeOp() override = default; - - // Description: A function that prints info about the node - void Print(std::ostream &out) const override { out << Name() << ": " << size1_ << " " << size2_; } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomResizeOp; } - - uint32_t NumInput() override { return 1; } - - uint32_t NumOutput() override { return 1; } - - private: - int32_t size1_; - int32_t size2_; - std::uniform_int_distribution distribution_{0, 3}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc deleted file mode 100644 index 5d9a80dfc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - // Randomly selects from the following four interpolation methods - // 0-bilinear, 1-nearest_neighbor, 2-bicubic, 3-area - IO_CHECK_VECTOR(input, output); - auto interpolation = static_cast(distribution_(random_generator_)); - std::shared_ptr resize_with_bbox_op = std::make_shared(size1_, size2_, interpolation); - RETURN_IF_NOT_OK(resize_with_bbox_op->Compute(input, output)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h deleted file mode 100644 index 60d084629..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_WITH_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_WITH_BBOX_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomResizeWithBBoxOp : public RandomTensorOp { - public: - RandomResizeWithBBoxOp(int32_t size_1, int32_t size_2) : size1_(size_1), size2_(size_2) {} - - ~RandomResizeWithBBoxOp() override = default; - - // Description: A function that prints info about the node - void Print(std::ostream &out) const override { out << Name() << ": " << size1_ << " " << size2_; } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomResizeWithBBoxOp; } - - private: - int32_t size1_; - int32_t size2_; - std::uniform_int_distribution distribution_{0, 3}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_RESIZE_WITH_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.cc deleted file mode 100644 index 6c787f202..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.cc +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h" - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// constructor -RandomRotationOp::RandomRotationOp(float start_degree, float end_degree, InterpolationMode resample, bool expand, - std::vector center, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : degree_start_(start_degree), - degree_end_(end_degree), - center_(std::move(center)), - interpolation_(resample), - expand_(expand), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} - -// main function call for random rotation : Generate the random degrees -Status RandomRotationOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - float random_double = distribution_(random_generator_); - // get the degree rotation range, mod by 360 because full rotation doesn't affect - // the way this op works (uniform distribution) - // assumption here is that mDegreesEnd > mDegreeStart so we always get positive number - // Note: the range technically is greater than 360 degrees, but will be halved - float degree_range = (degree_end_ - degree_start_) / 2; - float mid = (degree_end_ + degree_start_) / 2; - float degree = mid + random_double * degree_range; - - return Rotate(input, output, center_, degree, interpolation_, expand_, fill_r_, fill_g_, fill_b_); -} - -Status RandomRotationOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - int32_t outputH = -1; - int32_t outputW = -1; - constexpr int32_t kDimensionTwo = 2; - constexpr int32_t kDimensionThree = 3; - // if expand_, then we cannot know the shape. We need the input image to find the output shape --> set it to - // <-1,-1[,3]> - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty() && inputs[0].Size() >= kDimensionTwo, - "RandomRotationOp: invalid input shape, expected 2D or 3D input, but got input" - " dimension is: " + - std::to_string(inputs[0].Rank())); - if (!expand_) { - outputH = static_cast(inputs[0][0]); - outputW = static_cast(inputs[0][1]); - } - TensorShape out = TensorShape{outputH, outputW}; - if (inputs[0].Rank() == kDimensionTwo) { - (void)outputs.emplace_back(out); - } - if (inputs[0].Rank() == kDimensionThree) { - (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); - } - CHECK_FAIL_RETURN_UNEXPECTED( - !outputs.empty(), "RandomRotation: invalid input shape, expected 2D or 3D input, but got input dimension is:" + - std::to_string(inputs[0].Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h deleted file mode 100644 index 6f7d287c7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ROTATION_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ROTATION_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomRotationOp : public RandomTensorOp { - public: - // Constructor for RandomRotationOp - // @param startDegree starting range for random degree - // @param endDegree ending range for random degree - // @param interpolation DE interpolation mode for rotation - // @param expand option for the output image shape to change - // @param center coordinate for center of image rotation - // @param fill_r R value for the color to pad with - // @param fill_g G value for the color to pad with - // @param fill_b B value for the color to pad with - // @details the randomly chosen degree is uniformly distributed - // @details the output shape, if changed, will contain the entire rotated image - // @note maybe using unsigned long int isn't the best here according to our coding rules - RandomRotationOp(float start_degree, float end_degree, InterpolationMode resample, bool expand, - std::vector center, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b); - - ~RandomRotationOp() override = default; - - // Overrides the base class compute function - // Calls the rotate function in ImageUtils, this function takes an input tensor - // and transforms its data using openCV, the output memory is manipulated to contain the result - // @return Status The status code returned - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kRandomRotationOp; } - - private: - float degree_start_; - float degree_end_; - std::vector center_; - InterpolationMode interpolation_; - bool expand_; - uint8_t fill_r_; - uint8_t fill_g_; - uint8_t fill_b_; - std::uniform_real_distribution distribution_{-1.0, 1.0}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_ROTATION_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.cc deleted file mode 100644 index baa745219..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h" - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -RandomSelectSubpolicyOp::RandomSelectSubpolicyOp(const std::vector &policy) - : policy_(policy), rand_int_(0, policy.size() - 1), rand_double_(0, 1) {} - -Status RandomSelectSubpolicyOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - TensorRow in_row = input; - size_t rand_num = rand_int_(random_generator_); - CHECK_FAIL_RETURN_UNEXPECTED(rand_num < policy_.size(), - "RandomSelectSubpolicy: " - "get rand number failed:" + - std::to_string(rand_num)); - for (auto &sub : policy_[rand_num]) { - if (rand_double_(random_generator_) <= sub.second) { - RETURN_IF_NOT_OK(sub.first->Compute(in_row, output)); - in_row = std::move(*output); - } - } - *output = std::move(in_row); - return Status::OK(); -} - -uint32_t RandomSelectSubpolicyOp::NumInput() { - uint32_t num_in = policy_.front().front().first->NumInput(); - for (auto &sub : policy_) { - for (auto &p : sub) { - if (num_in != p.first->NumInput()) { - MS_LOG(WARNING) << "Unable to determine numInput."; - return 0; - } - } - } - return num_in; -} - -uint32_t RandomSelectSubpolicyOp::NumOutput() { - uint32_t num_out = policy_.front().front().first->NumOutput(); - for (auto &sub : policy_) { - for (auto &p : sub) { - if (num_out != p.first->NumOutput()) { - MS_LOG(WARNING) << "Unable to determine numInput."; - return 0; - } - } - } - return num_out; -} - -Status RandomSelectSubpolicyOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - outputs.clear(); - outputs.resize(NumOutput(), TensorShape::CreateUnknownRankShape()); - return Status::OK(); -} - -Status RandomSelectSubpolicyOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(policy_.front().front().first->OutputType(inputs, outputs)); - for (auto &sub : policy_) { - for (auto &p : sub) { - std::vector tmp_types; - RETURN_IF_NOT_OK(p.first->OutputType(inputs, tmp_types)); - if (outputs != tmp_types) { - outputs.clear(); - outputs.resize(NumOutput(), DataType(DataType::DE_UNKNOWN)); - return Status::OK(); - } - } - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h deleted file mode 100644 index 685119ca2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SELECT_SUBPOLICY_OP_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SELECT_SUBPOLICY_OP_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -using Subpolicy = std::vector, double>>; - -class RandomSelectSubpolicyOp : public RandomTensorOp { - public: - /// constructor - /// \param[in] policy policy to choose subpolicy from - explicit RandomSelectSubpolicyOp(const std::vector &policy); - - /// destructor - ~RandomSelectSubpolicyOp() override = default; - - /// return number of input tensors - /// \return number of inputs if all ops in policy have the same NumInput, otherwise return 0 - uint32_t NumInput() override; - - /// return number of output tensors - /// \return number of outputs if all ops in policy have the same NumOutput, otherwise return 0 - uint32_t NumOutput() override; - - /// return unknown shapes - /// \param[in] inputs - /// \param[out] outputs - /// \return Status Code - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - /// return output type if all ops in policy return the same type, otherwise return unknown type - /// \param[in] inputs - /// \param[out] outputs - /// \return Status Code - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - /// \param[in] input - /// \param[out] output - /// \return Status code - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomSelectSubpolicyOp; } - - private: - std::vector policy_; - std::uniform_int_distribution rand_int_; - std::uniform_real_distribution rand_double_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SELECT_SUBPOLICY_OP_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.cc deleted file mode 100644 index 94eb3be94..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.cc +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -/// constructor -RandomSharpnessOp::RandomSharpnessOp(float start_degree, float end_degree) - : start_degree_(start_degree), end_degree_(end_degree) {} - -/// main function call for random sharpness : Generate the random degrees -Status RandomSharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - float random_double = distribution_(random_generator_); - /// get the degree sharpness range - /// the way this op works (uniform distribution) - /// assumption here is that mDegreesEnd > mDegreeStart so we always get positive number - float degree_range = (end_degree_ - start_degree_) / 2; - float mid = (end_degree_ + start_degree_) / 2; - float alpha = mid + random_double * degree_range; - return AdjustSharpness(input, output, alpha); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h deleted file mode 100644 index 23d4f207b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SHARPNESS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SHARPNESS_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomSharpnessOp : public RandomTensorOp { - public: - /// Adjust the sharpness of the input image by a random degree within the given range. - /// \@param[in] start_degree A float indicating the beginning of the range. - /// \@param[in] end_degree A float indicating the end of the range. - explicit RandomSharpnessOp(float start_degree, float end_degree); - - ~RandomSharpnessOp() override = default; - - void Print(std::ostream &out) const override { out << Name(); } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomSharpnessOp; } - - protected: - float start_degree_; - float end_degree_; - std::uniform_real_distribution distribution_{-1.0, 1.0}; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SHARPNESS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.cc deleted file mode 100644 index 210d79a8f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.cc +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomSolarizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - - uint8_t threshold_min_ = threshold_[0], threshold_max_ = threshold_[1]; - - CHECK_FAIL_RETURN_UNEXPECTED(threshold_min_ <= threshold_max_, - "RandomSolarize: min of threshold: " + std::to_string(threshold_min_) + - " is greater than max of threshold: " + std::to_string(threshold_max_)); - - float threshold_min = static_cast(std::uniform_int_distribution( - static_cast(threshold_min_), static_cast(threshold_max_))(random_generator_)); - float threshold_max = static_cast(std::uniform_int_distribution( - static_cast(threshold_min_), static_cast(threshold_max_))(random_generator_)); - - if (threshold_max < threshold_min) { - std::swap(threshold_min, threshold_max); - } - return Solarize(input, output, {threshold_min, threshold_max}); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h deleted file mode 100644 index 2a6494666..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SOLARIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SOLARIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomSolarizeOp : public RandomTensorOp { - public: - // Pick a random threshold value to solarize the image with - explicit RandomSolarizeOp(const std::vector &threshold) : threshold_(threshold) {} - - ~RandomSolarizeOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRandomSolarizeOp; } - - private: - std::vector threshold_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_SOLARIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.cc deleted file mode 100644 index e2775243b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.cc +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RandomVerticalFlipOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - const auto output_count = input.size(); - output->resize(output_count); - - for (const auto &image : input) { - RETURN_IF_NOT_OK(ValidateImageDtype("RandomVerticalFlip", image->type())); - RETURN_IF_NOT_OK(ValidateImageRank("RandomVerticalFlip", image->Rank())); - } - - if (distribution_(random_generator_)) { - for (size_t i = 0; i < input.size(); i++) { - RETURN_IF_NOT_OK(VerticalFlip(input[i], &(*output)[i])); - } - return Status::OK(); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h deleted file mode 100644 index e6197d7c4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomVerticalFlipOp : public RandomTensorOp { - public: - explicit RandomVerticalFlipOp(float prob) : distribution_(prob) {} - - ~RandomVerticalFlipOp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomVerticalFlipOp; } - - uint32_t NumInput() override { return 1; } - - uint32_t NumOutput() override { return 1; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc deleted file mode 100644 index 00379bd22..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status RandomVerticalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - RETURN_IF_NOT_OK(ValidateImageDtype("RandomVerticalFlipWithBBox", input[0]->type())); - RETURN_IF_NOT_OK(ValidateImageRank("RandomVerticalFlipWithBBox", input[0]->Rank())); - - if (distribution_(random_generator_)) { - dsize_t imHeight = input[0]->shape()[0]; - size_t boxCount = input[1]->shape()[0]; // number of rows in tensor - - // one time allocation -> updated in the loop - // type defined based on VOC test dataset - for (int i = 0; i < boxCount; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); - - // subtract (curCorner + height) from (max) for new Corner position - BoundingBox::bbox_float newBoxCorner_y = - (static_cast(imHeight) - 1.0F) - ((bbox->y() + bbox->height()) - 1.0F); - bbox->SetY(newBoxCorner_y); - RETURN_IF_NOT_OK(bbox->WriteToTensor(input[1], i)); - } - const int output_count = 2; - output->resize(output_count); - (*output)[1] = input[1]; - - return VerticalFlip(input[0], &(*output)[0]); - } - *output = input; - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h deleted file mode 100644 index a77f600e8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_WITH_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_WITH_BBOX_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RandomVerticalFlipWithBBoxOp : public RandomTensorOp { - public: - // Constructor for RandomVerticalFlipWithBBoxOp - // @param probability: Probablity of Image flipping - explicit RandomVerticalFlipWithBBoxOp(float probability) : distribution_(probability) {} - - ~RandomVerticalFlipWithBBoxOp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kRandomVerticalFlipWithBBoxOp; } - - private: - std::bernoulli_distribution distribution_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RANDOM_VERTICAL_FLIP_WITH_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rescale_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rescale_op.cc deleted file mode 100644 index 9f3080a0c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rescale_op.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rescale_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RescaleOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Rescale(input, output, rescale_, shift_); -} - -Status RescaleOp::OutputType(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Rescale: inputs cannot be empty."); - outputs[0] = DataType(DataType::DE_FLOAT32); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rescale_op.h b/mindspore-lite/minddata/dataset/kernels/image/rescale_op.h deleted file mode 100644 index 74fbf75a0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rescale_op.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESCALE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESCALE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RescaleOp : public TensorOp { - public: - RescaleOp(float rescale, float shift) : rescale_(rescale), shift_(shift) {} - - ~RescaleOp() override = default; - - void Print(std::ostream &out) const override { - out << Name() << ": shift: " << shift_ << ", Rescale: " << rescale_ << std::endl; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - Status OutputType(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kRescaleOp; } - - private: - float rescale_; - float shift_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESCALE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_bilinear_op.h b/mindspore-lite/minddata/dataset/kernels/image/resize_bilinear_op.h deleted file mode 100644 index 0b5bf7b87..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_bilinear_op.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_BILINEAR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_BILINEAR_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ResizeBilinearOp : public ResizeOp { - public: - // Default values, also used by python_bindings.cc - static const int32_t kDefWidth; - - // Name: constructor - // Resizes the image to the output specified size using Bilinear interpolation. - // If only one value is provided, the it will resize the smaller size and maintains - // the aspect ratio. - // @param size1: the first size of output. If only this parameter is provided - // the smaller dimension will be resized to this and then the other dimension changes - // such that the aspect ratio is maintained. - // @param size2: the second size of output. If this is also provided, the output size - // will be (size1, size2) - explicit ResizeBilinearOp(int32_t size1, int32_t size2 = kDefWidth) - : ResizeOp(size1, size2, ResizeOp::kDefInterpolation) {} - - // Name: Destructor - // Description: Destructor - ~ResizeBilinearOp() override = default; - - std::string Name() const override { return kResizeBilinearOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_BILINEAR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.cc b/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.cc deleted file mode 100644 index 6ab8a02a4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.cc +++ /dev/null @@ -1,303 +0,0 @@ -/** - * Copyright 2021-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h" - -#include -#include -#include - -namespace mindspore { -namespace dataset { -// using 8 bits for result -constexpr uint8_t PrecisionBits = 22; -// using 16 bit for clip8 table capacity -constexpr uint16_t Clip8TableCapacity = 640; - -// construct lookup table -static const std::vector _clip8_table = []() { - std::vector v1(896, 0); - std::vector v2(384, 255); - for (int i = 0; i < 256; i++) { - v1[i + Clip8TableCapacity] = i; - } - v1.insert(v1.end(), v2.begin(), v2.end()); - return v1; -}(); - -static const uint8_t *clip8_table = &_clip8_table[Clip8TableCapacity]; - -static inline uint8_t clip8(unsigned int input) { - uint16_t index = input >> PrecisionBits; - if (index >= Clip8TableCapacity) { - return 0; - } - return clip8_table[index]; -} - -static inline double cubic_interp(double x) { - double a = -0.5; - if (x < 0.0) { - x = -x; - } - if (x < 1.0) { - return ((a + 2.0) * x - (a + 3.0)) * x * x + 1; - } - if (x < 2.0) { - return (((x - 5) * x + 8) * x - 4) * a; - } - return 0.0; -} - -struct interpolation { - double (*interpolation)(double x); - double threshold; -}; - -int calc_coeff(int input_size, int out_size, int input0, int input1, const struct interpolation *interp, - std::vector ®ions, std::vector &coeffs_interp) { - double threshold, scale, interp_scale; - int kernel_size; - - if (out_size == 0) { - MS_LOG(ERROR) << "out_size can not be zero."; - return 0; - } - scale = static_cast((input1 - input0)) / out_size; - if (scale < 1.0) { - interp_scale = 1.0; - } else { - interp_scale = scale; - } - - // obtain size - threshold = interp->threshold * interp_scale; - - // coefficients number - kernel_size = static_cast(ceil(threshold)) * 2 + 1; - if (out_size > INT_MAX / (kernel_size * static_cast(sizeof(double)))) { - MS_LOG(WARNING) << "Unable to allocator memory as output Image size is so large."; - return 0; - } - - // coefficient array - std::vector coeffs(out_size * kernel_size, 0.0); - std::vector region(out_size * 2, 0); - - for (int xx = 0; xx < out_size; xx++) { - double center = input0 + (xx + 0.5) * scale; - double mm = 0.0, ss = 1.0 / interp_scale; - int x; - // Round for x_min - int x_min = static_cast(lround(center - threshold)); - if (x_min < 0) { - x_min = 0; - } - // Round for x_max - int x_max = static_cast(lround(center + threshold)); - if (x_max > input_size) { - x_max = input_size; - } - x_max -= x_min; - double *coeff = &coeffs[xx * kernel_size]; - for (x = 0; x < x_max; x++) { - double m = interp->interpolation(((x + x_min) - center + 0.5) * ss); - coeff[x] = m; - mm += m; - } - for (x = 0; x < x_max; x++) { - if (std::fabs(mm) > std::numeric_limits::epsilon()) { - coeff[x] /= mm; - } - } - // Remaining values should stay empty if they are used despite of x_max. - for (; x < kernel_size; x++) { - coeff[x] = 0; - } - region[xx * 2] = x_min; - region[xx * 2 + 1] = x_max; - } - - regions = std::move(region); - coeffs_interp = std::move(coeffs); - return kernel_size; -} - -void normalize_coeff(int out_size, int kernel_size, const std::vector &prekk, std::vector &kk) { - for (int x = 0; x < out_size * kernel_size; x++) { - if (prekk[x] < 0) { - kk[x] = static_cast((-0.5 + prekk[x] * (1 << PrecisionBits))); - } else { - kk[x] = static_cast(lround(prekk[x] * (1 << PrecisionBits))); - } - } -} - -Status ImagingHorizontalInterp(LiteMat &output, LiteMat input, int offset, int kernel_size, - const std::vector ®ions, const std::vector &prekk) { - int ss0, ss1, ss2; - int32_t *k = nullptr; - - // normalize previous calculated coefficients - std::vector kk(prekk.begin(), prekk.end()); - normalize_coeff(output.width_, kernel_size, prekk, kk); - uint8_t *input_ptr = input; - auto *output_ptr = reinterpret_cast(output.data_ptr_); - int32_t input_width = input.width_ * 3; - int32_t output_width = output.width_ * 3; - - for (int yy = 0; yy < output.height_; yy++) { - // obtain the ptr of output, and put calculated value into it - uint8_t *bgr_buf = output_ptr; - for (int xx = 0; xx < output.width_; xx++) { - int x_min = regions[xx * 2]; - int x_max = regions[xx * 2 + 1]; - k = &kk[xx * kernel_size]; - ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); - for (int x = 0; x < x_max; x++) { - ss0 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3]) * k[x]; - ss1 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 1]) * k[x]; - ss2 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 2]) * k[x]; - } - bgr_buf[0] = clip8(ss0); - bgr_buf[1] = clip8(ss1); - bgr_buf[2] = clip8(ss2); - bgr_buf += 3; - } - output_ptr += output_width; - } - return Status::OK(); -} - -Status ImagingVerticalInterp(const LiteMat &output, LiteMat input, int kernel_size, const std::vector ®ions, - const std::vector &prekk) { - int ss0, ss1, ss2; - - // normalize previous calculated coefficients - std::vector kk(prekk.begin(), prekk.end()); - normalize_coeff(output.height_, kernel_size, prekk, kk); - uint8_t *input_ptr = input; - auto *output_ptr = reinterpret_cast(output.data_ptr_); - const int32_t input_width = input.width_ * 3; - const int32_t output_width = output.width_ * 3; - - for (int yy = 0; yy < output.height_; yy++) { - // obtain the ptr of output, and put calculated value into it - uint8_t *bgr_buf = output_ptr; - int32_t *k = &kk[yy * kernel_size]; - int y_min = regions[yy * 2]; - int y_max = regions[yy * 2 + 1]; - for (int xx = 0; xx < output.width_; xx++) { - ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); - for (int y = 0; y < y_max; y++) { - ss0 += (input_ptr[(y + y_min) * input_width + xx * 3]) * k[y]; - ss1 += (input_ptr[(y + y_min) * input_width + xx * 3 + 1]) * k[y]; - ss2 += (input_ptr[(y + y_min) * input_width + xx * 3 + 2]) * k[y]; - } - bgr_buf[0] = clip8(ss0); - bgr_buf[1] = clip8(ss1); - bgr_buf[2] = clip8(ss2); - bgr_buf += 3; - } - output_ptr += output_width; - } - return Status::OK(); -} - -bool ImageInterpolation(LiteMat input, LiteMat &output, int x_size, int y_size, struct interpolation *interp, - const int rect[4]) { - int horizontal_interp, vertical_interp, horiz_kernel, vert_kernel, rect_y0, rect_y1; - std::vector horiz_region, vert_region; - std::vector horiz_coeff, vert_coeff; - LiteMat temp; - - horizontal_interp = x_size != input.width_ || rect[2] != x_size || rect[0]; - vertical_interp = y_size != input.height_ || rect[3] != y_size || rect[1]; - - horiz_kernel = calc_coeff(input.width_, x_size, rect[0], rect[2], interp, horiz_region, horiz_coeff); - if (!horiz_kernel) { - return false; - } - - vert_kernel = calc_coeff(input.height_, y_size, rect[1], rect[3], interp, vert_region, vert_coeff); - if (!vert_kernel) { - return false; - } - - // first and last used row in the input image - rect_y0 = vert_region[0]; - rect_y1 = vert_region[y_size * 2 - 1] + vert_region[y_size * 2 - 2]; - - // two-direction resize, horizontal resize - if (horizontal_interp) { - // Shift region for vertical resize - for (int i = 0; i < y_size; i++) { - vert_region[i * 2] -= rect_y0; - } - temp.Init(x_size, rect_y1 - rect_y0, 3, LDataType::UINT8, false); - if (temp.IsEmpty()) { - MS_LOG(ERROR) << "Image horizontal resize failed, got empty tensor"; - return false; - } - auto rc = ImagingHorizontalInterp(temp, input, rect_y0, horiz_kernel, horiz_region, horiz_coeff); - if (rc.IsError()) { - MS_LOG(ERROR) << "Image horizontal resize failed, error msg is " << rc; - return false; - } - output = input = temp; - } - - /* vertical resize */ - if (vertical_interp) { - output.Init(input.width_, y_size, 3, LDataType::UINT8, false); - if (!output.IsEmpty()) { - auto rc = ImagingVerticalInterp(output, input, vert_kernel, vert_region, vert_coeff); - if (rc.IsError()) { - MS_LOG(ERROR) << "Image vertical resize failed, error msg is " << rc; - return false; - } - } - if (output.IsEmpty()) { - return false; - } - } - if (!horizontal_interp && !vertical_interp) { - output = input; - } - return true; -} - -bool ResizeCubic(const LiteMat &input, const LiteMat &dst, int dst_w, int dst_h) { - if (input.data_type_ != LDataType::UINT8 || input.channel_ != 3) { - MS_LOG(ERROR) << "Unsupported data type, only support input image of uint8 dtype and 3 channel, got channel: " + - std::to_string(input.channel_); - return false; - } - int x_size = dst_w, y_size = dst_h; - int rect[4] = {0, 0, input.width_, input.height_}; - LiteMat output; - - struct interpolation interp = {cubic_interp, 2.0}; - bool res = ImageInterpolation(input, output, x_size, y_size, &interp, rect); - - auto ret_code = memcpy_s(dst.data_ptr_, output.size_, output.data_ptr_, output.size_); - if (ret_code != 0) { - MS_LOG(ERROR) << "memcpy_s failed when copying tensor."; - return false; - } - return res; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h b/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h deleted file mode 100644 index cb31c148e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_cubic_op.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_CUBIC_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_CUBIC_OP_H_ - -#include -#include -#include - -#include "lite_cv/lite_mat.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \brief Calculate the coefficient for interpolation firstly -int calc_coeff(int input_size, int out_size, int input0, int input1, const struct interpolation *interp, - std::vector ®ions, std::vector &coeffs_interp); - -/// \brief Normalize the coefficient for interpolation -void normalize_coeff(int out_size, int kernel_size, const std::vector &prekk, std::vector &kk); - -/// \brief Apply horizontal interpolation on input image -Status ImagingHorizontalInterp(LiteMat &output, LiteMat input, int offset, int kernel_size, - const std::vector ®ions, const std::vector &prekk); - -/// \brief Apply Vertical interpolation on input image -Status ImagingVerticalInterp(LiteMat &output, LiteMat input, int offset, int kernel_size, - const std::vector ®ions, const std::vector &prekk); - -/// \brief Mainly logic of Cubic interpolation -bool ImageInterpolation(LiteMat input, LiteMat &output, int x_size, int y_size, struct interpolation *interp, - const int rect[4]); - -/// \brief Apply cubic interpolation on input image and obtain the output image -/// \param[in] input Input image -/// \param[out] dst Output image -/// \param[in] dst_w expected Output image width -/// \param[in] dst_h expected Output image height -bool ResizeCubic(const LiteMat &input, const LiteMat &dst, int dst_w, int dst_h); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_CUBIC_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/resize_op.cc deleted file mode 100644 index d53f50443..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_op.cc +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const int32_t ResizeOp::kDefWidth = 0; -const InterpolationMode ResizeOp::kDefInterpolation = InterpolationMode::kLinear; - -Status ResizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "Resize", {1, 2, 3, 4, 5, 6, 10, 11, 12})); - std::vector size; - RETURN_IF_NOT_OK(ImageSize(input, &size)); - auto input_h = static_cast(size[kHeightIndex]); - auto input_w = static_cast(size[kWidthIndex]); - int32_t output_h; - int32_t output_w; - if (size2_ == 0) { - if (input_h < input_w) { - CHECK_FAIL_RETURN_UNEXPECTED(input_h != 0, "Resize: the input height cannot be 0."); - output_h = size1_; - output_w = static_cast( - std::floor(static_cast(input_w) / static_cast(input_h) * static_cast(output_h))); - } else { - CHECK_FAIL_RETURN_UNEXPECTED(input_w != 0, "Resize: the input width cannot be 0."); - output_w = size1_; - output_h = static_cast( - std::floor(static_cast(input_h) / static_cast(input_w) * static_cast(output_w))); - } - } else { - output_h = size1_; - output_w = size2_; - } - if (input_h == output_h && input_w == output_w) { - *output = input; - return Status::OK(); - } - // [H, W] or [H, W, C] - if (input->Rank() <= kDefaultImageRank) { - RETURN_IF_NOT_OK(Resize(input, output, output_h, output_w, 0, 0, interpolation_)); - } else { - // reshape [..., H, W, C] to [N, H, W, C] - TensorShape original_shape = input->shape(); - dsize_t num_batch = input->Size() / (original_shape[-3] * original_shape[-2] * original_shape[-1]); - TensorShape new_shape({num_batch, original_shape[-3], original_shape[-2], original_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and Resize N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (const auto &input_hwc : input_vector_hwc) { - std::shared_ptr output_img; - RETURN_IF_NOT_OK(Resize(input_hwc, &output_img, output_h, output_w, 0, 0, interpolation_)); - output_vector_hwc.push_back(output_img); - } - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, &(*output))); - auto output_shape = ComputeOutputShape(original_shape, output_h, output_w); - RETURN_IF_NOT_OK((*output)->Reshape(output_shape)); - } - return Status::OK(); -} - -Status ResizeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - int32_t outputH = -1, outputW = -1; - // if size2_ == 0, then we cannot know the shape. We need the input image to find the output shape --> set it to - // <-1,-1[,3]> - if (size2_ != 0) { - outputH = size1_; - outputW = size2_; - } - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Resize: inputs cannot be empty."); - if (inputs[0].Rank() < kMinImageRank) { - std::string err_msg = - "Resize: input tensor should have at least 2 dimensions, but got: " + std::to_string(inputs[0].Rank()); - RETURN_STATUS_UNEXPECTED(err_msg); - } else if (inputs[0].Rank() == kMinImageRank) { - TensorShape out = TensorShape{outputH, outputW}; - (void)outputs.emplace_back(out); - } else if (inputs[0].Rank() >= kDefaultImageRank) { - auto out_shape = ComputeOutputShape(inputs[0], outputH, outputW); - (void)outputs.emplace_back(out_shape); - } - return Status::OK(); -} - -TensorShape ResizeOp::ComputeOutputShape(const TensorShape &input, int32_t output_h, int32_t output_w) { - const int kHeightIndexFromBack = -3; - const int kWidthIndexFromBack = -2; - auto out_shape_vec = input.AsVector(); - auto size = out_shape_vec.size(); - out_shape_vec[size + kHeightIndexFromBack] = output_h; - out_shape_vec[size + kWidthIndexFromBack] = output_w; - TensorShape out = TensorShape(out_shape_vec); - return out; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_op.h b/mindspore-lite/minddata/dataset/kernels/image/resize_op.h deleted file mode 100644 index db81abec4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_op.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -namespace mindspore { -namespace dataset { -class ResizeOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const int32_t kDefWidth; - static const InterpolationMode kDefInterpolation; - - // Resizes the image to the output specified size. If only one value is provided, - // the it will resize the smaller size and maintains the aspect ratio. - // @param size1: the first size of output. If only this parameter is provided - // the smaller dimension will be resized to this and then the other dimension changes - // such that the aspect ratio is maintained. - // @param size2: the second size of output. If this is also provided, the output size - // will be (size1, size2) - // @param InterpolationMode: the interpolation mode being used. - explicit ResizeOp(int32_t size1, int32_t size2 = kDefWidth, InterpolationMode interpolation = kDefInterpolation) - : size1_(size1), size2_(size2), interpolation_(interpolation) {} - - ResizeOp(const ResizeOp &rhs) = default; - - ResizeOp(ResizeOp &&rhs) = default; - - ~ResizeOp() override = default; - - void Print(std::ostream &out) const override { out << Name() << ": " << size1_ << " " << size2_; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - static TensorShape ComputeOutputShape(const TensorShape &input, int32_t output_h, int32_t output_w); - - std::string Name() const override { return kResizeOp; } - - protected: - int32_t size1_; - int32_t size2_; - InterpolationMode interpolation_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.cc b/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.cc deleted file mode 100644 index 681d94e4a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h" - -#ifdef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const int32_t ResizePreserveAROp::kDefImgOrientation = 0; - -ResizePreserveAROp::ResizePreserveAROp(int32_t height, int32_t width, int32_t img_orientation) - : height_(height), width_(width), img_orientation_(img_orientation) {} - -Status ResizePreserveAROp::Compute(const TensorRow &inputs, TensorRow *outputs) { - IO_CHECK_VECTOR(inputs, outputs); -#ifdef ENABLE_ANDROID - return ResizePreserve(inputs, height_, width_, img_orientation_, outputs); -#endif - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h b/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h deleted file mode 100644 index d8956e901..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_PRESERVE_AR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_PRESERVE_AR_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ResizePreserveAROp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const int32_t kDefImgOrientation; - - ResizePreserveAROp(int32_t height, int32_t width, int32_t img_orientation = kDefImgOrientation); - - ~ResizePreserveAROp() override = default; - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kResizePreserveAROp; } - - protected: - int32_t height_; - int32_t width_; - int32_t img_orientation_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_PRESERVE_AR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.cc b/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.cc deleted file mode 100644 index a415eabc9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status ResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - - auto input_h = static_cast(input[0]->shape()[0]); - auto input_w = static_cast(input[0]->shape()[1]); - - output->resize(2); - (*output)[1] = input[1]; // move boxes over to output - - std::shared_ptr input_cv = CVTensor::AsCVTensor(input[0]); - - RETURN_IF_NOT_OK(ResizeOp::Compute(std::static_pointer_cast(input_cv), &(*output)[0])); - - auto output_h = static_cast((*output)[0]->shape()[0]); // output height if ResizeWithBBox - auto output_w = static_cast((*output)[0]->shape()[1]); // output width if ResizeWithBBox - - size_t bboxCount = input[1]->shape()[0]; // number of rows in bbox tensor - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, output_w, output_h, input_w, input_h)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h b/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h deleted file mode 100644 index b2bc0f6ac..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_WITH_BBOX_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_WITH_BBOX_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ResizeWithBBoxOp : public ResizeOp { - public: - // Constructor for ResizeWithBBoxOp, with default value and passing to base class constructor - explicit ResizeWithBBoxOp(int32_t size_1, int32_t size_2 = kDefWidth, - InterpolationMode mInterpolation = kDefInterpolation) - : ResizeOp(size_1, size_2, mInterpolation) {} - - ~ResizeWithBBoxOp() override = default; - - void Print(std::ostream &out) const override { out << Name() << ": " << size1_ << " " << size2_; } - - // Use in pipeline mode - Status Compute(const TensorRow &input, TensorRow *output) override; - - // Use in execute mode - // ResizeWithBBoxOp is inherited from ResizeOp and this function has been overridden by ResizeOp, - // thus we need to change the behavior back to basic class (TensorOp). - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override { - return TensorOp::Compute(input, output); - } - - std::string Name() const override { return kResizeWithBBoxOp; } - - uint32_t NumInput() override { return 2; } - - uint32_t NumOutput() override { return 2; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZE_WITH_BBOX_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.cc b/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.cc deleted file mode 100644 index a1b56e4f5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status ResizedCropOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - // input output tensor shape check - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageRank("ResizedCropOp", input->Rank())); - - std::vector input_size; - RETURN_IF_NOT_OK(ImageSize(input, &input_size)); - auto input_h = static_cast(input_size[kHeightIndex]); - auto input_w = static_cast(input_size[kWidthIndex]); - int32_t size1 = size_[kHeightIndex]; - int32_t size2 = size_[kWidthIndex]; - - // crop check - CHECK_FAIL_RETURN_UNEXPECTED(top_ + height_ <= input_h, - "ResizedCrop: the sum of top and height: " + std::to_string(top_ + height_) + - " exceeds image height: " + std::to_string(input_h)); - CHECK_FAIL_RETURN_UNEXPECTED(left_ + width_ <= input_w, - "ResizedCrop: the sum of left and width: " + std::to_string(left_ + width_) + - " exceeds image width: " + std::to_string(input_w)); - - int32_t output_h; - int32_t output_w; - if (size2 == 0) { - if (input_h < input_w) { - output_h = size1; - output_w = static_cast( - roundf(static_cast(width_) / static_cast(height_) * static_cast(output_h))); - } else { - output_w = size1; - output_h = static_cast( - roundf(static_cast(height_) / static_cast(width_) * static_cast(output_w))); - } - } else { - output_h = size1; - output_w = size2; - } - - RETURN_IF_NOT_OK(CropAndResize(input, output, left_, top_, height_, width_, output_h, output_w, interpolation_)); - - return Status::OK(); -} - -Status ResizedCropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - - int32_t size1 = size_[kHeightIndex]; - int32_t size2 = size_[kWidthIndex]; - int32_t output_h = 0; - int32_t output_w = 0; - - if (size2 != 0) { - output_h = size1; - output_w = size2; - } - - TensorShape out = TensorShape{output_h, output_w}; - const TensorShape &input_shape = inputs.front(); - if (input_shape.Rank() == kMinImageRank) { - (void)outputs.emplace_back(out); - } - if (input_shape.Rank() == kDefaultImageRank) { - (void)outputs.emplace_back(out.AppendDim(input_shape[input_shape.Size() - 1])); - } - CHECK_FAIL_RETURN_UNEXPECTED(!outputs.empty(), - "ResizedCrop: input tensor is not in shape of or , but got rank: " + - std::to_string(input_shape.Rank())); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h b/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h deleted file mode 100644 index ea883db8c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZED_CROP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZED_CROP_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ResizedCropOp : public TensorOp { - public: - ResizedCropOp(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &size, - InterpolationMode interpolation) - : top_(top), left_(left), height_(height), width_(width), size_(size), interpolation_(interpolation) {} - - ~ResizedCropOp() override = default; - - void Print(std::ostream &out) const override { - out << Name() << ": " << top_ << " " << left_ << " " << height_ << " " << width_; - } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kResizedCropOp; } - - protected: - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - const std::vector size_; - InterpolationMode interpolation_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_RESIZED_CROP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.cc deleted file mode 100644 index 918450b5d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif - -namespace mindspore { -namespace dataset { -Status RgbToBgrOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - auto input_type = input->type(); - CHECK_FAIL_RETURN_UNEXPECTED( - input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && input_type != DataType::DE_INT64 && - !input_type.IsString(), - "RgbToBgr: Input includes unsupported data type in [uint32, int64, uint64, string, bytes]."); - return RgbToBgr(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h b/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h deleted file mode 100644 index 59b34de49..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_BGR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_BGR_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RgbToBgrOp : public TensorOp { - public: - RgbToBgrOp() = default; - - ~RgbToBgrOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRgbToBgrOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_BGR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.cc deleted file mode 100644 index 96ff4f766..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif - -namespace mindspore { -namespace dataset { -Status RgbToGrayOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return RgbToGray(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h b/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h deleted file mode 100644 index 26174c42d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_GRAY_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_GRAY_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RgbToGrayOp : public TensorOp { - public: - RgbToGrayOp() = default; - - ~RgbToGrayOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRgbToGrayOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGB_TO_GRAY_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.cc deleted file mode 100644 index 611a85720..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RgbaToBgrOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return RgbaToBgr(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h b/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h deleted file mode 100644 index 3466d6d3c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_BGR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_BGR_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RgbaToBgrOp : public TensorOp { - public: - RgbaToBgrOp() = default; - - ~RgbaToBgrOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRgbaToBgrOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_BGR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.cc deleted file mode 100644 index 3bcc4959a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status RgbaToRgbOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return RgbaToRgb(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h b/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h deleted file mode 100644 index d0840a38b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_RGB_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_RGB_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RgbaToRgbOp : public TensorOp { - public: - RgbaToRgbOp() = default; - - ~RgbaToRgbOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kRgbaToRgbOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_RGBA_TO_RGB_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/rotate_op.cc b/mindspore-lite/minddata/dataset/kernels/image/rotate_op.cc deleted file mode 100644 index 5f542d9f9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rotate_op.cc +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/rotate_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif - -namespace mindspore { -namespace dataset { -const std::vector RotateOp::kDefCenter = {}; -const InterpolationMode RotateOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; -const bool RotateOp::kDefExpand = false; -const uint8_t RotateOp::kDefFillR = 0; -const uint8_t RotateOp::kDefFillG = 0; -const uint8_t RotateOp::kDefFillB = 0; - -RotateOp::RotateOp(int angle_id) - : angle_id_(angle_id), - degrees_(0), - center_({}), - interpolation_(InterpolationMode::kLinear), - expand_(false), - fill_r_(0), - fill_g_(0), - fill_b_(0) {} - -RotateOp::RotateOp(float degrees, InterpolationMode resample, bool expand, std::vector center, uint8_t fill_r, - uint8_t fill_g, uint8_t fill_b) - : angle_id_(0), - degrees_(degrees), - center_(std::move(center)), - interpolation_(resample), - expand_(expand), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} - -Status RotateOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "Rotate", {1, 2, 3, 4, 5, 6, 10, 11, 12})); - if (input->Rank() <= kDefaultImageRank) { - // [H, W] or [H, W, C] -#ifndef ENABLE_ANDROID - RETURN_IF_NOT_OK(Rotate(input, output, center_, degrees_, interpolation_, expand_, fill_r_, fill_g_, fill_b_)); -#else - RETURN_IF_NOT_OK(Rotate(input, output, angle_id_)); -#endif - } else { - // reshape [..., H, W, C] to [N, H, W, C] - auto original_shape = input->shape(); - dsize_t num_batch = input->Size() / (original_shape[-3] * original_shape[-2] * original_shape[-1]); - TensorShape new_shape({num_batch, original_shape[-3], original_shape[-2], original_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and Rotate N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (const auto &input_hwc : input_vector_hwc) { - std::shared_ptr output_img; -#ifndef ENABLE_ANDROID - RETURN_IF_NOT_OK( - Rotate(input_hwc, &output_img, center_, degrees_, interpolation_, expand_, fill_r_, fill_g_, fill_b_)); -#else - RETURN_IF_NOT_OK(Rotate(input_hwc, &output_img, angle_id_)); -#endif - output_vector_hwc.push_back(output_img); - } - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, output)); - // reshape output before return, only height and width are changed - auto output_shape_new = ConstructShape(original_shape); - RETURN_IF_NOT_OK((*output)->Reshape(output_shape_new)); - } - return Status::OK(); -} - -Status RotateOp::OutputShape(const std::vector &inputs, std::vector &outputs) { -#ifndef ENABLE_ANDROID - RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); - outputs.clear(); - int32_t outputH = -1, outputW = -1; - // if expand_, then we cannot know the shape. We need the input image to find the output shape --> set it to - // <-1,-1[,3]> - CHECK_FAIL_RETURN_UNEXPECTED(!inputs.empty(), "Rotate: inputs cannot be empty."); - if (inputs[0].Rank() < kMinImageRank) { - std::string err_msg = - "Rotate: input tensor should have at least 2 dimensions, but got: " + std::to_string(inputs[0].Rank()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (!expand_) { - outputH = static_cast(inputs[0][0]); - outputW = static_cast(inputs[0][1]); - } - TensorShape out = TensorShape{outputH, outputW}; - if (inputs[0].Rank() == kMinImageRank) { - (void)outputs.emplace_back(out); - } - if (inputs[0].Rank() == kDefaultImageRank) { - outputs.emplace_back(out.AppendDim(inputs[0][kChannelIndexHWC])); - } - if (inputs[0].Rank() > kDefaultImageRank) { - auto out_shape = ConstructShape(inputs[0]); - (void)outputs.emplace_back(out_shape); - } - return Status::OK(); -#else - if (inputs.size() != NumInput()) { - return Status(StatusCode::kMDUnexpectedError, - "The size of the input argument vector: " + std::to_string(inputs.size()) + - ", does not match the number of inputs: " + std::to_string(NumInput())); - } - outputs = inputs; - return Status::OK(); -#endif -} - -TensorShape RotateOp::ConstructShape(const TensorShape &in_shape) const { - auto in_shape_vec = in_shape.AsVector(); - const int h_index = -3; - const int w_index = -2; - int32_t outputH = -1; - int32_t outputW = -1; - if (!expand_) { - outputH = static_cast(in_shape[h_index]); - outputW = static_cast(in_shape[w_index]); - } - in_shape_vec[in_shape_vec.size() + h_index] = outputH; - in_shape_vec[in_shape_vec.size() + w_index] = outputW; - TensorShape out = TensorShape(in_shape_vec); - return out; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/rotate_op.h b/mindspore-lite/minddata/dataset/kernels/image/rotate_op.h deleted file mode 100644 index 79b57ea62..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/rotate_op.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ROTATE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ROTATE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class RotateOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const std::vector kDefCenter; - static const InterpolationMode kDefInterpolation; - static const bool kDefExpand; - static const uint8_t kDefFillR; - static const uint8_t kDefFillG; - static const uint8_t kDefFillB; - - /// Constructor - explicit RotateOp(int angle_id); - - explicit RotateOp(float degrees, InterpolationMode resample = kDefInterpolation, bool expand = kDefExpand, - std::vector center = kDefCenter, uint8_t fill_r = kDefFillR, uint8_t fill_g = kDefFillG, - uint8_t fill_b = kDefFillB); - - ~RotateOp() override = default; - - TensorShape ConstructShape(const TensorShape &in_shape) const; - - Status OutputShape(const std::vector &inputs, std::vector &outputs) override; - - std::string Name() const override { return kRotateOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - void setAngle(uint64_t angle_id) { angle_id_ = angle_id; } - - /// Member variables - protected: - uint64_t angle_id_; - - private: - float degrees_; - std::vector center_; - InterpolationMode interpolation_; - bool expand_; - uint8_t fill_r_; - uint8_t fill_g_; - uint8_t fill_b_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ROTATE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.cc b/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.cc deleted file mode 100644 index 3be4a47b2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.cc +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const float SharpnessOp::kDefAlpha = 1.0; - -Status SharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return AdjustSharpness(input, output, alpha_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h b/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h deleted file mode 100644 index 2c4facf60..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SHARPNESS_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SHARPNESS_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class SharpnessOp : public TensorOp { - public: - /// Default values, also used by bindings.cc - static const float kDefAlpha; - - /// This class can be used to adjust the sharpness of an image. - /// \@param[in] alpha A float indicating the enhancement factor. - /// a factor of 0.0 gives a blurred image, a factor of 1.0 gives the - /// original image, and a factor of 2.0 gives a sharpened image. - - explicit SharpnessOp(const float alpha = kDefAlpha) : alpha_(alpha) {} - - ~SharpnessOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kSharpnessOp; } - - protected: - float alpha_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SHARPNESS_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.cc b/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.cc deleted file mode 100644 index 0b04b61be..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.cc +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -const int32_t SlicePatchesOp::kDefNumH = 1; -const int32_t SlicePatchesOp::kDefNumW = 1; -const uint8_t SlicePatchesOp::kDefFillV = 0; -const SliceMode SlicePatchesOp::kDefSliceMode = SliceMode::kPad; - -SlicePatchesOp::SlicePatchesOp(int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) - : num_height_(num_height), num_width_(num_width), slice_mode_(slice_mode), fill_value_(fill_value) {} - -Status SlicePatchesOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - CHECK_FAIL_RETURN_UNEXPECTED( - input.size() == 1, - "size of input should be 1, which means 'input_columns' should be 1 when call this operator, but got:" + - std::to_string(input.size())); - - const auto &in_tensor = input[0]; - auto in_type = in_tensor->type(); - auto in_shape = in_tensor->shape(); - - CHECK_FAIL_RETURN_UNEXPECTED(in_type.IsNumeric(), "Input Tensor type should be numeric, got type is non-numeric."); - CHECK_FAIL_RETURN_UNEXPECTED( - in_shape.Rank() >= 2, "Rank of input data should be greater than 2, but got:" + std::to_string(in_shape.Rank())); - - std::vector> out; - RETURN_IF_NOT_OK(SlicePatches(in_tensor, &out, num_height_, num_width_, slice_mode_, fill_value_)); - (void)std::copy(out.begin(), out.end(), std::back_inserter(*output)); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h b/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h deleted file mode 100644 index 738280c2f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SLICE_PATCHES_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SLICE_PATCHES_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class SlicePatchesOp : public TensorOp { - public: - // Default values, also used by python_bindings.cc - static const int32_t kDefNumH; - static const int32_t kDefNumW; - static const uint8_t kDefFillV; - static const SliceMode kDefSliceMode; - - explicit SlicePatchesOp(int32_t num_height = kDefNumH, int32_t num_width = kDefNumW, - SliceMode slice_mode = kDefSliceMode, uint8_t fill_value = kDefFillV); - - ~SlicePatchesOp() override = default; - - void Print(std::ostream &out) const override { - out << Name() << " patches number on height: " << num_height_ << ", patches number on width: " << num_width_; - } - - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kSlicePatchesOp; } - - protected: - int32_t num_height_; // number of patches on height axis - int32_t num_width_; // number of patches on width axis - SliceMode slice_mode_; // PadModel, DropModel - uint8_t fill_value_; // border width in number of pixels in right and bottom direction -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SLICE_PATCHES_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/solarize_op.cc b/mindspore-lite/minddata/dataset/kernels/image/solarize_op.cc deleted file mode 100644 index 5ab2be9ff..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/solarize_op.cc +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/solarize_op.h" - -#include "mindspore-lite/minddata/dataset/core/cv_tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status SolarizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return Solarize(input, output, threshold_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/solarize_op.h b/mindspore-lite/minddata/dataset/kernels/image/solarize_op.h deleted file mode 100644 index f663604d7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/solarize_op.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SOLARIZE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SOLARIZE_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class SolarizeOp : public TensorOp { - public: - explicit SolarizeOp(const std::vector &threshold) : threshold_(threshold) {} - - ~SolarizeOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kSolarizeOp; } - - protected: - std::vector threshold_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_SOLARIZE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.cc b/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.cc deleted file mode 100644 index 9b24c74d4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status SwapRedBlueOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - return SwapRedAndBlue(input, output); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h b/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h deleted file mode 100644 index cbd6ce5f0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_SWAP_RED_BLUE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_SWAP_RED_BLUE_OP_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class SwapRedBlueOp : public TensorOp { - public: - /// \brief Constructor - SwapRedBlueOp() = default; - - SwapRedBlueOp(const SwapRedBlueOp &rhs) = default; - - SwapRedBlueOp(SwapRedBlueOp &&rhs) = default; - - ~SwapRedBlueOp() override = default; - - void Print(std::ostream &out) const override { out << "SwapRedBlueOp"; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kSwapRedBlueOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_SWAP_RED_BLUE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.cc b/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.cc deleted file mode 100644 index 0eaebf195..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -Status ToTensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - auto input_type = input->type(); - CHECK_FAIL_RETURN_UNEXPECTED( - input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && input_type != DataType::DE_INT64 && - !input_type.IsString(), - "ToTensor: Input includes unsupported data type in [uint32, int64, uint64, string, bytes]."); - // Rescale and convert HWC to CHW format - return ToTensor(input, output, output_type_); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h b/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h deleted file mode 100644 index 533db0984..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TO_TENSOR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TO_TENSOR_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class ToTensorOp : public TensorOp { - public: - explicit ToTensorOp(const DataType::Type &output_type) : output_type_(output_type) {} - - explicit ToTensorOp(const std::string &output_type) : output_type_(DataType(output_type)) {} - - ~ToTensorOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kToTensorOp; } - - private: - DataType output_type_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TO_TENSOR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.cc b/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.cc deleted file mode 100644 index 3896a7082..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -TrivialAugmentWideOp::TrivialAugmentWideOp(int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value) - : num_magnitude_bins_(num_magnitude_bins), interpolation_(interpolation), fill_value_(fill_value) {} - -Status TrivialAugmentWideOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "TrivialAugmentWide", {3}, {3}, {3})); - - std::vector image_size; - RETURN_IF_NOT_OK(ImageSize(input, &image_size)); - Space space = GetSpace(num_magnitude_bins_); - size_t space_size = space.size(); - std::vector op_name_list; - (void)std::for_each( - space.begin(), space.end(), - [&op_name_list](const std::map, bool>>::value_type &p) { - op_name_list.push_back(p.first); - }); - - int32_t op_index = RandInt(0, static_cast(space_size)); - const std::string op_name = op_name_list[static_cast(op_index)]; - std::vector magnitudes = std::get<0>(space[op_name]); - bool sign = std::get<1>(space[op_name]); - float magnitude = 0.0; - if (magnitudes.size() != 1) { - int32_t magnitude_index = RandInt(0, static_cast(magnitudes.size())); - magnitude = magnitudes[static_cast(magnitude_index)]; - } - const int kRandUpperBound = 2; - int32_t random_number = RandInt(0, kRandUpperBound); - if (static_cast(sign) && random_number) { - magnitude *= -1.0; - } - std::shared_ptr img = input; - RETURN_IF_NOT_OK(ApplyAugment(img, &img, op_name, magnitude, interpolation_, fill_value_)); - *output = img; - return Status::OK(); -} - -Space TrivialAugmentWideOp::GetSpace(int32_t num_bins) { - Space space = {{"Identity", {{0.0}, false}}, - {"ShearX", {Linspace(0.0, 0.99, num_bins), true}}, - {"ShearY", {Linspace(0.0, 0.99, num_bins), true}}, - {"TranslateX", {Linspace(0.0, 32.0, num_bins), true}}, - {"TranslateY", {Linspace(0.0, 32.0, num_bins), true}}, - {"Rotate", {Linspace(0.0, 135.0, num_bins), true}}, - {"Brightness", {Linspace(0.0, 0.99, num_bins), true}}, - {"Color", {Linspace(0.0, 0.99, num_bins), true}}, - {"Contrast", {Linspace(0.0, 0.99, num_bins), true}}, - {"Sharpness", {Linspace(0.0, 0.99, num_bins), true}}, - {"Posterize", - {Linspace(0.0, static_cast(num_bins) - 1.F, num_bins, - -6.0F / (static_cast(num_bins) - 1.F), 8, true), - false}}, - {"Solarize", {Linspace(255.0, 0.0, num_bins), false}}, - {"AutoContrast", {{0.0}, false}}, - {"Equalize", {{0.0}, false}}}; - return space; -} - -int32_t TrivialAugmentWideOp::RandInt(int32_t low, int32_t high) { - std::uniform_int_distribution dis(low, high); - return dis(random_generator_) % (high - low) + low; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h b/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h deleted file mode 100644 index cef441888..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TRIVIAL_AUGMENT_WIDE_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TRIVIAL_AUGMENT_WIDE_OP_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#else -#include "mindspore-lite/minddata/dataset/kernels/image/lite_image_utils.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/image/math_utils.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -typedef std::map, bool>> Space; - -namespace mindspore { -namespace dataset { -constexpr char kTrivialAugmentWideOp[] = "TrivialAugmentWideOp"; - -class TrivialAugmentWideOp : public RandomTensorOp { - public: - TrivialAugmentWideOp(int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value); - - ~TrivialAugmentWideOp() override = default; - - std::string Name() const override { return kTrivialAugmentWideOp; } - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - private: - static Space GetSpace(int32_t num_bins); - - int32_t RandInt(int32_t low, int32_t high); - - int32_t num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_TRIVIAL_AUGMENT_WIDE_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.cc b/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.cc deleted file mode 100644 index caca15969..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.cc +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - - * 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. -*/ - -#include "mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/util/random.h" - -namespace mindspore { -namespace dataset { -UniformAugOp::UniformAugOp(std::vector> op_list, int32_t num_ops) - : tensor_op_list_(std::move(op_list)), num_ops_(num_ops) {} - -// compute method to apply uniformly random selected augmentations from a list -Status UniformAugOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - - // randomly select ops to be applied - std::vector> selected_tensor_ops; - std::sample(tensor_op_list_.begin(), tensor_op_list_.end(), std::back_inserter(selected_tensor_ops), num_ops_, - random_generator_); - - bool first = true; - for (const auto &tensor_op : selected_tensor_ops) { - // Do NOT apply the op, if second random generator returned zero - if (std::uniform_int_distribution(0, 1)(random_generator_)) { - continue; - } - // apply C++ ops (note: python OPs are not accepted) - if (first) { - RETURN_IF_NOT_OK(tensor_op->Compute(input, output)); - first = false; - } else { - TensorRow tmp; - RETURN_IF_NOT_OK(tensor_op->Compute(*output, &tmp)); - *output = std::move(tmp); - } - } - - // The case where no tensor op is applied. - if (output->empty()) { - *output = input; - } - - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h b/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h deleted file mode 100644 index 932137e7f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - - * 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. -*/ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class UniformAugOp : public RandomTensorOp { - public: - // Constructor for UniformAugOp - // @param std::vector> op_list: list of candidate C++ operations - // @param int32_t num_ops: number of augemtation operations to applied - UniformAugOp(std::vector> op_list, int32_t num_ops); - - // Destructor - ~UniformAugOp() override = default; - - void Print(std::ostream &out) const override { out << Name() << ":: number of ops " << num_ops_; } - - // Overrides the base class compute function - // @return Status The status code returned - Status Compute(const TensorRow &input, TensorRow *output) override; - - std::string Name() const override { return kUniformAugOp; } - - private: - std::vector> tensor_op_list_; - int32_t num_ops_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.cc b/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.cc deleted file mode 100644 index 496bd41d6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.cc +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/data/data_utils.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -Status VerticalFlipOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImage(input, "VerticalFlip", {1, 2, 3, 4, 5, 6, 10, 11, 12})); - dsize_t rank = input->shape().Rank(); - if (rank <= kDefaultImageRank) { - // [H, W] or [H, W, C] - RETURN_IF_NOT_OK(VerticalFlip(input, output)); - } else { - // reshape [..., H, W, C] to [N, H, W, C] - auto input_shape = input->shape(); - dsize_t num_batch = input->Size() / (input_shape[-3] * input_shape[-2] * input_shape[-1]); - TensorShape new_shape({num_batch, input_shape[-3], input_shape[-2], input_shape[-1]}); - RETURN_IF_NOT_OK(input->Reshape(new_shape)); - - // split [N, H, W, C] to N [H, W, C], and vertical flip N [H, W, C] - std::vector> input_vector_hwc, output_vector_hwc; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input, &input_vector_hwc)); - for (int i = 0; i < num_batch; i++) { - std::shared_ptr flip; - RETURN_IF_NOT_OK(VerticalFlip(input_vector_hwc[i], &flip)); - output_vector_hwc.push_back(flip); - } - - // integrate N [H, W, C] to [N, H, W, C], and reshape [..., H, W, C] - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(output_vector_hwc, output)); - RETURN_IF_NOT_OK((*output)->Reshape(input_shape)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h b/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h deleted file mode 100644 index d508caa00..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VERTICAL_FLIP_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VERTICAL_FLIP_OP_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class VerticalFlipOp : public TensorOp { - public: - VerticalFlipOp() = default; - - ~VerticalFlipOp() override = default; - - Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; - - std::string Name() const override { return kVerticalFlipOp; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VERTICAL_FLIP_OP_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/image/video_utils.cc b/mindspore-lite/minddata/dataset/kernels/image/video_utils.cc deleted file mode 100644 index f2c1e74e5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/video_utils.cc +++ /dev/null @@ -1,1463 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/image/video_utils.h" - -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#include -#include -#include -#include -#ifdef __cplusplus -}; -#endif - -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_shape.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" -#include "utils/file_utils.h" - -const int32_t MAX_AVIO_BUFFER_SIZE = 1073741824; - -namespace mindspore { -namespace dataset { -struct MediaContainer { - int channels = 1; - - AVCodecContext *codec_context = nullptr; - - AVFrame *frame = nullptr; - int64_t frame_number = 0; - float frame_rate = 1.0; - int64_t samples_count = 0; - - AVStream *stream = nullptr; - int stream_index = -1; - - float time_base = 1.0; - int timestamp_increment = 1; - - // start_frame_number: the frame number corresponding to the start_pts - int64_t start_frame_number = 0; - int64_t start_pts = 0; - - // end_frame_number: the frame number corresponding to the end_pts - int64_t end_frame_number = 0; - int64_t end_pts = 0; - - int64_t current_pts = 0; - - // for the calculation of current_pts - int adjust_pts_flag = 0; - - // for the conversion of frames - int conversion_flags = 0; - - // whether find any error in this container - bool find_error = false; - - ~MediaContainer() { - if (codec_context) { - avcodec_free_context(&codec_context); - codec_context = nullptr; - } - - if (frame) { - av_frame_free(&frame); - frame = nullptr; - } - } -}; - -struct TensorData { - const uint8_t *ptr; - // size left in the tensor - size_t size; -}; - -struct AudioVisual { - AVFormatContext *avformat = nullptr; - AVPacket *packet = nullptr; - - // for audio - struct MediaContainer audio; - AVSampleFormat sample_format = AV_SAMPLE_FMT_NONE; - int sample_rate = 1; - int64_t nb_samples = 0; - SwrContext *audio_conversion = nullptr; - - // for visual - struct MediaContainer visual; - int image_height = 1; - int image_width = 1; - std::map> map_visual_tensor; - SwsContext *visual_conversion = nullptr; - uint8_t *visual_aligned_buffer[kMaxImageChannel] = {nullptr, nullptr, nullptr, nullptr}; - int visual_aligned_linesize[kMaxImageChannel] = {0, 0, 0, 0}; - uint8_t *visual_unaligned_buffer = nullptr; - - // for DecodeVideo op - struct TensorData tensor_data = {nullptr, 0}; - uint8_t *avio_context_buffer = nullptr; - AVIOContext *avio_context = nullptr; - - // for debug and the log_level of FFMPEG - int av_log_level = AV_LOG_FATAL; - - ~AudioVisual() { - if (avio_context) { - av_freep(&avio_context->buffer); - avio_context_free(&avio_context); - } - - if (packet) { - av_packet_free(&packet); - packet = nullptr; - } - - if (audio_conversion) { - swr_free(&audio_conversion); - audio_conversion = nullptr; - } - - // Free the allocated memory by swap to an empty. - std::map>().swap(map_visual_tensor); - - if (visual_unaligned_buffer) { - free(visual_unaligned_buffer); - visual_unaligned_buffer = nullptr; - } - - if (visual_aligned_buffer[0]) { - av_freep(visual_aligned_buffer); - visual_aligned_buffer[0] = nullptr; - } - if (visual_conversion) { - sws_freeContext(visual_conversion); - visual_conversion = nullptr; - } - - if (avformat) { - avformat_close_input(&avformat); - avformat = nullptr; - } - } -}; - -int avio_read_buffer(void *opaque, uint8_t *buf, int buf_size) { - struct TensorData *tensor_data = (struct TensorData *)opaque; - buf_size = FFMIN(buf_size, tensor_data->size); - if (!buf_size) { - return AVERROR_EOF; - } - memcpy_s(buf, buf_size, tensor_data->ptr, buf_size); - tensor_data->ptr += buf_size; - tensor_data->size -= buf_size; - return buf_size; -} - -Status AVOpenMemoryRead(const TensorRow &input, struct AudioVisual *avinfo) { - std::string err_msg; - if (input.size() != 1) { - err_msg = "The input has invalid size " + std::to_string(input.size()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (input[0]->type() != DataType::DE_UINT8) { - err_msg = "The type of the elements of input data should be UINT8, but got " + input[0]->type().ToString() + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - int64_t data_size = input[0]->Size(); - if (data_size >= kDeMaxDim || data_size <= 0) { - err_msg = "The input[0] has invalid size " + std::to_string(data_size); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - const uint8_t *data_buffer; - data_buffer = (const uint8_t *)input[0]->GetBuffer(); - avinfo->tensor_data.ptr = data_buffer; - avinfo->tensor_data.size = data_size; - - // Allocate the avformat - avinfo->avformat = avformat_alloc_context(); - if (avinfo->avformat == nullptr) { - err_msg = "Failed to call avformat_alloc_context()."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - // Set the flags so that it will discard corrupt frames - avinfo->avformat->flags |= AVFMT_FLAG_DISCARD_CORRUPT; - - int avio_buffer_size = FFMIN(data_size, MAX_AVIO_BUFFER_SIZE); - avinfo->avio_context_buffer = reinterpret_cast(av_malloc(avio_buffer_size)); - if (avinfo->avio_context_buffer == nullptr) { - err_msg = "Failed to allocate buffer for avio_context"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - avinfo->avio_context = avio_alloc_context(avinfo->avio_context_buffer, avio_buffer_size, 0, &avinfo->tensor_data, - &avio_read_buffer, NULL, NULL); - if (avinfo->avio_context == nullptr) { - err_msg = "Failed to allocate avio_context"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - avinfo->avformat->pb = avinfo->avio_context; - - // Open the input from memory - if (avformat_open_input(&(avinfo->avformat), nullptr, nullptr, nullptr) < 0) { - err_msg = "Failed to open the input."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Find all the streams - if (avformat_find_stream_info(avinfo->avformat, nullptr) < 0) { - err_msg = "Failed to find stream information."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -// Check the filename and open it by FFMPEG -Status AVOpenFile(const std::string &filename, struct AudioVisual *avinfo) { - std::string err_msg; - // check the input parameter: filename - auto realpath = FileUtils::GetRealPath(filename.c_str()); - if (!realpath.has_value()) { - err_msg = "Invalid file path, " + filename + " does not exist."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - struct stat sb; - stat(realpath.value().c_str(), &sb); - if (S_ISREG(sb.st_mode) == 0) { - err_msg = "Invalid file path, " + filename + " is not a regular file."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (sb.st_size <= 0) { - err_msg = filename + " has invalid file size " + std::to_string(sb.st_size); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Allocate the avformat - avinfo->avformat = avformat_alloc_context(); - if (avinfo->avformat == nullptr) { - err_msg = "Failed to call avformat_alloc_context()."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - // Set the flags so that it will discard corrupt frames - avinfo->avformat->flags |= AVFMT_FLAG_DISCARD_CORRUPT; - - // Open the video file. - if (avformat_open_input(&(avinfo->avformat), realpath.value().c_str(), nullptr, nullptr) < 0) { - err_msg = "Failed to open the file " + filename + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Find all the streams - if (avformat_find_stream_info(avinfo->avformat, nullptr) < 0) { - err_msg = "Failed to find stream information."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} - -// Find one audio stream -int AVFindAudioStream(struct AudioVisual *avinfo) { - int stream_index = av_find_best_stream(avinfo->avformat, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); - if (stream_index >= 0 && stream_index < avinfo->avformat->nb_streams) { - avinfo->audio.stream_index = stream_index; - avinfo->audio.stream = avinfo->avformat->streams[stream_index]; - } - return stream_index; -} - -// Find one visual stream -int AVFindVisualStream(struct AudioVisual *avinfo) { - int stream_index = av_find_best_stream(avinfo->avformat, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); - if (stream_index >= 0 && stream_index < avinfo->avformat->nb_streams) { - avinfo->visual.stream_index = stream_index; - avinfo->visual.stream = avinfo->avformat->streams[stream_index]; - } - return stream_index; -} - -// Check and convert AVRational to float for the time_base -Status AVCalculateAudioTimeBase(struct AudioVisual *avinfo) { - // Use the time_base of codec_context - // The time_base of stream is different from that of codec_context - // The stream->time_base.den is channels * bits * codec_context->time_base.den - if (avinfo->audio.codec_context->time_base.den > 0) { - avinfo->audio.time_base = static_cast(av_q2d(avinfo->audio.codec_context->time_base)); - return Status::OK(); - } - RETURN_STATUS_UNEXPECTED("Failed to calculate the audio time base."); -} - -// Check and calculate the frame rate -Status AVCalculateAudioFrameRate(struct AudioVisual *avinfo) { - AVStream *stream = avinfo->audio.stream; - if (stream->avg_frame_rate.den > 0) { - avinfo->audio.frame_rate = static_cast(av_q2d(stream->avg_frame_rate)); - } else { - if (stream->time_base.den > 0 && stream->time_base.num > 0) { - // use the time_base to calculate the frame_rate - // frame_rate = 1.0 / time_base = time_base.den / time_base.num - stream->avg_frame_rate.num = stream->time_base.den; - stream->avg_frame_rate.den = stream->time_base.num; - avinfo->audio.frame_rate = static_cast(stream->time_base.den / stream->time_base.num); - } else { - RETURN_STATUS_UNEXPECTED("Failed to calculate the audio frame rate."); - } - } - return Status::OK(); -} - -// Check and convert AVRational to float for the time_base -Status AVCalculateVisualTimeBase(struct AudioVisual *avinfo) { - if (avinfo->visual.stream->time_base.den > 0) { - avinfo->visual.time_base = static_cast(av_q2d(avinfo->visual.stream->time_base)); - return Status::OK(); - } - RETURN_STATUS_UNEXPECTED("Failed to calculate the visual time base."); -} - -// Check and calculate the frame rate -Status AVCalculateVisualFrameRate(struct AudioVisual *avinfo) { - AVStream *stream = avinfo->visual.stream; - if (stream->avg_frame_rate.den > 0) { - avinfo->visual.frame_rate = static_cast(av_q2d(stream->avg_frame_rate)); - } else { - if (stream->time_base.den > 0 && stream->time_base.num > 0) { - // use the time_base to calculate the frame_rate - // frame_rate = 1.0 / time_base = time_base.den / time_base.num - stream->avg_frame_rate.num = stream->time_base.den; - stream->avg_frame_rate.den = stream->time_base.num; - avinfo->visual.frame_rate = static_cast(stream->time_base.den / stream->time_base.num); - } else { - RETURN_STATUS_UNEXPECTED("Failed to calculate the visual frame rate."); - } - } - return Status::OK(); -} - -// Check whether the extradata contains search_string -bool FindStringInCodecContextExtradata(const AVCodecContext *codec_context, const char *search_string) { - int extradata_size = codec_context->extradata_size; - if (extradata_size <= 0) { - return false; - } - - int i; - int len = strlen(search_string); - - // Locate the start position of the last string in extradata - for (i = extradata_size - len; i > 0; i--) { - if (codec_context->extradata[i - 1] == 0) { - break; - } - } - - if (strstr((const char *)(&(codec_context->extradata[i])), search_string)) { - return true; - } - return false; -} - -// Open the coder or decoder for the stream -Status AVOpenStreamCodecContext(struct MediaContainer *container, bool enable_thread_frame, bool enable_fast) { - AVStream *stream = container->stream; - std::string err_msg; - if (stream->codecpar == nullptr) { - err_msg = "The stream->codepar is nullptr"; - RETURN_STATUS_UNEXPECTED(err_msg); - } - AVCodecID codec_id = stream->codecpar->codec_id; - if (codec_id == AV_CODEC_ID_NONE) { - err_msg = "Failed to support AV_CODEC_ID_NONE."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - const AVCodec *codec = avcodec_find_decoder(codec_id); - if (codec == nullptr) { - err_msg = "Failed to find a proper codec for " + std::string(avcodec_get_name(codec_id)); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - AVCodecContext *codec_context = avcodec_alloc_context3(codec); - if (codec_context == nullptr) { - err_msg = "Failed to allocate the " + std::string(codec->name); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // Set the codec's parameters according to the stream - if (avcodec_parameters_to_context(codec_context, stream->codecpar) < 0) { - err_msg = "Failed to set the parameters of " + std::string(codec->name); - RETURN_STATUS_UNEXPECTED(err_msg); - } - container->codec_context = codec_context; - - // thread_count = 0 means automatically. - codec_context->thread_count = 0; - codec_context->thread_type = FF_THREAD_SLICE; - if (enable_thread_frame && (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) > 0) { - if (stream->nb_frames > 1) { - codec_context->thread_type |= FF_THREAD_FRAME; - } - } - - // Some decoders need unaligned memory access - codec_context->flags |= AV_CODEC_FLAG_UNALIGNED; - if (enable_fast) { - codec_context->flags2 |= AV_CODEC_FLAG2_FAST; - } - - // Open the codec - codec_context->pkt_timebase = codec_context->time_base; - if (avcodec_open2(codec_context, codec, nullptr) < 0) { - err_msg = "Failed to open the codec " + std::string(codec->name); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - return Status::OK(); -} - -// Allocate a packet and check it is not nullptr -Status AVPacketAllocate(struct AudioVisual *avinfo) { - if (avinfo->packet == nullptr) { - avinfo->packet = av_packet_alloc(); - if (avinfo->packet == nullptr) { - RETURN_STATUS_UNEXPECTED("Failed to allocate packet."); - } - } - return Status::OK(); -} - -// Allocate a frame and check it is not nullptr -Status AVFrameAllocate(struct AudioVisual *avinfo, struct MediaContainer *container) { - if (container->frame == nullptr) { - container->frame = av_frame_alloc(); - if (container->frame == nullptr) { - RETURN_STATUS_UNEXPECTED("Failed to allocate visual frame."); - } - } - return Status::OK(); -} - -// Read the presentation time stamps of a visual stream by decoding each frame -Status AVReadVisualPtsByFrame(struct AudioVisual *avinfo, struct MediaContainer *container, - std::vector *pts_int64_vector) { - int status = 0; - int64_t frame_number = 0; - int64_t packet_number = 0; - AVFormatContext *avformat = avinfo->avformat; - AVPacket *packet = avinfo->packet; - int stream_index = container->stream_index; - AVCodecContext *decoder_context = container->codec_context; - AVFrame *frame = container->frame; - int timestamp_increment = container->timestamp_increment; - char err_buf[AV_ERROR_MAX_STRING_SIZE]; - int64_t pts_number = 0; - int adjust_pts_flag = 0; - std::string err_msg; - - // Read a packet - status = av_read_frame(avformat, packet); - while (status >= 0) { - if (packet->stream_index == stream_index) { - // Send it to decoder - status = avcodec_send_packet(decoder_context, packet); - if (status < 0) { - // The decoder failed to receive the packet - av_make_error_string(err_buf, AV_ERROR_MAX_STRING_SIZE, status); - err_msg = "Failed to receive packet " + std::to_string(packet_number) + ". "; - err_msg += std::string(err_buf); - RETURN_STATUS_UNEXPECTED(err_msg); - } - while (status >= 0) { - // Read a frame from the decoder - status = avcodec_receive_frame(decoder_context, frame); - if (status < 0) { - if (status != AVERROR_EOF && status != AVERROR(EAGAIN)) { - // Failed to receive frame from the decoder - av_make_error_string(err_buf, AV_ERROR_MAX_STRING_SIZE, status); - err_msg = "Failed to receive frame " + std::to_string(frame_number) + ". "; - err_msg += std::string(err_buf); - RETURN_STATUS_UNEXPECTED(err_msg); - } - break; - } - // frame_number = 0 is the first frame - // frame_number = 1 is the second frame - // check whether the pts_number should be adjusted - // for example - // the frame_number : 0, 1, 2, 3, 4, ... - // the frame->pkt_dts : 2, 3, 4, 5, 6, ... - // should be adjusted to : 1, 2, 3, 4, 5, ... when frame->pkt_dts == frame->coded_picture_number - // : 1, 3, 4, 5, 6, ... when frame->pkt_dts != frame->coded_picture_number - // the adjust method is let it - 1 from the second frame - // we check the condition on the second frame, the first frame will be adjusted directly - // The condition can be described as: - // frame_number == 1 : this is the second frame - // timestamp_increment == 1 : the timestamp is incremented by 1 - // frame->pts == AV_NOPTS_VALUE : there is no valid pts value - // frame->pkt_dts == frame->coded_picture_number : the frame->pkt_dts is same as frame->coded_picture_number - // frame->pkt_dts -1 == frame_number + 1 : when the frame->pkt_dts is 3 for the second frame - if (frame_number == 1 && timestamp_increment == 1 && frame->pts == AV_NOPTS_VALUE && - frame->pkt_dts == frame->coded_picture_number && frame->pkt_dts - 1 == frame_number + 1) { - adjust_pts_flag = 1; - } - - pts_number = frame->best_effort_timestamp; - if (pts_number == AV_NOPTS_VALUE) { - pts_number = frame_number * timestamp_increment; - } else { - if (timestamp_increment == 1) { - if (frame_number == 0) { - if (pts_number > 1) { - pts_number = 1; - } - } else { - if (adjust_pts_flag) { - pts_number -= 1; - } - } - } - } - pts_int64_vector->push_back(pts_number); - frame_number++; - av_frame_unref(frame); - } - } - packet_number++; - av_packet_unref(packet); - status = av_read_frame(avformat, packet); - } - avinfo->visual.current_pts = pts_number; - avinfo->visual.adjust_pts_flag = adjust_pts_flag; - - return Status::OK(); -} - -Status AVReadVisualPtsByPacket(struct AudioVisual *avinfo, struct MediaContainer *container, - std::vector *pts_int64_vector) { - int status = 0; - int64_t packet_number = 0; - int64_t pts_number = 0; - std::string err_msg; - - AVFormatContext *avformat = avinfo->avformat; - AVPacket *packet = avinfo->packet; - int stream_index = container->stream_index; - - status = av_read_frame(avformat, packet); - while (status >= 0) { - if (packet->stream_index == stream_index) { - pts_number = packet->pts; - // Check the pts_number, make sure it is a valid number - if (pts_number == AV_NOPTS_VALUE) { - err_msg = "Failed to skip frame because there is no pts value for packet " + std::to_string(packet_number); - RETURN_STATUS_UNEXPECTED(err_msg); - } - pts_int64_vector->push_back(pts_number); - } - av_packet_unref(packet); - packet_number++; - status = av_read_frame(avformat, packet); - } - avinfo->visual.current_pts = pts_number; - return Status::OK(); -} - -// Flush the decoder and read it's pts -Status AVReadVisualPtsFlushDecoder(struct AudioVisual *avinfo, struct MediaContainer *container, - std::vector *pts_int64_vector) { - int status; - std::string err_msg; - - int64_t *pts_number = &(avinfo->visual.current_pts); - - AVCodecContext *decoder_context = container->codec_context; - AVFrame *frame = container->frame; - int timestamp_increment = container->timestamp_increment; - - status = avcodec_send_packet(decoder_context, nullptr); - - while (status >= 0) { - status = avcodec_receive_frame(decoder_context, frame); - if (status < 0) { - if (status != AVERROR_EOF && status != AVERROR(EAGAIN)) { - err_msg = "Failed to receive frame during the flushing of the decoders."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - break; - } - if (frame->best_effort_timestamp != AV_NOPTS_VALUE) { - *pts_number = frame->best_effort_timestamp; - } else { - *pts_number += timestamp_increment; - } - pts_int64_vector->push_back(*pts_number); - av_frame_unref(frame); - } - return Status::OK(); -} - -Status AVReadVisualPts(struct AudioVisual *avinfo, std::vector *pts_int64_vector, float *video_fps, - float *time_base) { - // Calculate the time_base - RETURN_IF_NOT_OK(AVCalculateVisualTimeBase(avinfo)); - *time_base = avinfo->visual.time_base; - - // Calculate the frame_rate - RETURN_IF_NOT_OK(AVCalculateVisualFrameRate(avinfo)); - *video_fps = avinfo->visual.frame_rate; - - // Calculate the timestampe_increment - AVStream *stream = avinfo->visual.stream; - int timestamp_increment = - (stream->time_base.den * stream->avg_frame_rate.den) / (stream->time_base.num * stream->avg_frame_rate.num); - avinfo->visual.timestamp_increment = timestamp_increment; - - bool skip_frame = FindStringInCodecContextExtradata(avinfo->visual.codec_context, "Lavc"); - if (skip_frame) { - RETURN_IF_NOT_OK(AVReadVisualPtsByPacket(avinfo, &(avinfo->visual), pts_int64_vector)); - } else { - RETURN_IF_NOT_OK(AVReadVisualPtsByFrame(avinfo, &(avinfo->visual), pts_int64_vector)); - } - - // Flush the decoders - return AVReadVisualPtsFlushDecoder(avinfo, &(avinfo->visual), pts_int64_vector); -} - -Status AVDecodeVisualFrame(struct AudioVisual *avinfo, std::shared_ptr *output) { - int status = 0; - int channels = avinfo->visual.channels; - AVFrame *frame = avinfo->visual.frame; - - int width = avinfo->image_width; - int height = avinfo->image_height; - - std::string err_msg; - if (frame->height != height || frame->width != width) { - err_msg = "image->height = " + std::to_string(height); - err_msg += " image->width = " + std::to_string(width); - err_msg += ". They are changed at visual frame " + std::to_string(avinfo->visual.frame_number) + "."; - err_msg += " frame->hegiht = " + std::to_string(frame->height); - err_msg += " frame->width = " + std::to_string(frame->width); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - SwsContext *conversion = avinfo->visual_conversion; - status = sws_scale(conversion, frame->data, frame->linesize, 0, height, avinfo->visual_aligned_buffer, - avinfo->visual_aligned_linesize); - if (status < 1) { - err_msg = "Failed to call sws_scale at visual frame " + std::to_string(avinfo->visual.frame_number); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - int linesize = avinfo->visual_aligned_linesize[0]; - // Use the aligned buffer to create Tensor. - if (linesize == width * channels) { - return Tensor::CreateFromMemory(TensorShape({height, width, channels}), (const DataType)DataType::DE_UINT8, - (const uchar *)avinfo->visual_aligned_buffer[0], output); - } - - // Use the unaligned buffer to create Tensor. - // Ignore the unused padding bytes for each line. - int i; - uint8_t *src_address = avinfo->visual_aligned_buffer[0]; - uint8_t *target_address = avinfo->visual_unaligned_buffer; - int src_offset = linesize; - int target_offset = width * channels; - for (i = 0; i < height; i++) { - if (target_offset < SECUREC_MEM_MAX_LEN) { - int ret_code = memcpy_s(target_address, target_offset, src_address, target_offset); - CHECK_FAIL_RETURN_UNEXPECTED( - ret_code == EOK, "Failed to copy data into tensor, memcpy_s errorno: " + std::to_string(ret_code) + "."); - } else { - auto ret_code = std::memcpy(target_address, src_address, target_offset); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == target_address, "Failed to copy data into tensor."); - } - src_address += src_offset; - target_address += target_offset; - } - return Tensor::CreateFromMemory(TensorShape({height, width, channels}), (const DataType)DataType::DE_UINT8, - (const uchar *)avinfo->visual_unaligned_buffer, output); -} - -template -void AVDecodeAudioFramePacket(struct AudioVisual *avinfo, std::vector> *audio_vector) { - AVFrame *frame = avinfo->audio.frame; - - int channels = frame->channels; - int64_t nb_samples = frame->nb_samples; - int out_buffer_size = nb_samples * sizeof(T); - - const uint8_t **out_data = (const uint8_t **)(frame->extended_data); - - if (audio_vector->size() == 0) { - audio_vector->resize(avinfo->audio.channels, std::vector()); - } - - const T *p_data = nullptr; - for (int channel = 0; channel < channels; channel++) { - for (int i = channel * sizeof(T); i < out_buffer_size + channel * sizeof(T); i += sizeof(T)) { - p_data = (const T *)(out_data[0] + i); - (*audio_vector)[channel].push_back(*p_data); - } - } -} - -template -void AVDecodeAudioFramePlanar(struct AudioVisual *avinfo, std::vector> *audio_vector) { - AVFrame *frame = avinfo->audio.frame; - - int channels = frame->channels; - int64_t nb_samples = frame->nb_samples; - - const uint8_t **out_data = (const uint8_t **)(frame->extended_data); - - if (audio_vector->size() == 0) { - audio_vector->resize(avinfo->audio.channels, std::vector()); - } - - for (int channel = 0; channel < channels; channel++) { - const T *p_data = reinterpret_cast(out_data[channel]); - (*audio_vector)[channel].insert((*audio_vector)[channel].end(), p_data, p_data + nb_samples); - } -} - -Status AVAllocateAudioConversion(struct AudioVisual *avinfo, AVSampleFormat out_sample_format) { - std::string err_msg; - if (avinfo->audio_conversion != nullptr) { - err_msg = "audio_conversion should be freed firstly."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - AVFrame *frame = avinfo->audio.frame; - int sample_rate = frame->sample_rate; - int64_t channel_layout = frame->channel_layout; - - AVSampleFormat in_sample_format = (AVSampleFormat)(frame->format); - - SwrContext *swr_context = swr_alloc(); - if (swr_context == nullptr) { - err_msg = "Failed to get audio convert context."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - av_opt_set_int(swr_context, "in_channel_layout", channel_layout, 0); - av_opt_set_int(swr_context, "out_channel_layout", channel_layout, 0); - av_opt_set_int(swr_context, "in_sample_rate", sample_rate, 0); - av_opt_set_int(swr_context, "out_sample_rate", sample_rate, 0); - av_opt_set_sample_fmt(swr_context, "in_sample_fmt", in_sample_format, 0); - av_opt_set_sample_fmt(swr_context, "out_sample_fmt", out_sample_format, 0); - - int result = swr_init(swr_context); - if (result < 0) { - err_msg = "Failed to initialize the resampling context."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - avinfo->audio_conversion = swr_context; - return Status::OK(); -} - -template -Status AVDecodeAudioFrameByConversion(struct AudioVisual *avinfo, std::vector> *audio_vector) { - AVFrame *frame = avinfo->audio.frame; - - if (audio_vector->size() == 0) { - audio_vector->resize(avinfo->audio.channels, std::vector()); - } - - std::string err_msg; - - struct SwrContext *swr_context = avinfo->audio_conversion; - AVSampleFormat out_sample_format = AV_SAMPLE_FMT_FLTP; - if (swr_context == nullptr) { - RETURN_IF_NOT_OK(AVAllocateAudioConversion(avinfo, out_sample_format)); - swr_context = avinfo->audio_conversion; - } - - int64_t nb_samples = frame->nb_samples; - int sample_rate = frame->sample_rate; - int64_t out_nb_samples = av_rescale_rnd(nb_samples, sample_rate, sample_rate, AV_ROUND_UP); - - uint8_t **out_data = nullptr; - int linesize[2] = {0, 0}; - int channels = frame->channels; - int status = av_samples_alloc_array_and_samples(&out_data, linesize, channels, out_nb_samples, out_sample_format, 0); - if (status < 0) { - err_msg = "Failed to call av_samples_alloc_array_and_samples at audio frame "; - err_msg += std::to_string(avinfo->audio.frame_number); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - status = swr_convert(swr_context, out_data, out_nb_samples, (const uint8_t **)(frame->extended_data), nb_samples); - if (status < 0) { - err_msg = "Failed to call swr_convert at audio frame " + std::to_string(avinfo->audio.frame_number); - RETURN_STATUS_UNEXPECTED(err_msg); - } - - for (int channel = 0; channel < channels; channel++) { - T *p_data = reinterpret_cast(out_data[channel]); - (*audio_vector)[channel].insert((*audio_vector)[channel].end(), p_data, p_data + nb_samples); - } - - if (out_data) { - av_freep(out_data); - } - return Status::OK(); -} - -template -Status AVDecodeAudioFrame(struct AudioVisual *avinfo, std::vector> *audio_vector) { - AVFrame *frame = avinfo->audio.frame; - AVSampleFormat in_sample_format = (AVSampleFormat)(frame->format); - - std::string err_msg; - if (avinfo->sample_format == AV_SAMPLE_FMT_NONE) { - avinfo->sample_format = in_sample_format; - } else { - if (avinfo->sample_format != in_sample_format) { - err_msg = "Failed to support the change of sample format at audio frame "; - err_msg += std::to_string(avinfo->audio.frame_number); - avinfo->audio.find_error = true; - RETURN_STATUS_UNEXPECTED(err_msg); - } - } - if (frame->channels != avinfo->audio.channels) { - err_msg = "The frame->channels is " + std::to_string(frame->channels) + ", it should be "; - err_msg += std::to_string(avinfo->audio.channels) + " at audio frame "; - err_msg += std::to_string(avinfo->audio.frame_number); - if (avinfo->audio.frame_number > 0) { - avinfo->audio.find_error = true; - RETURN_STATUS_UNEXPECTED(err_msg); - } - MS_LOG(WARNING) << err_msg; - avinfo->audio.channels = frame->channels; - } - - switch (in_sample_format) { - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S64: - case AV_SAMPLE_FMT_U8: - AVDecodeAudioFramePacket(avinfo, audio_vector); - break; - case AV_SAMPLE_FMT_DBLP: - case AV_SAMPLE_FMT_FLTP: - case AV_SAMPLE_FMT_S16P: - case AV_SAMPLE_FMT_S32P: - case AV_SAMPLE_FMT_S64P: - case AV_SAMPLE_FMT_U8P: - AVDecodeAudioFramePlanar(avinfo, audio_vector); - break; - default: - return AVDecodeAudioFrameByConversion(avinfo, audio_vector); - break; - } - return Status::OK(); -} - -template -Status AVAlignAudioFrames(std::vector> *audio_vector, int audio_channels, - std::shared_ptr *audio_output) { - TensorShape shape({audio_channels, (int64_t)(*audio_vector)[0].size()}); - DataType type = DataType::FromCType(); - if (Tensor::CreateEmpty(shape, type, audio_output) != Status::OK()) { - RETURN_STATUS_UNEXPECTED("Failed to call Tensor::CreateEmpty."); - } - for (int channel = 0; channel < audio_channels; channel++) { - std::shared_ptr single_audio; - if (Tensor::CreateFromVector((*audio_vector)[channel], &single_audio) != Status::OK()) { - RETURN_STATUS_UNEXPECTED("Failed to call Tensor::CreateFromVector."); - } - (*audio_output)->InsertTensor({channel}, single_audio); - } - return Status::OK(); -} - -void GetVisualCurrentPts(struct AudioVisual *avinfo) { - AVFrame *frame = avinfo->visual.frame; - int64_t frame_number = avinfo->visual.frame_number; - int timestamp_increment = avinfo->visual.timestamp_increment; - - // frame_number = 0 is the first frame - // frame_number = 1 is the second frame - // check whether the pts_number should be adjusted - // for example - // the frame_number : 0, 1, 2, 3, 4, ... - // the frame->pkt_dts : 2, 3, 4, 5, 6, ... - // should be adjusted to : 1, 2, 3, 4, 5, ... when frame->pkt_dts == frame->coded_picture_number - // : 1, 3, 4, 5, 6, ... when frame->pkt_dts != frame->coded_picture_number - // the adjust method is let it - 1 from the second frame - // we check the condition on the second frame, the first frame will be adjusted directly - // The condition can be described as: - // frame_number == 1 : this is the second frame - // timestamp_increment == 1 : the timestamp is incremented by 1 - // frame->pts == AV_NOPTS_VALUE : there is no valid pts value - // frame->pkt_dts == frame->coded_picture_number : the frame->pkt_dts is same as frame->coded_picture_number - // frame->pkt_dts -1 == frame_number + 1 : when the frame->pkt_dts is 3 for the second frame - - int *adjust_pts_flag = &(avinfo->visual.adjust_pts_flag); - if (frame_number == 1 && timestamp_increment == 1 && frame->pts == AV_NOPTS_VALUE && - frame->pkt_dts == frame->coded_picture_number && frame->pkt_dts - 1 == frame_number + 1) { - *adjust_pts_flag = 1; - } - - int64_t *current_pts = &(avinfo->visual.current_pts); - *current_pts = frame->best_effort_timestamp; - if (*current_pts == AV_NOPTS_VALUE) { - *current_pts = frame_number * timestamp_increment; - } else { - if (timestamp_increment == 1) { - if (frame_number == 0) { - if (*current_pts > 1) { - *current_pts = 1; - } - } else { - if (*adjust_pts_flag) { - *current_pts -= 1; - } - } - } - } -} - -Status AVDecodePacketVisual(struct AudioVisual *avinfo, AVPacket *packet) { - int status = 0; - AVCodecContext *codec_context = avinfo->visual.codec_context; - AVFrame *frame = avinfo->visual.frame; - - std::map> *output = &(avinfo->map_visual_tensor); - int64_t *frame_number = &(avinfo->visual.frame_number); - int64_t *current_pts = &(avinfo->visual.current_pts); - int64_t start_pts = avinfo->visual.start_pts; - int64_t end_pts = avinfo->visual.end_pts; - int64_t *start_frame_number = &(avinfo->visual.start_frame_number); - int64_t *end_frame_number = &(avinfo->visual.end_frame_number); - - std::string err_msg; - - status = avcodec_send_packet(codec_context, packet); - if (status < 0) { - err_msg = "Failed to receive packet for visual frame " + std::to_string(*frame_number); - if (packet == nullptr) { - err_msg += " when flushing the codec_context"; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - while (status >= 0) { - std::shared_ptr frame_tensor; - status = avcodec_receive_frame(codec_context, frame); - if (status < 0) { - if (status == AVERROR_EOF || status == AVERROR(EAGAIN)) { - return Status::OK(); - } - err_msg = "Failed to receive visual frame " + std::to_string(*frame_number) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - GetVisualCurrentPts(avinfo); - - // adjust the start_pts when the *current_pts is 1 , *frame_number is 0, and start_pts is 1 - if (*current_pts == 1 && *frame_number == 0 && start_pts == 1) { - start_pts = 0; - avinfo->visual.start_pts = 0; - } - - if (*current_pts >= start_pts && *current_pts <= end_pts) { - RETURN_IF_NOT_OK(AVDecodeVisualFrame(avinfo, &frame_tensor)); - } - - av_frame_unref(frame); - - int64_t best_frame_number; - - if (frame->display_picture_number > 0) { - best_frame_number = frame->display_picture_number; - } else { - best_frame_number = *frame_number; - } - if (*current_pts >= start_pts && *current_pts <= end_pts) { - if (start_pts > 0) { - // Record the frame number corresponding to the start_pts - if (*start_frame_number == 0) { - *start_frame_number = best_frame_number; - } - } - if (best_frame_number + 1 > *end_frame_number) { - *end_frame_number = best_frame_number + 1; - } - output->insert(std::pair>(best_frame_number, frame_tensor)); - } - - if (best_frame_number + 1 > *frame_number) { - *frame_number = best_frame_number + 1; - } - } - - return Status::OK(); -} - -template -Status AVDecodePacketAudio(struct AudioVisual *avinfo, std::vector> *audio_vector, AVPacket *packet) { - AVCodecContext *codec_context = avinfo->audio.codec_context; - AVFrame *frame = avinfo->audio.frame; - - int64_t *frame_number = &(avinfo->audio.frame_number); - - std::string err_msg; - int status = avcodec_send_packet(codec_context, packet); - if (status < 0) { - err_msg = "Failed to receive packet for audio frame " + std::to_string(*frame_number); - if (packet == nullptr) { - err_msg += " when flushing the codec_context"; - } - char err_buf[AV_ERROR_MAX_STRING_SIZE]; - av_make_error_string(err_buf, AV_ERROR_MAX_STRING_SIZE, status); - err_msg += ". " + std::string(err_buf); - MS_LOG(WARNING) << err_msg; - avinfo->audio.find_error = true; - return Status::OK(); - } - while (status >= 0) { - status = avcodec_receive_frame(codec_context, frame); - if (status < 0) { - if (status == AVERROR_EOF || status == AVERROR(EAGAIN)) { - return Status::OK(); - } - err_msg = "Failed to receive audio frame " + std::to_string(*frame_number) + "."; - MS_LOG(WARNING) << err_msg; - avinfo->audio.find_error = true; - return Status::OK(); - } - - RETURN_IF_NOT_OK(AVDecodeAudioFrame(avinfo, audio_vector)); - - *frame_number += 1; - avinfo->nb_samples += frame->nb_samples; - - av_frame_unref(frame); - } - return Status::OK(); -} - -Status AVOpenAudioStream(struct AudioVisual *avinfo) { - int stream_index = AVFindAudioStream(avinfo); - if (stream_index < 0) { - return Status::OK(); - } - - RETURN_IF_NOT_OK(AVOpenStreamCodecContext(&avinfo->audio, false, false)); - - avinfo->sample_format = avinfo->audio.codec_context->sample_fmt; - avinfo->audio.channels = avinfo->audio.codec_context->channels; - // Calculate the time_base - return AVCalculateAudioTimeBase(avinfo); -} - -Status AVOpenVisualStream(struct AudioVisual *avinfo) { - int stream_index = AVFindVisualStream(avinfo); - if (stream_index < 0) { - return Status::OK(); - } - - RETURN_IF_NOT_OK(AVOpenStreamCodecContext(&avinfo->visual, true, false)); - - std::string err_msg; - // check the pixel format - AVStream *stream = avinfo->visual.stream; - if (stream->codecpar->format == AV_PIX_FMT_NONE) { - err_msg = "The pixel format is AV_PIX_FMT_NONE."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - avinfo->image_height = stream->codecpar->height; - avinfo->image_width = stream->codecpar->width; - - RETURN_IF_NOT_OK(AVCalculateVisualFrameRate(avinfo)); - - // Calculate the time_base - RETURN_IF_NOT_OK(AVCalculateVisualTimeBase(avinfo)); - - AVRational time_base = stream->time_base; - AVRational avg_frame_rate = stream->avg_frame_rate; - avinfo->visual.timestamp_increment = (time_base.den * avg_frame_rate.den) / (time_base.num * avg_frame_rate.num); - - int width = avinfo->image_width; - int height = avinfo->image_height; - - AVCodecContext *codec_context = avinfo->visual.codec_context; - AVPixelFormat from_format = codec_context->pix_fmt; - AVPixelFormat target_format = AV_PIX_FMT_RGB24; - - int channels = kDefaultImageChannel; - avinfo->visual.channels = channels; - - int align = kMaxImageChannel * kMaxImageChannel; - if (avinfo->visual_aligned_buffer[0] == nullptr) { - // adjust the width, height according to the codec_context - int adjusted_width = width; - int adjusted_height = height; - avcodec_align_dimensions(codec_context, &adjusted_width, &adjusted_height); - // Some codecs need extra 16 bytes, then add 1 extra line. - adjusted_height += 1; - - av_image_alloc(avinfo->visual_aligned_buffer, avinfo->visual_aligned_linesize, adjusted_width, adjusted_height, - target_format, align); - } - if (avinfo->visual_aligned_buffer[0] == nullptr) { - err_msg = "Failed to allocate visual_aligned_buffer."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - if (avinfo->visual_unaligned_buffer == nullptr) { - avinfo->visual_unaligned_buffer = reinterpret_cast(calloc((height + 1) * width * channels, 1)); - } - if (avinfo->visual_unaligned_buffer == nullptr) { - err_msg = "Failed to allocate visual_unaligned_buffer."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - SwsContext *conversion = sws_getContext(width, height, from_format, width, height, target_format, - avinfo->visual.conversion_flags, nullptr, nullptr, nullptr); - if (conversion == nullptr) { - err_msg = "Failed to get visual convert context."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - avinfo->visual_conversion = conversion; - return Status::OK(); -} - -template -Status AVGenereateAudioTensor(std::vector> *audio_vector, int audio_channels, - std::shared_ptr *audio_output) { - if (audio_vector->size() > 0) { - return AVAlignAudioFrames(audio_vector, audio_channels, audio_output); - } - - TensorShape audio_shape = TensorShape({1, 0}); - if (Tensor::CreateEmpty(audio_shape, DataType(DataType::DE_FLOAT32), audio_output) != Status::OK()) { - RETURN_STATUS_UNEXPECTED("Failed to call Tensor::CreateEmpty."); - } - return Status::OK(); -} - -Status AVGenereateVisualTensor(struct AudioVisual *avinfo, std::shared_ptr *visual_output) { - int tensor_size = static_cast(avinfo->map_visual_tensor.size()); - int image_height = avinfo->image_height; - int image_width = avinfo->image_width; - int64_t start_frame_number = avinfo->visual.start_frame_number; - - TensorShape shape({tensor_size, image_height, image_width, kDefaultImageRank}); - if (Tensor::CreateEmpty(shape, DataType(DataType::DE_UINT8), visual_output) != Status::OK()) { - RETURN_STATUS_UNEXPECTED("Failed to call Tensor::CreateEmpty."); - } - for (auto &visual_frame : avinfo->map_visual_tensor) { - if (visual_frame.first == 0 && start_frame_number != 0) { - MS_LOG(WARNING) << "visual_frame.first = " + std::to_string(visual_frame.first); - MS_LOG(WARNING) << "start_frame_number = " + std::to_string(start_frame_number); - - start_frame_number = 0; - avinfo->visual.start_frame_number = 0; - - MS_LOG(WARNING) << "The bug is fixed by let start_frame_number = " + std::to_string(start_frame_number); - } - (*visual_output)->InsertTensor({visual_frame.first - start_frame_number}, visual_frame.second); - } - return Status::OK(); -} - -template -void AudioRemovePoint(struct AudioVisual *avinfo, std::vector> *audio_vector) { - int k; - - int64_t start_pts = avinfo->audio.start_pts; - int64_t end_pts = avinfo->audio.end_pts; - int64_t point_number = avinfo->nb_samples; - int channels = avinfo->audio.channels; - - for (k = 0; k < channels; k++) { - auto it_begin = (*audio_vector)[k].begin(); - (*audio_vector)[k].erase(it_begin + end_pts, it_begin + point_number); - (*audio_vector)[k].erase(it_begin, it_begin + start_pts); - } -} - -template -void AudioStartEnd(struct AudioVisual *avinfo, std::vector> *audio_vector) { - if (avinfo->audio.stream_index < 0 || avinfo->visual.stream_index < 0) { - return; - } - - int64_t start_frame_number = avinfo->visual.start_frame_number; - int64_t frame_count = avinfo->visual.frame_number; - int64_t tensor_count = avinfo->map_visual_tensor.size(); - int64_t point_count = avinfo->nb_samples; - - if (point_count < 1 || frame_count < 1) { - return; - } - - float points_per_frame = static_cast(point_count) / static_cast(frame_count); - int64_t start_point = round(start_frame_number * points_per_frame); - int64_t end_point = start_point + round(tensor_count * points_per_frame); - if (start_point < 1 && end_point >= point_count) { - return; - } - - avinfo->audio.start_pts = start_point; - avinfo->audio.end_pts = end_point; - - AudioRemovePoint(avinfo, audio_vector); -} - -template -Status AVReadPackets(struct AudioVisual *avinfo, std::shared_ptr *visual_output, - std::shared_ptr *audio_output, std::vector> *audio_vector) { - AVFormatContext *avformat = avinfo->avformat; - AVPacket *packet = avinfo->packet; - - while (av_read_frame(avformat, packet) >= 0) { - if (packet->stream_index == avinfo->audio.stream_index && avinfo->audio.find_error == false) { - RETURN_IF_NOT_OK(AVDecodePacketAudio(avinfo, audio_vector, packet)); - } - if (packet->stream_index == avinfo->visual.stream_index) { - RETURN_IF_NOT_OK(AVDecodePacketVisual(avinfo, packet)); - } - av_packet_unref(packet); - } - if (avinfo->audio.stream_index >= 0 && avinfo->audio.find_error == false) { - // Flush the audio codec with a null packet - RETURN_IF_NOT_OK(AVDecodePacketAudio(avinfo, audio_vector, nullptr)); - } - if (avinfo->visual.stream_index >= 0) { - // Flush the visual codec with a null packet - RETURN_IF_NOT_OK(AVDecodePacketVisual(avinfo, nullptr)); - } - - RETURN_IF_NOT_OK(AVGenereateVisualTensor(avinfo, visual_output)); - - AudioStartEnd(avinfo, audio_vector); - - return AVGenereateAudioTensor(audio_vector, avinfo->audio.channels, audio_output); -} - -Status VisualStartEnd(struct AudioVisual *avinfo, float start_pts, float end_pts, const std::string &pts_unit) { - if (avinfo->visual.stream_index >= 0) { - if (pts_unit == "pts") { - avinfo->visual.start_pts = round(start_pts); - avinfo->visual.end_pts = round(end_pts); - } else { - if (avinfo->visual.time_base < 1.0e-9) { - RETURN_STATUS_UNEXPECTED("The time_base is too small. It is " + std::to_string(avinfo->visual.time_base)); - } - avinfo->visual.start_pts = round(start_pts / avinfo->visual.time_base); - avinfo->visual.end_pts = round(end_pts / avinfo->visual.time_base); - } - } - return Status::OK(); -} - -Status AVGenerateDefaultOutput(std::shared_ptr *video_output, std::shared_ptr *audio_output) { - TensorShape video_shape({0, 1, 1, kDefaultImageRank}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(video_shape, DataType(DataType::DE_UINT8), video_output)); - - TensorShape audio_shape({1, 0}); - return Tensor::CreateEmpty(audio_shape, DataType(DataType::DE_FLOAT32), audio_output); -} - -Status PrepareRead(struct AudioVisual *avinfo, float start_pts, float end_pts, const std::string &pts_unit) { - // Allocate the packet - RETURN_IF_NOT_OK(AVPacketAllocate(avinfo)); - - // Allocate the frames - RETURN_IF_NOT_OK(AVFrameAllocate(avinfo, &avinfo->audio)); - RETURN_IF_NOT_OK(AVFrameAllocate(avinfo, &avinfo->visual)); - - // Open the video stream and decoder - RETURN_IF_NOT_OK(AVOpenVisualStream(avinfo)); - - // Open the audio stream and decoder - RETURN_IF_NOT_OK(AVOpenAudioStream(avinfo)); - - if (avinfo->visual.stream_index < 0 && avinfo->audio.stream_index < 0) { - RETURN_STATUS_UNEXPECTED("Neither audio nor visual is found."); - } - - return VisualStartEnd(avinfo, start_pts, end_pts, pts_unit); -} - -Status AVReadVisualAudio(struct AudioVisual *avinfo, std::shared_ptr *visual_output, - std::shared_ptr *audio_output) { - if (avinfo->audio.stream_index < 0) { - std::vector> audio_float_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_float_vector); - } - switch (avinfo->sample_format) { - case AV_SAMPLE_FMT_DBL: - case AV_SAMPLE_FMT_DBLP: { - std::vector> audio_double_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_double_vector); - } - case AV_SAMPLE_FMT_FLT: - case AV_SAMPLE_FMT_FLTP: { - std::vector> audio_float_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_float_vector); - } - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S16P: { - std::vector> audio_int16_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_int16_vector); - } - case AV_SAMPLE_FMT_S32: - case AV_SAMPLE_FMT_S32P: { - std::vector> audio_int32_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_int32_vector); - } - case AV_SAMPLE_FMT_S64: - case AV_SAMPLE_FMT_S64P: { - std::vector> audio_int64_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_int64_vector); - } - case AV_SAMPLE_FMT_U8: - case AV_SAMPLE_FMT_U8P: { - std::vector> audio_uint8_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_uint8_vector); - } - default: { - std::vector> audio_float_vector; - return AVReadPackets(avinfo, visual_output, audio_output, &audio_float_vector); - } - } - return Status::OK(); -} - -Status DecodeVideo(const TensorRow &input, TensorRow *output) { - std::shared_ptr visual_output; - std::shared_ptr audio_output; - - struct AudioVisual avinfo; - av_log_set_level(avinfo.av_log_level); - RETURN_IF_NOT_OK(AVOpenMemoryRead(input, &avinfo)); - - RETURN_IF_NOT_OK(PrepareRead(&avinfo, 0, INT_MAX, "pts")); - - // Read the packets - RETURN_IF_NOT_OK(AVReadVisualAudio(&avinfo, &visual_output, &audio_output)); - - output->emplace_back(visual_output); - output->emplace_back(audio_output); - return Status::OK(); -} - -Status ReadVideo(const std::string &filename, std::shared_ptr *video_output, - std::shared_ptr *audio_output, std::map *metadata_output, - float start_pts, float end_pts, const std::string &pts_unit) { - // Check the output parameters - RETURN_UNEXPECTED_IF_NULL(video_output); - RETURN_UNEXPECTED_IF_NULL(audio_output); - RETURN_UNEXPECTED_IF_NULL(metadata_output); - - std::string err_msg; - - // Check the input parameter: start_pts - if (start_pts < 0) { - err_msg = "ReadVideo: Not supported start_pts for " + std::to_string(start_pts) + ". It should be >= 0."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - // Check the input parameter: end_pts - if (end_pts < start_pts) { - err_msg = "ReadVideo: Not supported end_pts for " + std::to_string(end_pts) + "."; - err_msg += " The start_pts = " + std::to_string(start_pts) + ". The end_pts should be >= start_pts."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - // Check the input parameter: pts_unit - if (pts_unit != "pts" && pts_unit != "sec") { - RETURN_STATUS_UNEXPECTED("ReadVideo: Not supported pts_unit for " + pts_unit + "."); - } - - struct AudioVisual avinfo; - av_log_set_level(avinfo.av_log_level); - // check the input parameter filename and open it - RETURN_IF_NOT_OK(AVOpenFile(filename, &avinfo)); - - RETURN_IF_NOT_OK(PrepareRead(&avinfo, start_pts, end_pts, pts_unit)); - if (avinfo.visual.stream_index >= 0) { - (*metadata_output)["video_fps"] = std::to_string(avinfo.visual.frame_rate); - } - if (avinfo.audio.stream_index >= 0) { - (*metadata_output)["audio_fps"] = std::to_string(avinfo.audio.codec_context->sample_rate); - } - - // Read the packets - return AVReadVisualAudio(&avinfo, video_output, audio_output); -} - -Status ReadVideoTimestamps(const std::string &filename, std::vector *pts_int64_vector, float *video_fps, - float *time_base, const std::string &pts_unit) { - // check the output parameters - RETURN_UNEXPECTED_IF_NULL(pts_int64_vector); - RETURN_UNEXPECTED_IF_NULL(video_fps); - RETURN_UNEXPECTED_IF_NULL(time_base); - - // check the input parameter: pts_unit - if (pts_unit != "pts" && pts_unit != "sec") { - std::string err_msg = "ReadVideoTimestamps: Not supported pts_unit for " + pts_unit; - RETURN_STATUS_UNEXPECTED(err_msg); - } - - // set default outputs - // when there is not any video stream, assume the video_fps and time_base are both 1.0 - *video_fps = 1.0; - *time_base = 1.0; - - struct AudioVisual avinfo; - av_log_set_level(avinfo.av_log_level); - // check the input parameter filename and open it - RETURN_IF_NOT_OK(AVOpenFile(filename, &avinfo)); - - int video_stream_index = AVFindVisualStream(&avinfo); - if (video_stream_index < 0) { - return Status::OK(); - } - - // Allocate the packet - RETURN_IF_NOT_OK(AVPacketAllocate(&avinfo)); - - // Allocate the frames - RETURN_IF_NOT_OK(AVFrameAllocate(&avinfo, &avinfo.visual)); - - // Open the decoder for the visual - RETURN_IF_NOT_OK(AVOpenStreamCodecContext(&avinfo.visual, false, true)); - - return AVReadVisualPts(&avinfo, pts_int64_vector, video_fps, time_base); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/image/video_utils.h b/mindspore-lite/minddata/dataset/kernels/image/video_utils.h deleted file mode 100644 index 4750c08f2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/image/video_utils.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VIDEO_UTILS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VIDEO_UTILS_H_ - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -/// \brief Decode the raw input video bytes. Supported video formats are AVI, H264, H265, MOV, MP4 and WMV. -/// \param input: CVTensor containing the not decoded video 1D bytes. -/// \param output: Decoded visual Tensor and audio Tensor. For visual tensor, the shape is , the type is -/// DE_UINT8. Pixel order is RGB. For audio tensor, the shape is . -Status DecodeVideo(const TensorRow &input, TensorRow *output); - -/// \brief Read the video, audio, metadata from a video file. It supports AVI, H264, H265, MOV, MP4, WMV files. -/// \param[in] filename The path to the videoe file to be read. -/// \param[out] video_output The video frames of the video file. -/// \param[out] audio_output The audio frames of the video file. -/// \param[out] metadata_output The metadata contains video_fps, audio_fps. -/// \param[in] start_pts The start presentation timestamp of the video. -/// \param[in] end_pts The end presentation timestamp of the video. -/// \param[in] pts_unit The unit for the timestamps, can be one of ["pts", "sec"]. -/// \return The status code. -Status ReadVideo(const std::string &filename, std::shared_ptr *video_output, - std::shared_ptr *audio_output, std::map *metadata_output, - float start_pts, float end_pts, const std::string &pts_unit); - -/// \brief Read the timestamps and frame rate of a video file. It supports AVI, H264, H265, MOV, MP4, WMV files. -/// \param[in] filename The path to the video file to be read. -/// \param[out] pts_int64_vector The pts vector of the video file. -/// \param[out] video_fps The video frame rate of the video file. -/// \param[out] time_base The time base for the pts_int64_vector. -/// \param[in] pts_unit The unit for the timestamps, can be one of ["pts", "sec"]. -/// \return The status code. -Status ReadVideoTimestamps(const std::string &filename, std::vector *pts_int64_vector, float *video_fps, - float *time_base, const std::string &pts_unit); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_VIDEO_UTILS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.cc deleted file mode 100644 index c6c0d6fc6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.cc +++ /dev/null @@ -1,533 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h" - -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -// Kernel data headers (in alphabetical order) -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/fill_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/mask_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/slice_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/unique_op.h" -#endif - -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/plugin_op.h" -#endif -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/kernels/py_func_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -// Transform operations for data. -namespace transforms { -/* ####################################### Derived TensorOperation classes ################################# */ -// (In alphabetical order) - -// ComposeOperation -ComposeOperation::ComposeOperation(const std::vector> &transforms) - : transforms_(transforms) {} - -Status ComposeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("Compose", transforms_)); - return Status::OK(); -} - -std::shared_ptr ComposeOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](const auto &op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops); -} - -Status ComposeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ComposeOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kComposeOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - *operation = std::make_shared(operations); - return Status::OK(); -} - -// ConcatenateOperation -ConcatenateOperation::ConcatenateOperation(int8_t axis, const std::shared_ptr &prepend, - const std::shared_ptr &append) - : axis_(axis), prepend_(prepend), append_(append) {} - -Status ConcatenateOperation::ValidateParams() { - if (axis_ != 0 && axis_ != -1) { - std::string err_msg = - "Concatenate: Only 1D concatenation supported, input 'axis' should be 0 or -1, but got:" + std::to_string(axis_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (prepend_) { - if (prepend_->shape().Size() != 1) { - std::string err_msg = "Concatenate: Can only prepend 1D arrays, rank of input 'prepend' should be 1, but got:" + - std::to_string(prepend_->shape().Size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (append_) { - if (append_->shape().Size() != 1) { - std::string err_msg = "Concatenate: Can only append 1D arrays, rank of input 'append' should be 1, but got:" + - std::to_string(append_->shape().Size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - return Status::OK(); -} - -std::shared_ptr ConcatenateOperation::Build() { - return std::make_shared(axis_, prepend_, append_); -} - -Status ConcatenateOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["axis"] = axis_; - nlohmann::json prepend; - nlohmann::json append; - RETURN_IF_NOT_OK(prepend_->to_json(&prepend)); - RETURN_IF_NOT_OK(append_->to_json(&append)); - args["prepend"] = prepend; - args["append"] = append; - *out_json = args; - return Status::OK(); -} - -Status ConcatenateOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "axis", kConcatenateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prepend", kConcatenateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "append", kConcatenateOperation)); - int8_t axis = op_params["axis"]; - std::shared_ptr prepend; - std::shared_ptr append; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["prepend"], &prepend)); - RETURN_IF_NOT_OK(Tensor::from_json(op_params["append"], &append)); - *operation = std::make_shared(axis, prepend, append); - return Status::OK(); -} -#endif - -// DuplicateOperation -Status DuplicateOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr DuplicateOperation::Build() { return std::make_shared(); } - -Status DuplicateOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// FillOperation -FillOperation::FillOperation(const std::shared_ptr &fill_value) : fill_value_(fill_value) {} - -Status FillOperation::ValidateParams() { - if (fill_value_->shape() != TensorShape::CreateScalar()) { - std::string err_msg = "Fill: fill_value is not a scalar tensor, got shape:" + fill_value_->shape().ToString(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr FillOperation::Build() { return std::make_shared(fill_value_); } - -Status FillOperation::to_json(nlohmann::json *out_json) { - RETURN_IF_NOT_OK(fill_value_->to_json(out_json)); - return Status::OK(); -} - -Status FillOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - std::shared_ptr fill_value; - RETURN_IF_NOT_OK(Tensor::from_json(op_params, &fill_value)); - *operation = std::make_shared(fill_value); - return Status::OK(); -} - -// MaskOperation -MaskOperation::MaskOperation(RelationalOp op, const std::shared_ptr &constant, const DataType &dtype) - : op_(op), constant_(constant), dtype_(dtype) {} - -Status MaskOperation::ValidateParams() { - if (!dtype_.IsBool() && !dtype_.IsFloat() && !dtype_.IsInt()) { - std::string err_msg = - "Mask: Only supports bool or numeric datatype for generated mask type, but got:" + dtype_.ToString(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr MaskOperation::Build() { return std::make_shared(op_, constant_, dtype_); } - -Status MaskOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["op"] = op_; - nlohmann::json constant; - RETURN_IF_NOT_OK(constant_->to_json(&constant)); - args["constant"] = constant; - args["dtype"] = dtype_.value(); - *out_json = args; - return Status::OK(); -} - -Status MaskOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "op", kMaskOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "constant", kMaskOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "dtype", kMaskOperation)); - RelationalOp op = op_params["op"]; - std::shared_ptr constant; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["constant"], &constant)); - auto dtype = DataType(static_cast(op_params["dtype"])); - *operation = std::make_shared(op, constant, dtype); - return Status::OK(); -} -#endif - -// OneHotOperation -OneHotOperation::OneHotOperation(int32_t num_classes, double smoothing_rate) - : num_classes_(num_classes), smoothing_rate_(smoothing_rate) {} - -Status OneHotOperation::ValidateParams() { - if (num_classes_ <= 0) { - std::string err_msg = "OneHot: Number of classes must be greater than 0, but got: " + std::to_string(num_classes_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (smoothing_rate_ < 0.0 || smoothing_rate_ > 1.0) { - std::string err_msg = "OneHot: Smoothing rate must be between 0 and 1, but got: " + std::to_string(smoothing_rate_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr OneHotOperation::Build() { return std::make_shared(num_classes_, smoothing_rate_); } - -Status OneHotOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_classes"] = num_classes_; - args["smoothing_rate"] = smoothing_rate_; - - *out_json = args; - return Status::OK(); -} - -Status OneHotOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_classes", kOneHotOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "smoothing_rate", kOneHotOperation)); - int32_t num_classes = op_params["num_classes"]; - double smoothing_rate = op_params["smoothing_rate"]; - *operation = std::make_shared(num_classes, smoothing_rate); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// PadEndOperation -PadEndOperation::PadEndOperation(const TensorShape &pad_shape, const std::shared_ptr &pad_value) - : pad_shape_(pad_shape), pad_value_(pad_value) {} - -Status PadEndOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr PadEndOperation::Build() { return std::make_shared(pad_shape_, pad_value_); } - -Status PadEndOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["pad_shape"] = pad_shape_.AsVector(); - nlohmann::json pad_value; - RETURN_IF_NOT_OK(pad_value_->to_json(&pad_value)); - args["pad_value"] = pad_value; - *out_json = args; - return Status::OK(); -} - -Status PadEndOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_shape", kPadEndOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_value", kPadEndOperation)); - std::vector shape_vector = op_params["pad_shape"]; - TensorShape pad_shape = TensorShape(shape_vector); - std::shared_ptr pad_value; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["pad_value"], &pad_value)); - *operation = std::make_shared(pad_shape, pad_value); - return Status::OK(); -} - -#if !defined(_WIN32) && !defined(_WIN64) -// ParseExampleOperation -ParseExampleOperation::ParseExampleOperation(DataSchema schema, std::vector column_list, - bool parallel_parse) - : schema_(std::move(schema)), column_list_(std::move(column_list)), parallel_parse_(parallel_parse) {} - -std::shared_ptr ParseExampleOperation::Build() { - return std::make_shared(schema_, column_list_, parallel_parse_); -} -#endif -#endif - -// PreBuiltOperation -PreBuiltOperation::PreBuiltOperation(std::shared_ptr tensor_op) : op_(std::move(tensor_op)) { -#ifdef ENABLE_PYTHON - auto pyfunc_tensor_op = std::dynamic_pointer_cast(op_); - if (pyfunc_tensor_op && pyfunc_tensor_op->IsRandom()) { - random_op_ = true; - } -#endif -} - -Status PreBuiltOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr PreBuiltOperation::Build() { return op_; } - -std::string PreBuiltOperation::Name() const { return op_ ? op_->Name() : kPreBuiltOperation; } - -Status PreBuiltOperation::to_json(nlohmann::json *out_json) { - RETURN_IF_NOT_OK(op_->to_json(out_json)); - return Status::OK(); -} - -// RandomApplyOperation -RandomApplyOperation::RandomApplyOperation(const std::vector> &transforms, double prob) - : TensorOperation(true), transforms_(transforms), prob_(prob) {} - -Status RandomApplyOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomApply", transforms_)); - RETURN_IF_NOT_OK(ValidateProbability("RandomApply", prob_)); - return Status::OK(); -} - -std::shared_ptr RandomApplyOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](std::shared_ptr op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops, prob_); -} - -Status RandomApplyOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - (*out_json)["prob"] = prob_; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RandomApplyOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kRandomApplyOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomApplyOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - double prob = op_params["prob"]; - *operation = std::make_shared(operations, prob); - return Status::OK(); -} -#endif - -// RandomChoiceOperation -RandomChoiceOperation::RandomChoiceOperation(const std::vector> &transforms) - : TensorOperation(true), transforms_(transforms) {} - -Status RandomChoiceOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomChoice", transforms_)); - return Status::OK(); -} - -std::shared_ptr RandomChoiceOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](const auto &op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops); -} - -Status RandomChoiceOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RandomChoiceOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kRandomChoiceOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - *operation = std::make_shared(operations); - return Status::OK(); -} - -// SliceOperation -SliceOperation::SliceOperation(const std::vector &slice_input) : slice_input_(slice_input) {} - -Status SliceOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr SliceOperation::Build() { return std::make_shared(slice_input_); } -#endif - -// TypeCastOperation -// DataType data_type - required for C++ API -TypeCastOperation::TypeCastOperation(const DataType &data_type) : data_type_(data_type) {} - -// std::string data_type - required for Pybind -TypeCastOperation::TypeCastOperation(const std::string &data_type) { - // Convert from string to DEType - DataType temp_data_type(data_type); - data_type_ = temp_data_type; -} - -Status TypeCastOperation::ValidateParams() { - if (data_type_ == DataType::DE_UNKNOWN) { - std::string err_msg = "TypeCast: Invalid data type"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr TypeCastOperation::Build() { return std::make_shared(data_type_); } - -Status TypeCastOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["data_type"] = data_type_.ToString(); - *out_json = args; - return Status::OK(); -} - -Status TypeCastOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "data_type", kTypeCastOperation)); - std::string data_type = op_params["data_type"]; - *operation = std::make_shared(data_type); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// UniqueOperation -Status UniqueOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr UniqueOperation::Build() { return std::make_shared(); } - -Status UniqueOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -Status PluginOperation::ValidateParams() { - std::string err_msg; - err_msg += lib_path_.empty() ? "lib_path is empty, please specify a path to .so file. " : ""; - err_msg += func_name_.empty() ? "func_name_ is empty, please specify function name to load." : ""; - if (!err_msg.empty()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} -std::shared_ptr PluginOperation::Build() { - return std::make_shared(lib_path_, func_name_, user_args_); -} - -Status PluginOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["lib_path"] = lib_path_; - args["func_name"] = func_name_; - args["user_args"] = user_args_; - *out_json = args; - return Status::OK(); -} - -Status PluginOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "lib_path", kPluginOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "func_name", kPluginOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "user_args", kPluginOperation)); - std::string lib_path = op_params["lib_path"]; - std::string func_name = op_params["func_name"]; - std::string user_args = op_params["user_args"]; - *operation = std::make_shared(lib_path, func_name, user_args); - return Status::OK(); -} -#endif -} // namespace transforms -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h deleted file mode 100644 index 86b910311..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h +++ /dev/null @@ -1,372 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_DATA_TRANSFORMS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_DATA_TRANSFORMS_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/data_type.h" -#include "mindspore-lite/minddata/dataset/engine/data_schema.h" -#include "mindspore-lite/minddata/dataset/include/dataset/datasets.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -// Transform operations for performing data transformation. -namespace transforms { -// Char arrays storing name of corresponding classes (in alphabetical order) -constexpr char kComposeOperation[] = "Compose"; -constexpr char kConcatenateOperation[] = "Concatenate"; -constexpr char kDuplicateOperation[] = "Duplicate"; -constexpr char kFillOperation[] = "Fill"; -constexpr char kMaskOperation[] = "Mask"; -constexpr char kOneHotOperation[] = "OneHot"; -constexpr char kPadEndOperation[] = "PadEnd"; -constexpr char kParseExampleOperation[] = "ParseExample"; -constexpr char kPluginOperation[] = "Plugin"; -constexpr char kPreBuiltOperation[] = "PreBuilt"; -constexpr char kRandomApplyOperation[] = "RandomApply"; -constexpr char kRandomChoiceOperation[] = "RandomChoice"; -constexpr char kSliceOperation[] = "Slice"; -constexpr char kTypeCastOperation[] = "TypeCast"; -constexpr char kUniqueOperation[] = "Unique"; -/* ####################################### Derived TensorOperation classes ################################# */ - -class ComposeOperation : public TensorOperation { - public: - explicit ComposeOperation(const std::vector> &transforms); - - ~ComposeOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kComposeOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(const nlohmann::json &op_params, std::shared_ptr *operation); - - // Get the compose type: kInvalid / kAscend910B / kCpu. - virtual MapTargetDevice Type() { - bool have_dvpp = false; - bool have_cpu = false; - for (auto &item : transforms_) { - if (item->Type() == MapTargetDevice::kAscend910B) { - have_dvpp = true; - } else if (item->Type() == MapTargetDevice::kCpu) { - have_cpu = true; - } else { - MS_LOG(ERROR) << "The transform: " << item->Name() << " is not Ascend or Cpu."; - return MapTargetDevice::kInvalid; - } - } - - if (have_dvpp && have_cpu) { - MS_LOG(ERROR) << "Currently, it is not supported to mix DVPP transforms with CPU transforms in Compose."; - return MapTargetDevice::kInvalid; - } else if (have_dvpp) { - return MapTargetDevice::kAscend910B; - } else { - return MapTargetDevice::kCpu; - } - } - - private: - std::vector> transforms_; -}; - -class ConcatenateOperation : public TensorOperation { - public: - ConcatenateOperation(int8_t axis, const std::shared_ptr &prepend, const std::shared_ptr &append); - - ~ConcatenateOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kConcatenateOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(const nlohmann::json &op_params, std::shared_ptr *operation); - - private: - int8_t axis_; - std::shared_ptr prepend_; - std::shared_ptr append_; -}; - -class DuplicateOperation : public TensorOperation { - public: - DuplicateOperation() = default; - - ~DuplicateOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDuplicateOperation; } - - static Status from_json(const nlohmann::json &op_params, std::shared_ptr *operation); -}; - -class FillOperation : public TensorOperation { - public: - explicit FillOperation(const std::shared_ptr &fill_value); - - ~FillOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kFillOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::shared_ptr fill_value_; -}; - -class MaskOperation : public TensorOperation { - public: - MaskOperation(RelationalOp op, const std::shared_ptr &constant, const DataType &dtype); - - ~MaskOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kMaskOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(const nlohmann::json &op_params, std::shared_ptr *operation); - - private: - RelationalOp op_; - std::shared_ptr constant_; - DataType dtype_; -}; - -class OneHotOperation : public TensorOperation { - public: - explicit OneHotOperation(int32_t num_classes, double smoothing_rate = 0.0); - - ~OneHotOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kOneHotOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t num_classes_; - double smoothing_rate_; -}; - -class PadEndOperation : public TensorOperation { - public: - PadEndOperation(const TensorShape &pad_shape, const std::shared_ptr &pad_value); - - ~PadEndOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kPadEndOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - TensorShape pad_shape_; - std::shared_ptr pad_value_; -}; - -class ParseExampleOperation : public TensorOperation { - public: - ParseExampleOperation(DataSchema schema, std::vector column_list, bool parallel_parse); - - ~ParseExampleOperation() override = default; - - std::shared_ptr Build() override; - - std::string Name() const override { return kParseExampleOperation; } - - private: - DataSchema schema_; - std::vector column_list_; - bool parallel_parse_; -}; - -class PreBuiltOperation : public TensorOperation { - public: - explicit PreBuiltOperation(std::shared_ptr tensor_op); - - ~PreBuiltOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - private: - std::shared_ptr op_; -}; - -class RandomApplyOperation : public TensorOperation { - public: - RandomApplyOperation(const std::vector> &transforms, double prob); - - ~RandomApplyOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kRandomApplyOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector> transforms_; - double prob_; -}; - -class RandomChoiceOperation : public TensorOperation { - public: - explicit RandomChoiceOperation(const std::vector> &transforms); - - ~RandomChoiceOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kRandomChoiceOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector> transforms_; -}; - -class SliceOperation : public TensorOperation { - public: - explicit SliceOperation(const std::vector &slice_input); - - ~SliceOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kSliceOperation; } - - private: - std::vector slice_input_; -}; - -class TypeCastOperation : public TensorOperation { - public: - explicit TypeCastOperation(const DataType &data_type); // Used for C++ API - - explicit TypeCastOperation(const std::string &data_type); // Used for Pybind - - ~TypeCastOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kTypeCastOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - DataType data_type_; -}; - -#ifndef ENABLE_ANDROID -class UniqueOperation : public TensorOperation { - public: - UniqueOperation() = default; - - ~UniqueOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kUniqueOperation; } - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; - -class PluginOperation : public TensorOperation { - public: - explicit PluginOperation(const std::string &lib_path, const std::string &func_name, const std::string &user_args) - : lib_path_(lib_path), func_name_(func_name), user_args_(user_args) {} - - ~PluginOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kPluginOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::string lib_path_; - std::string func_name_; - std::string user_args_; -}; -#endif -} // namespace transforms -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_DATA_TRANSFORMS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h b/mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h deleted file mode 100644 index e8ac8c302..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_TENSOR_OPERATION_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_TENSOR_OPERATION_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Abstract class to represent a dataset in the data pipeline. -class TensorOperation : public std::enable_shared_from_this { - public: - /// \brief Constructor - TensorOperation() : random_op_(false) {} - - /// \brief Constructor - explicit TensorOperation(bool random) : random_op_(random) {} - - /// \brief Destructor - virtual ~TensorOperation() = default; - - /// \brief Pure virtual function to convert a TensorOperation class into a runtime TensorOp object. - /// \return shared pointer to the newly created TensorOp. - virtual std::shared_ptr Build() = 0; - - virtual Status ValidateParams() { return Status::OK(); } - - virtual std::string Name() const = 0; - - /// \brief Check whether the operation is deterministic. - /// \return true if this op is a random op (returns non-deterministic result e.g. RandomCrop) - bool IsRandomOp() const { return random_op_; } - - virtual Status to_json(nlohmann::json *out_json) { return Status::OK(); } - - virtual MapTargetDevice Type() { return MapTargetDevice::kCpu; } - - protected: - bool random_op_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_TENSOR_OPERATION_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/transforms_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/transforms_ir.cc deleted file mode 100644 index c6c0d6fc6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/transforms_ir.cc +++ /dev/null @@ -1,533 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/data/transforms_ir.h" - -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#endif - -// Kernel data headers (in alphabetical order) -#include "mindspore-lite/minddata/dataset/kernels/data/compose_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/concatenate_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/duplicate_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/fill_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/mask_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/one_hot_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/pad_end_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/parse_example_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/random_apply_op.h" -#include "mindspore-lite/minddata/dataset/kernels/data/random_choice_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/slice_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/data/type_cast_op.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/data/unique_op.h" -#endif - -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/plugin_op.h" -#endif -#ifdef ENABLE_PYTHON -#include "mindspore-lite/minddata/dataset/kernels/py_func_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -// Transform operations for data. -namespace transforms { -/* ####################################### Derived TensorOperation classes ################################# */ -// (In alphabetical order) - -// ComposeOperation -ComposeOperation::ComposeOperation(const std::vector> &transforms) - : transforms_(transforms) {} - -Status ComposeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("Compose", transforms_)); - return Status::OK(); -} - -std::shared_ptr ComposeOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](const auto &op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops); -} - -Status ComposeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status ComposeOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kComposeOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - *operation = std::make_shared(operations); - return Status::OK(); -} - -// ConcatenateOperation -ConcatenateOperation::ConcatenateOperation(int8_t axis, const std::shared_ptr &prepend, - const std::shared_ptr &append) - : axis_(axis), prepend_(prepend), append_(append) {} - -Status ConcatenateOperation::ValidateParams() { - if (axis_ != 0 && axis_ != -1) { - std::string err_msg = - "Concatenate: Only 1D concatenation supported, input 'axis' should be 0 or -1, but got:" + std::to_string(axis_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (prepend_) { - if (prepend_->shape().Size() != 1) { - std::string err_msg = "Concatenate: Can only prepend 1D arrays, rank of input 'prepend' should be 1, but got:" + - std::to_string(prepend_->shape().Size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (append_) { - if (append_->shape().Size() != 1) { - std::string err_msg = "Concatenate: Can only append 1D arrays, rank of input 'append' should be 1, but got:" + - std::to_string(append_->shape().Size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - return Status::OK(); -} - -std::shared_ptr ConcatenateOperation::Build() { - return std::make_shared(axis_, prepend_, append_); -} - -Status ConcatenateOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["axis"] = axis_; - nlohmann::json prepend; - nlohmann::json append; - RETURN_IF_NOT_OK(prepend_->to_json(&prepend)); - RETURN_IF_NOT_OK(append_->to_json(&append)); - args["prepend"] = prepend; - args["append"] = append; - *out_json = args; - return Status::OK(); -} - -Status ConcatenateOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "axis", kConcatenateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prepend", kConcatenateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "append", kConcatenateOperation)); - int8_t axis = op_params["axis"]; - std::shared_ptr prepend; - std::shared_ptr append; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["prepend"], &prepend)); - RETURN_IF_NOT_OK(Tensor::from_json(op_params["append"], &append)); - *operation = std::make_shared(axis, prepend, append); - return Status::OK(); -} -#endif - -// DuplicateOperation -Status DuplicateOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr DuplicateOperation::Build() { return std::make_shared(); } - -Status DuplicateOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// FillOperation -FillOperation::FillOperation(const std::shared_ptr &fill_value) : fill_value_(fill_value) {} - -Status FillOperation::ValidateParams() { - if (fill_value_->shape() != TensorShape::CreateScalar()) { - std::string err_msg = "Fill: fill_value is not a scalar tensor, got shape:" + fill_value_->shape().ToString(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr FillOperation::Build() { return std::make_shared(fill_value_); } - -Status FillOperation::to_json(nlohmann::json *out_json) { - RETURN_IF_NOT_OK(fill_value_->to_json(out_json)); - return Status::OK(); -} - -Status FillOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - std::shared_ptr fill_value; - RETURN_IF_NOT_OK(Tensor::from_json(op_params, &fill_value)); - *operation = std::make_shared(fill_value); - return Status::OK(); -} - -// MaskOperation -MaskOperation::MaskOperation(RelationalOp op, const std::shared_ptr &constant, const DataType &dtype) - : op_(op), constant_(constant), dtype_(dtype) {} - -Status MaskOperation::ValidateParams() { - if (!dtype_.IsBool() && !dtype_.IsFloat() && !dtype_.IsInt()) { - std::string err_msg = - "Mask: Only supports bool or numeric datatype for generated mask type, but got:" + dtype_.ToString(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr MaskOperation::Build() { return std::make_shared(op_, constant_, dtype_); } - -Status MaskOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["op"] = op_; - nlohmann::json constant; - RETURN_IF_NOT_OK(constant_->to_json(&constant)); - args["constant"] = constant; - args["dtype"] = dtype_.value(); - *out_json = args; - return Status::OK(); -} - -Status MaskOperation::from_json(const nlohmann::json &op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "op", kMaskOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "constant", kMaskOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "dtype", kMaskOperation)); - RelationalOp op = op_params["op"]; - std::shared_ptr constant; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["constant"], &constant)); - auto dtype = DataType(static_cast(op_params["dtype"])); - *operation = std::make_shared(op, constant, dtype); - return Status::OK(); -} -#endif - -// OneHotOperation -OneHotOperation::OneHotOperation(int32_t num_classes, double smoothing_rate) - : num_classes_(num_classes), smoothing_rate_(smoothing_rate) {} - -Status OneHotOperation::ValidateParams() { - if (num_classes_ <= 0) { - std::string err_msg = "OneHot: Number of classes must be greater than 0, but got: " + std::to_string(num_classes_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (smoothing_rate_ < 0.0 || smoothing_rate_ > 1.0) { - std::string err_msg = "OneHot: Smoothing rate must be between 0 and 1, but got: " + std::to_string(smoothing_rate_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr OneHotOperation::Build() { return std::make_shared(num_classes_, smoothing_rate_); } - -Status OneHotOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_classes"] = num_classes_; - args["smoothing_rate"] = smoothing_rate_; - - *out_json = args; - return Status::OK(); -} - -Status OneHotOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_classes", kOneHotOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "smoothing_rate", kOneHotOperation)); - int32_t num_classes = op_params["num_classes"]; - double smoothing_rate = op_params["smoothing_rate"]; - *operation = std::make_shared(num_classes, smoothing_rate); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// PadEndOperation -PadEndOperation::PadEndOperation(const TensorShape &pad_shape, const std::shared_ptr &pad_value) - : pad_shape_(pad_shape), pad_value_(pad_value) {} - -Status PadEndOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr PadEndOperation::Build() { return std::make_shared(pad_shape_, pad_value_); } - -Status PadEndOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["pad_shape"] = pad_shape_.AsVector(); - nlohmann::json pad_value; - RETURN_IF_NOT_OK(pad_value_->to_json(&pad_value)); - args["pad_value"] = pad_value; - *out_json = args; - return Status::OK(); -} - -Status PadEndOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_shape", kPadEndOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_value", kPadEndOperation)); - std::vector shape_vector = op_params["pad_shape"]; - TensorShape pad_shape = TensorShape(shape_vector); - std::shared_ptr pad_value; - RETURN_IF_NOT_OK(Tensor::from_json(op_params["pad_value"], &pad_value)); - *operation = std::make_shared(pad_shape, pad_value); - return Status::OK(); -} - -#if !defined(_WIN32) && !defined(_WIN64) -// ParseExampleOperation -ParseExampleOperation::ParseExampleOperation(DataSchema schema, std::vector column_list, - bool parallel_parse) - : schema_(std::move(schema)), column_list_(std::move(column_list)), parallel_parse_(parallel_parse) {} - -std::shared_ptr ParseExampleOperation::Build() { - return std::make_shared(schema_, column_list_, parallel_parse_); -} -#endif -#endif - -// PreBuiltOperation -PreBuiltOperation::PreBuiltOperation(std::shared_ptr tensor_op) : op_(std::move(tensor_op)) { -#ifdef ENABLE_PYTHON - auto pyfunc_tensor_op = std::dynamic_pointer_cast(op_); - if (pyfunc_tensor_op && pyfunc_tensor_op->IsRandom()) { - random_op_ = true; - } -#endif -} - -Status PreBuiltOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr PreBuiltOperation::Build() { return op_; } - -std::string PreBuiltOperation::Name() const { return op_ ? op_->Name() : kPreBuiltOperation; } - -Status PreBuiltOperation::to_json(nlohmann::json *out_json) { - RETURN_IF_NOT_OK(op_->to_json(out_json)); - return Status::OK(); -} - -// RandomApplyOperation -RandomApplyOperation::RandomApplyOperation(const std::vector> &transforms, double prob) - : TensorOperation(true), transforms_(transforms), prob_(prob) {} - -Status RandomApplyOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomApply", transforms_)); - RETURN_IF_NOT_OK(ValidateProbability("RandomApply", prob_)); - return Status::OK(); -} - -std::shared_ptr RandomApplyOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](std::shared_ptr op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops, prob_); -} - -Status RandomApplyOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - (*out_json)["prob"] = prob_; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RandomApplyOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kRandomApplyOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomApplyOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - double prob = op_params["prob"]; - *operation = std::make_shared(operations, prob); - return Status::OK(); -} -#endif - -// RandomChoiceOperation -RandomChoiceOperation::RandomChoiceOperation(const std::vector> &transforms) - : TensorOperation(true), transforms_(transforms) {} - -Status RandomChoiceOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomChoice", transforms_)); - return Status::OK(); -} - -std::shared_ptr RandomChoiceOperation::Build() { - std::vector> tensor_ops; - (void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](const auto &op) -> std::shared_ptr { return op->Build(); }); - return std::make_shared(tensor_ops); -} - -Status RandomChoiceOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto transforms = nlohmann::json::array(); - for (auto &tensor_operation : transforms_) { - nlohmann::json tensor_op, args; - RETURN_IF_NOT_OK(tensor_operation->to_json(&args)); - tensor_op["tensor_op_params"] = args; - tensor_op["tensor_op_name"] = tensor_operation->Name(); - transforms.push_back(tensor_op); - } - (*out_json)["transforms"] = transforms; - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -Status RandomChoiceOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kRandomChoiceOperation)); - nlohmann::json transforms = op_params["transforms"]; - std::vector> operations; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(transforms, &operations)); - *operation = std::make_shared(operations); - return Status::OK(); -} - -// SliceOperation -SliceOperation::SliceOperation(const std::vector &slice_input) : slice_input_(slice_input) {} - -Status SliceOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr SliceOperation::Build() { return std::make_shared(slice_input_); } -#endif - -// TypeCastOperation -// DataType data_type - required for C++ API -TypeCastOperation::TypeCastOperation(const DataType &data_type) : data_type_(data_type) {} - -// std::string data_type - required for Pybind -TypeCastOperation::TypeCastOperation(const std::string &data_type) { - // Convert from string to DEType - DataType temp_data_type(data_type); - data_type_ = temp_data_type; -} - -Status TypeCastOperation::ValidateParams() { - if (data_type_ == DataType::DE_UNKNOWN) { - std::string err_msg = "TypeCast: Invalid data type"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr TypeCastOperation::Build() { return std::make_shared(data_type_); } - -Status TypeCastOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["data_type"] = data_type_.ToString(); - *out_json = args; - return Status::OK(); -} - -Status TypeCastOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "data_type", kTypeCastOperation)); - std::string data_type = op_params["data_type"]; - *operation = std::make_shared(data_type); - return Status::OK(); -} - -#ifndef ENABLE_ANDROID -// UniqueOperation -Status UniqueOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr UniqueOperation::Build() { return std::make_shared(); } - -Status UniqueOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -Status PluginOperation::ValidateParams() { - std::string err_msg; - err_msg += lib_path_.empty() ? "lib_path is empty, please specify a path to .so file. " : ""; - err_msg += func_name_.empty() ? "func_name_ is empty, please specify function name to load." : ""; - if (!err_msg.empty()) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} -std::shared_ptr PluginOperation::Build() { - return std::make_shared(lib_path_, func_name_, user_args_); -} - -Status PluginOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["lib_path"] = lib_path_; - args["func_name"] = func_name_; - args["user_args"] = user_args_; - *out_json = args; - return Status::OK(); -} - -Status PluginOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "lib_path", kPluginOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "func_name", kPluginOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "user_args", kPluginOperation)); - std::string lib_path = op_params["lib_path"]; - std::string func_name = op_params["func_name"]; - std::string user_args = op_params["user_args"]; - *operation = std::make_shared(lib_path, func_name, user_args); - return Status::OK(); -} -#endif -} // namespace transforms -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/validators.cc b/mindspore-lite/minddata/dataset/kernels/ir/validators.cc deleted file mode 100644 index 1c86342b2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/validators.cc +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" - -namespace mindspore { -namespace dataset { -/* ####################################### Validator Functions ############################################ */ -Status ValidateProbability(const std::string &op_name, double probability) { - if (probability < 0.0 || probability > 1.0) { - std::string err_msg = op_name + ": probability must be between 0.0 and 1.0, got: " + std::to_string(probability); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -Status ValidateIntScalarPositive(const std::string &op_name, const std::string &scalar_name, int32_t scalar) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, scalar_name, scalar, {0}, true)); - return Status::OK(); -} - -Status ValidateFloatScalarPositive(const std::string &op_name, const std::string &scalar_name, float scalar) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, scalar_name, scalar, {0}, true)); - return Status::OK(); -} - -Status ValidateFloatScalarNonNegative(const std::string &op_name, const std::string &scalar_name, float scalar) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, scalar_name, scalar, {0}, false)); - return Status::OK(); -} - -Status ValidateVectorFillvalue(const std::string &op_name, const std::vector &fill_value) { - const size_t kMaxFillValueSize = 3; - if (fill_value.empty() || (fill_value.size() != 1 && fill_value.size() != kMaxFillValueSize)) { - std::string err_msg = - op_name + ": fill_value expecting size 1 or 3, got fill_value.size(): " + std::to_string(fill_value.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // Note that fill_value need to be in range [0, 255], - // but we omit the check since its type is uint8_t - return Status::OK(); -} - -Status ValidateVectorColorAttribute(const std::string &op_name, const std::string &attr_name, - const std::vector &attr, const std::vector &range) { - const size_t kMaxAttrSize = 2; - if (attr.empty() || attr.size() > kMaxAttrSize) { - std::string err_msg = op_name + ":" + attr_name + " expecting size 1 or 2, but got: " + std::to_string(attr.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (auto &attr_val : attr) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, attr_name, attr_val, range, false, false)); - } - if (attr.size() == kMaxAttrSize && (attr[0] > attr[1])) { - std::string err_msg = op_name + ":" + attr_name + - " lower bound must be less or equal to upper bound, got lb: " + std::to_string(attr[0]) + - ", ub: " + std::to_string(attr[1]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -Status ValidateVectorMeanStd(const std::string &op_name, const std::vector &mean, - const std::vector &std) { - if (mean.empty()) { - std::string err_msg = op_name + ": mean expecting non-empty vector"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (std.empty()) { - std::string err_msg = op_name + ": std expecting non-empty vector"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (mean.size() != std.size()) { - std::string err_msg = op_name + ": mean and std vectors are expected to be of the same size"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // check std/mean value - for (int32_t i = 0; i < std.size(); ++i) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, "mean", mean[i], {0.0, 255.0}, false, false)); - RETURN_IF_NOT_OK(ValidateScalar(op_name, "std", std[i], {0.0, 255.0}, true, false)); - } - - return Status::OK(); -} - -Status ValidateVectorOdd(const std::string &op_name, const std::string &vec_name, const std::vector &value) { - constexpr int64_t divided_two = 2; - for (int i = 0; i < value.size(); i++) { - if (value[i] % divided_two != 1) { - std::string err_msg = op_name + ":" + vec_name + " must be odd value, got: " + vec_name + "[" + - std::to_string(i) + "]=" + std::to_string(value[i]); - MS_LOG(ERROR) << err_msg; - RETURN_SYNTAX_ERROR(err_msg); - } - } - return Status::OK(); -} - -Status ValidateVectorPadding(const std::string &op_name, const std::vector &padding) { - const size_t kDefaultPaddingSize = 2; - const size_t kMaxPaddingSize = 4; - if (padding.size() != 1 && padding.size() != kDefaultPaddingSize && padding.size() != kMaxPaddingSize) { - std::string err_msg = op_name + ": padding expecting size 1, 2 or 4, got size: " + std::to_string(padding.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (const auto &pad_val : padding) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, "padding", pad_val, {0, INT_MAX}, false, false)); - } - - return Status::OK(); -} - -Status ValidateVectorPositive(const std::string &op_name, const std::string &vec_name, - const std::vector &vec) { - for (const auto &vec_val : vec) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, vec_name, vec_val, {0}, true)); - } - - return Status::OK(); -} - -Status ValidateVectorNonNegative(const std::string &op_name, const std::string &vec_name, - const std::vector &vec) { - for (const auto &vec_val : vec) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, vec_name, vec_val, {0}, false)); - } - - return Status::OK(); -} - -Status ValidateVectorSigma(const std::string &op_name, const std::vector &sigma) { - const size_t kMaxSigmaSize = 2; - if (sigma.empty() || sigma.size() > kMaxSigmaSize) { - std::string err_msg = op_name + ": sigma expecting size 2, got sigma.size(): " + std::to_string(sigma.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (const auto &sigma_val : sigma) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, "sigma", sigma_val, {0}, false)); - } - - return Status::OK(); -} - -Status ValidateVectorSize(const std::string &op_name, const std::vector &size) { - const size_t kMaxSizeSize = 2; - if (size.empty() || size.size() > kMaxSizeSize) { - std::string err_msg = op_name + ": size expecting size 2, got size.size(): " + std::to_string(size.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (const auto &size_val : size) { - RETURN_IF_NOT_OK(ValidateScalar(op_name, "size", size_val, {0, INT_MAX}, true, false)); - } - - return Status::OK(); -} - -Status ValidateVectorScale(const std::string &op_name, const std::vector &scale) { - const size_t kScaleSize = 2; - if (scale.size() != kScaleSize) { - std::string err_msg = op_name + ": scale expecting size 2, got scale.size(): " + std::to_string(scale.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateScalar(op_name, "scale", scale[0], {0}, false)); - RETURN_IF_NOT_OK(ValidateScalar(op_name, "scale", scale[1], {0}, true)); - if (scale[1] < scale[0]) { - std::string err_msg = op_name + ": scale must be in the format of (min, max), but got: (" + - std::to_string(scale[0]) + ", " + std::to_string(scale[1]) + ")."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -Status ValidateVectorRatio(const std::string &op_name, const std::vector &ratio) { - const size_t kRatioSize = 2; - if (ratio.size() != kRatioSize) { - std::string err_msg = op_name + ": ratio expecting size 2, got ratio.size(): " + std::to_string(ratio.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateScalar(op_name, "ratio", ratio[0], {0}, true)); - RETURN_IF_NOT_OK(ValidateScalar(op_name, "ratio", ratio[1], {0}, true)); - if (ratio[1] < ratio[0]) { - std::string err_msg = op_name + ": ratio must be in the format of (min, max), but got: (" + - std::to_string(ratio[0]) + ", " + std::to_string(ratio[1]) + ")."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -Status ValidateVectorTransforms(const std::string &op_name, - const std::vector> &transforms) { - if (transforms.empty()) { - std::string err_msg = op_name + ": transform list must not be empty."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < transforms.size(); ++i) { - if (transforms[i] == nullptr) { - std::string err_msg = - op_name + ": transform ops must not be null, got transform[" + std::to_string(i) + "] == nullptr."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } else { - RETURN_IF_NOT_OK(transforms[i]->ValidateParams()); - } - } - - return Status::OK(); -} - -bool CmpFloat(const float a, const float b, float epsilon) { return (std::fabs(a - b) < epsilon); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/validators.h b/mindspore-lite/minddata/dataset/kernels/ir/validators.h deleted file mode 100644 index ac637c5ee..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/validators.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VALIDATORS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VALIDATORS_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Helper function to validate probability -Status ValidateProbability(const std::string &op_name, double probability); - -// Helper function to positive int scalar -Status ValidateIntScalarPositive(const std::string &op_name, const std::string &scalar_name, int32_t scalar); - -// Helper function to positive int scalar -Status ValidateIntScalarNonNegative(const std::string &op_name, const std::string &scalar_name, int32_t scalar); - -// Helper function to positive float scalar -Status ValidateFloatScalarPositive(const std::string &op_name, const std::string &scalar_name, float scalar); - -// Helper function to non-negative float scalar -Status ValidateFloatScalarNonNegative(const std::string &op_name, const std::string &scalar_name, float scalar); - -// Helper function to validate scalar -template -Status ValidateScalar(const std::string &op_name, const std::string &scalar_name, const T scalar, - const std::vector &range, bool left_open_interval = false, bool right_open_interval = false) { - const size_t kRangeSize = 2; - if (range.empty() || range.size() > kRangeSize) { - std::string err_msg = op_name + ": expecting range size 1 or 2, but got: " + std::to_string(range.size()); - MS_LOG(ERROR) << err_msg; - RETURN_SYNTAX_ERROR(err_msg); - } - if ((left_open_interval && scalar <= range[0]) || (!left_open_interval && scalar < range[0])) { - std::string interval_description = left_open_interval ? " greater than " : " greater than or equal to "; - std::string err_msg = op_name + ": '" + scalar_name + "' must be" + interval_description + - std::to_string(range[0]) + ", got: " + std::to_string(scalar); - MS_LOG(ERROR) << err_msg; - RETURN_SYNTAX_ERROR(err_msg); - } - if (range.size() == kRangeSize) { - if ((right_open_interval && scalar >= range[1]) || (!right_open_interval && scalar > range[1])) { - std::string left_bracket = left_open_interval ? "(" : "["; - std::string right_bracket = right_open_interval ? ")" : "]"; - std::string err_msg = op_name + ":" + scalar_name + " is out of range " + left_bracket + - std::to_string(range[0]) + ", " + std::to_string(range[1]) + right_bracket + - ", got: " + std::to_string(scalar); - MS_LOG(ERROR) << err_msg; - RETURN_SYNTAX_ERROR(err_msg); - } - } - return Status::OK(); -} - -// Helper function to validate enum -template -Status ValidateEnum(const std::string &op_name, const std::string &enum_name, const T enumeration, - const std::vector &enum_list) { - auto existed = std::find(enum_list.begin(), enum_list.end(), enumeration); - std::string err_msg = op_name + ": Invalid " + enum_name + ", check input value of enum."; - if (existed != enum_list.end()) { - return Status::OK(); - } - RETURN_SYNTAX_ERROR(err_msg); -} - -// Helper function to validate color attribute -Status ValidateVectorColorAttribute(const std::string &op_name, const std::string &attr_name, - const std::vector &attr, const std::vector &range); - -// Helper function to validate fill value -Status ValidateVectorFillvalue(const std::string &op_name, const std::vector &fill_value); - -// Helper function to validate mean/std value -Status ValidateVectorMeanStd(const std::string &op_name, const std::vector &mean, const std::vector &std); - -// Helper function to validate odd value -Status ValidateVectorOdd(const std::string &op_name, const std::string &vec_name, const std::vector &value); - -// Helper function to validate padding -Status ValidateVectorPadding(const std::string &op_name, const std::vector &padding); - -// Helper function to validate positive value -Status ValidateVectorPositive(const std::string &op_name, const std::string &vec_name, const std::vector &vec); - -// Helper function to validate non-negative value -Status ValidateVectorNonNegative(const std::string &op_name, const std::string &vec_name, - const std::vector &vec); - -// Helper function to validate size of sigma -Status ValidateVectorSigma(const std::string &op_name, const std::vector &sigma); - -// Helper function to validate size of size -Status ValidateVectorSize(const std::string &op_name, const std::vector &size); - -// Helper function to validate scale -Status ValidateVectorScale(const std::string &op_name, const std::vector &scale); - -// Helper function to validate ratio -Status ValidateVectorRatio(const std::string &op_name, const std::vector &ratio); - -// Helper function to validate transforms -Status ValidateVectorTransforms(const std::string &op_name, - const std::vector> &transforms); - -// Helper function to compare float value -bool CmpFloat(float a, float b, float epsilon = 0.0000000001F); -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VALIDATORS_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.cc deleted file mode 100644 index 0662feef3..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_brightness_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_adjust_brightness_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustBrightnessOperation -AdjustBrightnessOperation::AdjustBrightnessOperation(float brightness_factor, const std::string &device_target) - : brightness_factor_(brightness_factor), device_target_(device_target) {} - -Status AdjustBrightnessOperation::ValidateParams() { - // brightness_factor - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustBrightness", "brightness_factor", brightness_factor_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AdjustBrightness: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AdjustBrightnessOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(brightness_factor_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(brightness_factor_); -#endif - } else { - MS_LOG(ERROR) << "AdjustBrightness: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AdjustBrightnessOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["brightness_factor"] = brightness_factor_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AdjustBrightnessOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "brightness_factor", kAdjustBrightnessOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAdjustBrightnessOperation)); - float brightness_factor = op_params["brightness_factor"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(brightness_factor, device_target); - return Status::OK(); -} - -MapTargetDevice AdjustBrightnessOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AdjustBrightness: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h deleted file mode 100644 index 86c3f5f3f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_brightness_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_BRIGHTNESS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_BRIGHTNESS_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustBrightnessOperation[] = "AdjustBrightness"; - -class AdjustBrightnessOperation : public TensorOperation { - public: - explicit AdjustBrightnessOperation(float brightness_factor, const std::string &device_target = "CPU"); - - ~AdjustBrightnessOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustBrightnessOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float brightness_factor_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_BRIGHTNESS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.cc deleted file mode 100644 index 295228745..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_contrast_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_adjust_contrast_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustContrastOperation -AdjustContrastOperation::AdjustContrastOperation(float contrast_factor, const std::string &device_target) - : contrast_factor_(contrast_factor), device_target_(device_target) {} - -Status AdjustContrastOperation::ValidateParams() { - // contrast_factor - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustContrast", "contrast_factor", contrast_factor_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AdjustContrast: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AdjustContrastOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(contrast_factor_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(contrast_factor_); -#endif - } else { - MS_LOG(ERROR) << "AdjustContrast: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AdjustContrastOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["contrast_factor"] = contrast_factor_; - args["device_target"] = contrast_factor_; - *out_json = args; - return Status::OK(); -} - -Status AdjustContrastOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "contrast_factor", kAdjustContrastOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAdjustContrastOperation)); - float contrast_factor = op_params["contrast_factor"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(contrast_factor, device_target); - return Status::OK(); -} - -MapTargetDevice AdjustContrastOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AdjustContrast: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h deleted file mode 100644 index 7ea701966..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_contrast_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_CONTRAST_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_CONTRAST_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustContrastOperation[] = "AdjustContrast"; - -class AdjustContrastOperation : public TensorOperation { - public: - explicit AdjustContrastOperation(float contrast_factor, const std::string &device_target = "CPU"); - - ~AdjustContrastOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustContrastOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float contrast_factor_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_CONTRAST_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc deleted file mode 100644 index 95ca28c53..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_gamma_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustGammaOperation -AdjustGammaOperation::AdjustGammaOperation(float gamma, float gain) : gamma_(gamma), gain_(gain) {} - -Status AdjustGammaOperation::ValidateParams() { - // gamma - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustGamma", "gamma", gamma_)); - return Status::OK(); -} - -std::shared_ptr AdjustGammaOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(gamma_, gain_); - return tensor_op; -} - -Status AdjustGammaOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["gamma"] = gamma_; - args["gain"] = gain_; - *out_json = args; - return Status::OK(); -} - -Status AdjustGammaOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "gamma", kAdjustGammaOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "gain", kAdjustGammaOperation)); - float gamma = op_params["gamma"]; - float gain = op_params["gain"]; - *operation = std::make_shared(gamma, gain); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h deleted file mode 100644 index ae111b20b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustGammaOperation[] = "AdjustGamma"; - -class AdjustGammaOperation : public TensorOperation { - public: - AdjustGammaOperation(float gamma, float gain); - - ~AdjustGammaOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustGammaOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float gamma_; - float gain_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.cc deleted file mode 100644 index 6b5ab3228..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_hue_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_adjust_hue_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustHueOperation -AdjustHueOperation::AdjustHueOperation(float hue_factor, const std::string &device_target) - : hue_factor_(hue_factor), device_target_(device_target) {} - -Status AdjustHueOperation::ValidateParams() { - // hue_factor - RETURN_IF_NOT_OK(ValidateScalar("AdjustHue", "hue_factor", hue_factor_, {-0.5, 0.5}, false, false)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AdjustHue: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AdjustHueOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(hue_factor_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(hue_factor_); -#endif - } else { - MS_LOG(ERROR) << "AdjustHue: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AdjustHueOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["hue_factor"] = hue_factor_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AdjustHueOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "hue_factor", kAdjustHueOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAdjustHueOperation)); - float hue_factor = op_params["hue_factor"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(hue_factor, device_target); - return Status::OK(); -} - -MapTargetDevice AdjustHueOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AdjustHue: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h deleted file mode 100644 index 00ca68a9d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_hue_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_HUE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_HUE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustHueOperation[] = "AdjustHue"; - -class AdjustHueOperation : public TensorOperation { - public: - explicit AdjustHueOperation(float hue_factor, const std::string &device_target = "CPU"); - - ~AdjustHueOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustHueOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float hue_factor_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_HUE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.cc deleted file mode 100644 index 11c64add1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/adjust_saturation_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_adjust_saturation_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustSaturationOperation -AdjustSaturationOperation::AdjustSaturationOperation(float saturation_factor, const std::string &device_target) - : saturation_factor_(saturation_factor), device_target_(device_target) {} - -Status AdjustSaturationOperation::ValidateParams() { - // saturation_factor - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustSaturation", "saturation_factor", saturation_factor_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AdjustSaturation: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AdjustSaturationOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(saturation_factor_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(saturation_factor_); -#endif - } else { - MS_LOG(ERROR) << "AdjustSaturation: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AdjustSaturationOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["saturation_factor"] = saturation_factor_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AdjustSaturationOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "saturation_factor", kAdjustSaturationOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAdjustSaturationOperation)); - float saturation_factor = op_params["saturation_factor"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(saturation_factor, device_target); - return Status::OK(); -} - -MapTargetDevice AdjustSaturationOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AdjustSaturation: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h deleted file mode 100644 index 4f72e6774..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_saturation_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SATURATION_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SATURATION_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustSaturationOperation[] = "AdjustSaturation"; - -class AdjustSaturationOperation : public TensorOperation { - public: - explicit AdjustSaturationOperation(float saturation_factor, const std::string &device_target = "CPU"); - - ~AdjustSaturationOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustSaturationOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float saturation_factor_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SATURATION_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.cc deleted file mode 100644 index 165b6d449..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.cc +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/sharpness_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_adjust_sharpness_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AdjustSharpnessOperation -AdjustSharpnessOperation::AdjustSharpnessOperation(float sharpness_factor, const std::string &device_target) - : sharpness_factor_(sharpness_factor), device_target_(device_target) {} - -Status AdjustSharpnessOperation::ValidateParams() { - // sharpness_factor - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustSharpness", "sharpness_factor", sharpness_factor_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AdjustSharpness: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AdjustSharpnessOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(sharpness_factor_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(sharpness_factor_); -#endif - } else { - MS_LOG(ERROR) << "AdjustSharpness: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AdjustSharpnessOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["sharpness_factor"] = sharpness_factor_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AdjustSharpnessOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "sharpness_factor", kAdjustSharpnessOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAdjustSharpnessOperation)); - float sharpness_factor = op_params["sharpness_factor"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(sharpness_factor, device_target); - return Status::OK(); -} - -MapTargetDevice AdjustSharpnessOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AdjustSharpness: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h deleted file mode 100644 index b7f6bb072..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/adjust_sharpness_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SHARPNESS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SHARPNESS_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAdjustSharpnessOperation[] = "AdjustSharpness"; - -class AdjustSharpnessOperation : public TensorOperation { - public: - explicit AdjustSharpnessOperation(float sharpness_factor, const std::string &device_target = "CPU"); - - ~AdjustSharpnessOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kAdjustSharpnessOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float sharpness_factor_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_SHARPNESS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.cc deleted file mode 100644 index 072e45b2e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.cc +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/affine_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_affine_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -AffineOperation::AffineOperation(float_t degrees, const std::vector &translation, float scale, - const std::vector &shear, InterpolationMode interpolation, - const std::vector &fill_value, const std::string &device_target) - : degrees_(degrees), - translation_(translation), - scale_(scale), - shear_(shear), - interpolation_(interpolation), - fill_value_(fill_value), - device_target_(device_target) {} - -AffineOperation::~AffineOperation() = default; - -std::string AffineOperation::Name() const { return kAffineOperation; } - -Status AffineOperation::ValidateParams() { - // Degrees - if (degrees_ < -180 || degrees_ > 180) { - std::string err_msg = "Affine: rotation angle in degrees between -180 and 180, but got " + std::to_string(degrees_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // Translate - constexpr size_t kExpectedTranslationSize = 2; - if (translation_.size() != kExpectedTranslationSize) { - std::string err_msg = - "Affine: translate expecting size 2, got: translation.size() = " + std::to_string(translation_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateScalar("Affine", "translate", translation_[0], {-1, 1}, false, false)); - RETURN_IF_NOT_OK(ValidateScalar("Affine", "translate", translation_[1], {-1, 1}, false, false)); - - // Scale - RETURN_IF_NOT_OK(ValidateScalar("Affine", "scale", scale_, {0}, true)); - - // Shear - constexpr size_t kExpectedShearSize = 2; - if (shear_.size() != kExpectedShearSize) { - std::string err_msg = "Affine: shear_ranges expecting size 2, got: shear.size() = " + std::to_string(shear_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (const auto &s : shear_) { - if (s < -180 || s > 180) { - std::string err_msg = "Affine: shear angle value between -180 and 180, but got " + std::to_string(s); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - - // Fill Value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("Affine", fill_value_)); - - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea) { - std::string err_msg = "Affine: Invalid InterpolationMode, only support Linear, Nearest, Cubic and Area."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Affine: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr AffineOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = - std::make_shared(degrees_, translation_, scale_, shear_, interpolation_, fill_value_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = - std::make_shared(degrees_, translation_, scale_, shear_, interpolation_, fill_value_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Affine: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AffineOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["degrees"] = degrees_; - args["translate"] = translation_; - args["scale"] = scale_; - args["shear"] = shear_; - args["resample"] = interpolation_; - args["fill_value"] = fill_value_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AffineOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degrees", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "translate", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "scale", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "shear", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "resample", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAffineOperation)); - float_t degrees = op_params["degrees"]; - std::vector translation = op_params["translate"]; - float scale = op_params["scale"]; - std::vector shear = op_params["shear"]; - auto interpolation = static_cast(op_params["resample"]); - std::vector fill_value = op_params["fill_value"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(degrees, translation, scale, shear, interpolation, fill_value, - device_target); - return Status::OK(); -} - -MapTargetDevice AffineOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Affine: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h deleted file mode 100644 index fbc3f42ad..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/affine_ir.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AFFINE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AFFINE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAffineOperation[] = "Affine"; - -class AffineOperation : public TensorOperation { - public: - AffineOperation(float_t degrees, const std::vector &translation, float scale, const std::vector &shear, - InterpolationMode interpolation, const std::vector &fill_value, - const std::string &device_target = "CPU"); - - ~AffineOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float degrees_; - std::vector translation_; - float scale_; - std::vector shear_; - InterpolationMode interpolation_; - std::vector fill_value_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AFFINE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc deleted file mode 100644 index 072be52d4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc +++ /dev/null @@ -1,492 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_crop_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_png_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_crop_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_resize_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_decode_video_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_normalize_op.h" -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend310/dvpp_resize_jpeg_op.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -// Transform operations for computer vision -namespace vision { -/* ####################################### Derived TensorOperation classes ################################# */ -// DvppCropOperation -DvppCropJpegOperation::DvppCropJpegOperation(const std::vector &crop) : crop_(crop) {} - -Status DvppCropJpegOperation::ValidateParams() { - // size - if (crop_.empty() || crop_.size() > 2) { - std::string err_msg = - "DvppCropJpeg: Crop resolution must be a vector of one or two elements, got: " + std::to_string(crop_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(crop_.begin(), crop_.end()) < 32 || *max_element(crop_.begin(), crop_.end()) > 2048) { - std::string err_msg = "Dvpp module supports crop image with resolution in range [32, 2048], got crop Parameters: "; - if (crop_.size() == 2) { - MS_LOG(ERROR) << err_msg << "[" << crop_[0] << ", " << crop_[1] << "]"; - } else { - MS_LOG(ERROR) << err_msg << "[" << crop_[0] << ", " << crop_[0] << "]"; - } - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr DvppCropJpegOperation::Build() { - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - uint32_t cropHeight; - uint32_t cropWidth; - // User specified the width value. - if (crop_.size() == 1) { - cropHeight = crop_[0]; - cropWidth = crop_[0]; - } else { - cropHeight = crop_[0]; - cropWidth = crop_[1]; - } - std::shared_ptr tensor_op = std::make_shared(cropHeight, cropWidth); - return tensor_op; -} - -Status DvppCropJpegOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = crop_; - *out_json = args; - return Status::OK(); -} - -Status DvppCropJpegOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kDvppCropJpegOperation)); - std::vector resize = op_params["size"]; - *operation = std::make_shared(resize); - return Status::OK(); -} - -// DvppDecodeResizeOperation -DvppDecodeResizeOperation::DvppDecodeResizeOperation(const std::vector &resize) : resize_(resize) {} - -Status DvppDecodeResizeOperation::ValidateParams() { - // size - if (resize_.empty() || resize_.size() > 2) { - std::string err_msg = "DvppDecodeResizeJpeg: resize resolution must be a vector of one or two elements, got: " + - std::to_string(resize_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(resize_.begin(), resize_.end()) < 32 || *max_element(resize_.begin(), resize_.end()) > 2048) { - std::string err_msg = - "Dvpp module supports resize image with resolution in range [32, 2048], got resize Parameters: "; - if (resize_.size() == 2) { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << ", " << resize_[1] << "]"; - } else { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << ", " << resize_[0] << "]"; - } - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr DvppDecodeResizeOperation::Build() { - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - uint32_t resizeHeight; - uint32_t resizeWidth; - // User specified the width value. - if (resize_.size() == 1) { - resizeHeight = resize_[0]; - resizeWidth = 0; - } else { - resizeHeight = resize_[0]; - resizeWidth = resize_[1]; - } - std::shared_ptr tensor_op = - std::make_shared(resizeHeight, resizeWidth); - return tensor_op; -} - -Status DvppDecodeResizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = resize_; - *out_json = args; - return Status::OK(); -} - -Status DvppDecodeResizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kDvppDecodeResizeOperation)); - std::vector resize = op_params["size"]; - *operation = std::make_shared(resize); - return Status::OK(); -} - -// DvppDecodeVideoOperation -DvppDecodeVideoOperation::DvppDecodeVideoOperation(const std::vector &size, VdecStreamFormat type, - VdecOutputFormat out_format, const std::string &output) - : size_(size), format_(out_format), en_type_(type), output_(output) {} - -Status DvppDecodeVideoOperation::ValidateParams() { - // check size_ - constexpr auto kTwoElements = 2; - if (size_.size() != kTwoElements) { - std::string err_msg = - "DvppDecodeVideo: Video frame size must be a vector of two elements, got: " + std::to_string(size_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // check height and width - uint32_t height; - uint32_t width; - height = size_[0]; - width = size_[1]; - - if ((width < kFrameWidthMin) || (width > kFrameWidthMax)) { - std::string err_msg = "DvppDecodeVideo: video frame width " + std::to_string(width) + - " is invalid, the legal range is [" + std::to_string(kFrameWidthMin) + ", " + - std::to_string(kFrameWidthMax) + "]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if ((height < kFrameHeightMin) || (height > kFrameHeightMax)) { - std::string err_msg = "DvppDecodeVideo: video frame height " + std::to_string(height) + - " is invalid, the legal range is [" + std::to_string(kFrameHeightMin) + ", " + - std::to_string(kFrameHeightMax) + "]"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - if (en_type_ < VdecStreamFormat::kH265MainLevel || en_type_ > VdecStreamFormat::kH264HighLevel) { - std::string err_msg = "DvppDecodeVideo: Invalid VdecStreamFormat, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (format_ < VdecOutputFormat::kYuvSemiplanar420 || format_ > VdecOutputFormat::kYvuSemiplanar420) { - std::string err_msg = "DvppDecodeVideo: Invalid VdecOutputFormat, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // check and normalize output path - Path output(output_); - if (!output.Exists()) { - RETURN_IF_NOT_OK(output.CreateDirectories()); - } - if (!output.IsDirectory()) { - std::string err_msg = "DvppDecodeVideo: Invalid out path, check path: " + output.ToString(); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - output_ = output.ToString(); - return Status::OK(); -} - -std::shared_ptr DvppDecodeVideoOperation::Build() { - uint32_t height; - uint32_t width; - height = size_[0]; - width = size_[1]; - auto tensor_op = std::make_shared(width, height, en_type_, format_, output_); - return tensor_op; -} - -Status DvppDecodeVideoOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["en_type"] = en_type_; - args["out_format"] = format_; - args["output"] = output_; - *out_json = args; - return Status::OK(); -} - -Status DvppDecodeVideoOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kDvppDecodeVideoOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "en_type", kDvppDecodeVideoOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "out_format", kDvppDecodeVideoOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "output", kDvppDecodeVideoOperation)); - - std::vector size = op_params["size"]; - auto type = static_cast(op_params["en_type"]); - auto out_format = static_cast(op_params["out_format"]); - std::string output = op_params["output"]; - - *operation = std::make_shared(size, type, out_format, output); - return Status::OK(); -} - -// DvppDecodeResizeCropOperation -DvppDecodeResizeCropOperation::DvppDecodeResizeCropOperation(const std::vector &crop, - const std::vector &resize) - : crop_(crop), resize_(resize) {} - -Status DvppDecodeResizeCropOperation::ValidateParams() { - // size - if (crop_.empty() || crop_.size() > 2) { - std::string err_msg = "DvppDecodeResizeCropJpeg: crop resolution must be a vector of one or two elements, got: " + - std::to_string(crop_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (resize_.empty() || resize_.size() > 2) { - std::string err_msg = "DvppDecodeResizeCropJpeg: resize resolution must be a vector of one or two elements, got: " + - std::to_string(resize_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(crop_.begin(), crop_.end()) < 32 || *max_element(crop_.begin(), crop_.end()) > 2048) { - std::string err_msg = "Dvpp module supports crop image with resolution in range [32, 2048], got Crop Parameters: "; - if (crop_.size() == 2) { - MS_LOG(ERROR) << err_msg << "[" << crop_[0] << ", " << crop_[1] << "]"; - } else { - MS_LOG(ERROR) << err_msg << "[" << crop_[0] << ", " << crop_[0] << "]"; - } - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(resize_.begin(), resize_.end()) < 32 || *max_element(resize_.begin(), resize_.end()) > 2048) { - std::string err_msg = - "Dvpp module supports resize image with resolution in range [32, 2048], got Crop Parameters: "; - if (resize_.size() == 2) { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << ", " << resize_[1] << "]"; - } else { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << "]"; - } - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (crop_.size() < resize_.size()) { - if (crop_[0] > std::min(resize_[0], resize_[1])) { - std::string err_msg = - "Each value of crop parameter must be smaller than corresponding resize parameter, for example: x[0] <= " - "y[0], and x[1] <= y[1], please verify your input parameters."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (crop_.size() > resize_.size()) { - if (std::max(crop_[0], crop_[1]) > resize_[0]) { - std::string err_msg = - "Each value of crop parameter must be smaller than corresponding resize parameter, for example: x[0] <= " - "y[0], and x[1] <= y[1], please verify your input parameters."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (crop_.size() == resize_.size()) { - for (int32_t i = 0; i < crop_.size(); ++i) { - if (crop_[i] > resize_[i]) { - std::string err_msg = - "Each value of crop parameter must be smaller than corresponding resize parameter, for example: x[0] <= " - "y[0], and x[1] <= y[1], please verify your input parameters."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - } - return Status::OK(); -} - -std::shared_ptr DvppDecodeResizeCropOperation::Build() { - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - uint32_t cropHeight; - uint32_t cropWidth; - uint32_t resizeHeight; - uint32_t resizeWidth; - if (crop_.size() == 1) { - cropHeight = crop_[0]; - cropWidth = crop_[0]; - } else { - cropHeight = crop_[0]; - cropWidth = crop_[1]; - } - // User specified the width value. - if (resize_.size() == 1) { - resizeHeight = resize_[0]; - resizeWidth = 0; - } else { - resizeHeight = resize_[0]; - resizeWidth = resize_[1]; - } - std::shared_ptr tensor_op = - std::make_shared(cropHeight, cropWidth, resizeHeight, resizeWidth); - return tensor_op; -} - -Status DvppDecodeResizeCropOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["crop_size"] = crop_; - args["resize_size"] = resize_; - *out_json = args; - return Status::OK(); -} - -Status DvppDecodeResizeCropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "crop_size", kDvppDecodeResizeCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "resize_size", kDvppDecodeResizeCropOperation)); - std::vector crop = op_params["crop_size"]; - std::vector resize = op_params["resize_size"]; - *operation = std::make_shared(crop, resize); - return Status::OK(); -} - -// DvppDecodeJPEG -Status DvppDecodeJpegOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr DvppDecodeJpegOperation::Build() { return std::make_shared(); } - -Status DvppDecodeJpegOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -// DvppDecodePNG -Status DvppDecodePngOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr DvppDecodePngOperation::Build() { return std::make_shared(); } - -Status DvppDecodePngOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} - -// DvppNormalize -DvppNormalizeOperation::DvppNormalizeOperation(const std::vector &mean, const std::vector &std) - : mean_(mean), std_(std) {} - -Status DvppNormalizeOperation::ValidateParams() { - constexpr auto kMaxElement = 256.0; - if (mean_.size() != 3) { - std::string err_msg = "DvppNormalization:: mean expecting size 3, got size: " + std::to_string(mean_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (std_.size() != 3) { - std::string err_msg = "DvppNormalization: std expecting size 3, got size: " + std::to_string(std_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(mean_.begin(), mean_.end()) < 0.0 || *max_element(mean_.begin(), mean_.end()) > kMaxElement) { - std::string err_msg = - "Normalization can take parameters in range [0, 256] according to math theory of mean and sigma, got mean " - "vector" + - std::to_string(std_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(std_.begin(), std_.end()) < 0.0 || *max_element(std_.begin(), std_.end()) > kMaxElement) { - std::string err_msg = - "Normalization can take parameters in range [0, 256] according to math theory of mean and sigma, got mean " - "vector" + - std::to_string(std_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr DvppNormalizeOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(mean_, std_); - return tensor_op; -} - -Status DvppNormalizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - std::vector enlarge_mean_; - std::vector enlarge_std_; - constexpr auto kEnlarge = 10000.; - (void)std::transform(mean_.begin(), mean_.end(), std::back_inserter(enlarge_mean_), - [&kEnlarge](float i) -> uint32_t { return static_cast(kEnlarge * i); }); - (void)std::transform(std_.begin(), std_.end(), std::back_inserter(enlarge_std_), - [&kEnlarge](float j) -> uint32_t { return static_cast(kEnlarge * j); }); - args["mean"] = enlarge_mean_; - args["std"] = enlarge_std_; - *out_json = args; - return Status::OK(); -} - -Status DvppNormalizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "mean", kDvppNormalizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "std", kDvppNormalizeOperation)); - std::vector mean = op_params["mean"]; - std::vector std = op_params["std"]; - *operation = std::make_shared(mean, std); - return Status::OK(); -} - -// DvppResizeOperation -DvppResizeJpegOperation::DvppResizeJpegOperation(const std::vector &resize) : resize_(resize) {} - -Status DvppResizeJpegOperation::ValidateParams() { - // size - constexpr int32_t kValidResizeSize = 2; - if (resize_.size() != 1 && resize_.size() != kValidResizeSize) { - std::string err_msg = "DvppResizeJpeg: resize resolution must be a vector of one or two elements, got: " + - std::to_string(resize_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (*min_element(resize_.begin(), resize_.end()) < 32 || *max_element(resize_.begin(), resize_.end()) > 2048) { - std::string err_msg = - "Dvpp module supports resize image with resolution in range [32, 2048], got resize Parameters: "; - if (resize_.size() == 2) { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << ", " << resize_[1] << "]"; - } else { - MS_LOG(ERROR) << err_msg << "[" << resize_[0] << ", " << resize_[0] << "]"; - } - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr DvppResizeJpegOperation::Build() { - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - uint32_t resizeHeight; - uint32_t resizeWidth; - // User specified the width value. - if (resize_.size() == 1) { - resizeHeight = resize_[0]; - resizeWidth = 0; - } else { - resizeHeight = resize_[0]; - resizeWidth = resize_[1]; - } - std::shared_ptr tensor_op = std::make_shared(resizeHeight, resizeWidth); - return tensor_op; -} - -Status DvppResizeJpegOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = resize_; - *out_json = args; - return Status::OK(); -} - -Status DvppResizeJpegOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kDvppResizeJpegOperation)); - std::vector resize = op_params["size"]; - *operation = std::make_shared(resize); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h deleted file mode 100644 index 55628936a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/ascend_vision_ir.h +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright 2021-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ASCEND_VISION_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ASCEND_VISION_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -// Transform operations for computer vision -namespace vision { -// Char arrays storing name of corresponding classes (in alphabetical order) -constexpr char kDvppCropJpegOperation[] = "DvppCropJpeg"; -constexpr char kDvppDecodeResizeOperation[] = "DvppDecodeResize"; -constexpr char kDvppDecodeResizeCropOperation[] = "DvppDecodeResizeCrop"; -constexpr char kDvppDecodeJpegOperation[] = "DvppDecodeJpeg"; -constexpr char kDvppDecodePngOperation[] = "DvppDecodePng"; -constexpr char kDvppDecodeVideoOperation[] = "DvppDecodeVideo"; -constexpr char kDvppNormalizeOperation[] = "DvppNormalize"; -constexpr char kDvppResizeJpegOperation[] = "DvppResizeJpeg"; - -/* ####################################### Derived TensorOperation classes ################################# */ - -class DvppCropJpegOperation : public TensorOperation { - public: - explicit DvppCropJpegOperation(const std::vector &resize); - - ~DvppCropJpegOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppCropJpegOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector crop_; -}; - -class DvppDecodeResizeOperation : public TensorOperation { - public: - explicit DvppDecodeResizeOperation(const std::vector &resize); - - ~DvppDecodeResizeOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppDecodeResizeOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector resize_; -}; - -class DvppDecodeResizeCropOperation : public TensorOperation { - public: - DvppDecodeResizeCropOperation(const std::vector &crop, const std::vector &resize); - - ~DvppDecodeResizeCropOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppDecodeResizeCropOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector crop_; - std::vector resize_; -}; - -class DvppDecodeJpegOperation : public TensorOperation { - public: - ~DvppDecodeJpegOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppDecodeJpegOperation; } - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; - -class DvppDecodeVideoOperation : public TensorOperation { - public: - DvppDecodeVideoOperation(const std::vector &size, VdecStreamFormat type, VdecOutputFormat out_format, - const std::string &output); - - ~DvppDecodeVideoOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppDecodeVideoOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - VdecOutputFormat format_; - VdecStreamFormat en_type_; - std::string output_; -}; - -class DvppDecodePngOperation : public TensorOperation { - public: - ~DvppDecodePngOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppDecodePngOperation; } - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; - -class DvppNormalizeOperation : public TensorOperation { - public: - DvppNormalizeOperation(const std::vector &mean, const std::vector &std); - - ~DvppNormalizeOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppNormalizeOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector mean_; - std::vector std_; -}; - -class DvppResizeJpegOperation : public TensorOperation { - public: - explicit DvppResizeJpegOperation(const std::vector &resize); - - ~DvppResizeJpegOperation() override = default; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kDvppResizeJpegOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector resize_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ASCEND_VISION_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.cc deleted file mode 100644 index c2f5f50e8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.cc +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AutoAugmentOperation -AutoAugmentOperation::AutoAugmentOperation(AutoAugmentPolicy policy, InterpolationMode interpolation, - const std::vector &fill_value) - : policy_(policy), interpolation_(interpolation), fill_value_(fill_value) {} - -AutoAugmentOperation::~AutoAugmentOperation() = default; - -std::string AutoAugmentOperation::Name() const { return kAutoAugmentOperation; } - -Status AutoAugmentOperation::ValidateParams() { - if (policy_ != AutoAugmentPolicy::kImageNet && policy_ != AutoAugmentPolicy::kCifar10 && - policy_ != AutoAugmentPolicy::kSVHN) { - std::string err_msg = "AutoAugment: Invalid AutoAugmentPolicy, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea) { - std::string err_msg = "AutoAugment: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateVectorFillvalue("AutoAugment", fill_value_)); - return Status::OK(); -} - -std::shared_ptr AutoAugmentOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(policy_, interpolation_, fill_value_); - return tensor_op; -} - -Status AutoAugmentOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["policy"] = policy_; - args["interpolation"] = interpolation_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status AutoAugmentOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "policy", kAutoAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kAutoAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kAutoAugmentOperation)); - AutoAugmentPolicy policy = op_params["policy"]; - InterpolationMode interpolation = op_params["interpolation"]; - std::vector fill_value = op_params["fill_value"]; - *operation = std::make_shared(policy, interpolation, fill_value); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h deleted file mode 100644 index 0b17ca523..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_augment_ir.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_AUGMENT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_AUGMENT_IR_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/kernels/image/auto_augment_op.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAutoAugmentOperation[] = "AutoAugment"; - -class AutoAugmentOperation : public TensorOperation { - public: - AutoAugmentOperation(AutoAugmentPolicy policy, InterpolationMode interpolation, - const std::vector &fill_value); - - ~AutoAugmentOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - AutoAugmentPolicy policy_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_AUGMENT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.cc deleted file mode 100644 index 1b3eade4a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.cc +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/auto_contrast_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_auto_contrast_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// AutoContrastOperation -AutoContrastOperation::AutoContrastOperation(float cutoff, const std::vector &ignore, - const std::string &device_target) - : cutoff_(cutoff), ignore_(ignore), device_target_(device_target) {} - -AutoContrastOperation::~AutoContrastOperation() = default; - -std::string AutoContrastOperation::Name() const { return kAutoContrastOperation; } - -Status AutoContrastOperation::ValidateParams() { - constexpr float kMaxCutOff = 100.0; - if (cutoff_ < 0.0 || cutoff_ > kMaxCutOff) { - std::string err_msg = "AutoContrast: 'cutoff' has to be between 0 and 100, got: " + std::to_string(cutoff_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - constexpr uint32_t kMaxIgnoreSize = 255; - for (uint32_t single_ignore : ignore_) { - if (single_ignore > kMaxIgnoreSize) { - std::string err_msg = - "AutoContrast: invalid size, 'ignore' has to be between 0 and 255, got: " + std::to_string(single_ignore); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "AutoContrast: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr AutoContrastOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(cutoff_, ignore_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::vector dvpp_cutoff = {cutoff_, cutoff_}; - std::shared_ptr dvpp_tensor_op = std::make_shared(dvpp_cutoff, ignore_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "AutoContrast: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status AutoContrastOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["cutoff"] = cutoff_; - args["ignore"] = ignore_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status AutoContrastOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "cutoff", kAutoContrastOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ignore", kAutoContrastOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kAutoContrastOperation)); - float cutoff = op_params["cutoff"]; - std::vector ignore = op_params["ignore"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(cutoff, ignore, device_target); - return Status::OK(); -} - -MapTargetDevice AutoContrastOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "AutoContrast: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h deleted file mode 100644 index 0301ba424..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/auto_contrast_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_CONTRAST_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_CONTRAST_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kAutoContrastOperation[] = "AutoContrast"; - -class AutoContrastOperation : public TensorOperation { - public: - AutoContrastOperation(float cutoff, const std::vector &ignore, const std::string &device_target = "CPU"); - - ~AutoContrastOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - float cutoff_; - std::vector ignore_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_AUTO_CONTRAST_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.cc deleted file mode 100644 index 44f96a57e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#include "mindspore-lite/minddata/dataset/kernels/image/bounding_box_augment_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -BoundingBoxAugmentOperation::BoundingBoxAugmentOperation(const std::shared_ptr &transform, float ratio) - : transform_(transform), ratio_(ratio) {} - -BoundingBoxAugmentOperation::~BoundingBoxAugmentOperation() = default; - -std::string BoundingBoxAugmentOperation::Name() const { return kBoundingBoxAugmentOperation; } - -Status BoundingBoxAugmentOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorTransforms("BoundingBoxAugment", {transform_})); - RETURN_IF_NOT_OK(ValidateScalar("BoundingBoxAugment", "ratio", ratio_, {0.0, 1.0}, false, false)); - return Status::OK(); -} - -std::shared_ptr BoundingBoxAugmentOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(transform_->Build(), ratio_); - return tensor_op; -} - -Status BoundingBoxAugmentOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args, transform_args; - nlohmann::json op_item; - RETURN_IF_NOT_OK(transform_->to_json(&transform_args)); - op_item["tensor_op_params"] = transform_args; - op_item["tensor_op_name"] = transform_->Name(); - args["transform"] = op_item; - args["ratio"] = ratio_; - *out_json = args; - return Status::OK(); -} - -Status BoundingBoxAugmentOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transform", kBoundingBoxAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ratio", kBoundingBoxAugmentOperation)); - std::vector> transforms; - std::vector json_operations = {}; - json_operations.push_back(op_params["transform"]); - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(json_operations, &transforms)); - float ratio = op_params["ratio"]; - CHECK_FAIL_RETURN_UNEXPECTED(transforms.size() == 1, - "Expect size one of transforms parameter, but got:" + std::to_string(transforms.size())); - *operation = std::make_shared(transforms[0], ratio); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h deleted file mode 100644 index dc01f7a1b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_BOUNDING_BOX_AUGMENT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_BOUNDING_BOX_AUGMENT_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kBoundingBoxAugmentOperation[] = "BoundingBoxAugment"; - -class BoundingBoxAugmentOperation : public TensorOperation { - public: - BoundingBoxAugmentOperation(const std::shared_ptr &transform, float ratio); - - ~BoundingBoxAugmentOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::shared_ptr transform_; - float ratio_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_BOUNDING_BOX_AUGMENT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.cc deleted file mode 100644 index 187f03ac6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.cc +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/center_crop_op.h" - -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -CenterCropOperation::CenterCropOperation(const std::vector &size) : size_(size) {} - -CenterCropOperation::~CenterCropOperation() = default; - -std::string CenterCropOperation::Name() const { return kCenterCropOperation; } - -Status CenterCropOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("CenterCrop", size_)); - return Status::OK(); -} - -std::shared_ptr CenterCropOperation::Build() { - int32_t crop_height = size_[0]; - int32_t crop_width = size_[0]; - - // User has specified crop_width. - constexpr size_t kSizeSize = 2; - if (size_.size() == kSizeSize) { - crop_width = size_[1]; - } - - std::shared_ptr tensor_op = std::make_shared(crop_height, crop_width); - return tensor_op; -} - -Status CenterCropOperation::to_json(nlohmann::json *out_json) { - (*out_json)["size"] = size_; - return Status::OK(); -} - -Status CenterCropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kCenterCropOperation)); - std::vector size = op_params["size"]; - *operation = std::make_shared(size); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h deleted file mode 100644 index 72c97c67b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/center_crop_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CENTER_CROP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CENTER_CROP_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/include/dataset/vision.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kCenterCropOperation[] = "CenterCrop"; - -class CenterCropOperation : public TensorOperation { - public: - explicit CenterCropOperation(const std::vector &size); - - ~CenterCropOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CENTER_CROP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.cc deleted file mode 100644 index 7d6cbed40..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.cc +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/convert_color_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_convert_color_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// ConvertColorOperation -ConvertColorOperation::ConvertColorOperation(ConvertMode convert_mode, const std::string &device_target) - : convert_mode_(convert_mode), device_target_(device_target) {} - -ConvertColorOperation::~ConvertColorOperation() = default; - -std::string ConvertColorOperation::Name() const { return kConvertColorOperation; } - -Status ConvertColorOperation::ValidateParams() { - if (convert_mode_ < ConvertMode::COLOR_BGR2BGRA || convert_mode_ > ConvertMode::COLOR_RGBA2GRAY) { - std::string err_msg = "ConvertColorOperation: convert_mode must be in ConvertMode."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "ConvertColor: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ConvertColorOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(convert_mode_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(convert_mode_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "ConvertColor: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status ConvertColorOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["convert_mode"] = convert_mode_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status ConvertColorOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "convert_mode", kConvertColorOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kConvertColorOperation)); - auto convert_mode = static_cast(op_params["convert_mode"]); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(convert_mode, device_target); - return Status::OK(); -} - -MapTargetDevice ConvertColorOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "ConvertColor: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h deleted file mode 100644 index 3761ad4fa..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/convert_color_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CONVERT_COLOR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CONVERT_COLOR_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kConvertColorOperation[] = "ConvertColor"; - -class ConvertColorOperation : public TensorOperation { - public: - explicit ConvertColorOperation(ConvertMode convert_mode, const std::string &device_target = "CPU"); - - ~ConvertColorOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - ConvertMode convert_mode_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CONVERT_COLOR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.cc deleted file mode 100644 index 6668f789d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.cc +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/crop_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_crop_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -CropOperation::CropOperation(const std::vector &coordinates, const std::vector &size, - const std::string &device_target) - : coordinates_(coordinates), size_(size), device_target_(device_target) {} - -CropOperation::~CropOperation() = default; - -std::string CropOperation::Name() const { return kCropOperation; } - -Status CropOperation::ValidateParams() { - // We have to limit crop size due to library restrictions, optimized to only iterate over size_ once - // we don't check the coordinates here because we don't have access to image dimensions - RETURN_IF_NOT_OK(ValidateVectorSize("Crop", size_)); - - constexpr size_t kSizeSize = 2; - if (coordinates_.size() != kSizeSize) { - std::string err_msg = "Crop: 'coordinates' must be a vector of two values."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateVectorNonNegative("Crop", "coordinates", coordinates_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Crop: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr CropOperation::Build() { - int32_t y, x, height, width; - - x = coordinates_[0]; - y = coordinates_[1]; - - height = size_[0]; - width = size_[0]; - // User has specified crop_width. - constexpr size_t size_two = 2; - if (size_.size() == size_two) { - width = size_[1]; - } - - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(y, x, height, width); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(y, x, height, width); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Crop: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status CropOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["coordinates"] = coordinates_; - (*out_json)["size"] = size_; - (*out_json)["device_target"] = device_target_; - return Status::OK(); -} - -Status CropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "coordinates", kCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kCropOperation)); - std::vector coordinates = op_params["coordinates"]; - std::vector size = op_params["size"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(coordinates, size, device_target); - return Status::OK(); -} - -MapTargetDevice CropOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Crop: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h deleted file mode 100644 index bd394bfec..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/crop_ir.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CROP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CROP_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kCropOperation[] = "Crop"; - -class CropOperation : public TensorOperation { - public: - CropOperation(const std::vector &coordinates, const std::vector &size, - const std::string &device_target = "CPU"); - - ~CropOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector coordinates_; - std::vector size_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CROP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.cc deleted file mode 100644 index 4e140eb78..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.cc +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/cutmix_batch_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// CutMixBatchOperation -CutMixBatchOperation::CutMixBatchOperation(ImageBatchFormat image_batch_format, float alpha, float prob) - : image_batch_format_(image_batch_format), alpha_(alpha), prob_(prob) {} - -CutMixBatchOperation::~CutMixBatchOperation() = default; - -std::string CutMixBatchOperation::Name() const { return kCutMixBatchOperation; } - -Status CutMixBatchOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateFloatScalarPositive("CutMixBatch", "alpha", alpha_)); - RETURN_IF_NOT_OK(ValidateProbability("CutMixBatch", prob_)); - if (image_batch_format_ != ImageBatchFormat::kNHWC && image_batch_format_ != ImageBatchFormat::kNCHW) { - std::string err_msg = "CutMixBatch: Invalid ImageBatchFormat, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr CutMixBatchOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(image_batch_format_, alpha_, prob_); - return tensor_op; -} - -Status CutMixBatchOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["image_batch_format"] = image_batch_format_; - args["alpha"] = alpha_; - args["prob"] = prob_; - *out_json = args; - return Status::OK(); -} - -Status CutMixBatchOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "image_batch_format", kCutMixBatchOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "alpha", kCutMixBatchOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kCutMixBatchOperation)); - auto image_batch = static_cast(op_params["image_batch_format"]); - float alpha = op_params["alpha"]; - float prob = op_params["prob"]; - *operation = std::make_shared(image_batch, alpha, prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h deleted file mode 100644 index fceb7be43..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTMIX_BATCH_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTMIX_BATCH_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kCutMixBatchOperation[] = "CutMixBatch"; - -class CutMixBatchOperation : public TensorOperation { - public: - CutMixBatchOperation(ImageBatchFormat image_batch_format, float alpha, float prob); - - ~CutMixBatchOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float alpha_; - float prob_; - ImageBatchFormat image_batch_format_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTMIX_BATCH_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.cc deleted file mode 100644 index e74de0736..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.cc +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h" - -#include -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/cut_out_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -CutOutOperation::CutOutOperation(int32_t length, int32_t num_patches, bool is_hwc) - : length_(length), num_patches_(num_patches), is_hwc_(is_hwc) {} - -CutOutOperation::~CutOutOperation() = default; - -std::string CutOutOperation::Name() const { return kCutOutOperation; } - -Status CutOutOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateIntScalarPositive("CutOut", "length", length_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("CutOut", "num_patches", num_patches_)); - return Status::OK(); -} - -std::shared_ptr CutOutOperation::Build() { - std::vector fill; - std::shared_ptr tensor_op = - std::make_shared(length_, length_, num_patches_, false, fill, is_hwc_); - return tensor_op; -} - -Status CutOutOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["length"] = length_; - args["num_patches"] = num_patches_; - args["is_hwc"] = is_hwc_; - *out_json = args; - return Status::OK(); -} - -Status CutOutOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "length", kCutOutOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_patches", kCutOutOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "is_hwc", kCutOutOperation)); - int32_t length = op_params["length"]; - int32_t num_patches = op_params["num_patches"]; - bool is_hwc = op_params["is_hwc"]; - *operation = std::make_shared(length, num_patches, is_hwc); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h deleted file mode 100644 index d0ba9d1f7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/cutout_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTOUT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTOUT_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kCutOutOperation[] = "CutOut"; - -class CutOutOperation : public TensorOperation { - public: - CutOutOperation(int32_t length, int32_t num_patches, bool is_hwc); - - ~CutOutOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t length_; - int32_t num_patches_; - bool is_hwc_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_CUTOUT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.cc deleted file mode 100644 index a4af222c6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.cc +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/decode_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_decode_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// DecodeOperation -DecodeOperation::DecodeOperation(bool rgb, const std::string &device_target) - : rgb_(rgb), device_target_(device_target) {} - -DecodeOperation::~DecodeOperation() = default; - -std::string DecodeOperation::Name() const { return kDecodeOperation; } - -Status DecodeOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Decode: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr DecodeOperation::Build() { - if (device_target_ == "CPU") { - return std::make_shared(rgb_); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(); -#endif - } else { - MS_LOG(ERROR) << "Decode: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status DecodeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["rgb"] = rgb_; - (*out_json)["device_target"] = device_target_; - return Status::OK(); -} - -Status DecodeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "rgb", kDecodeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kDecodeOperation)); - bool rgb = op_params["rgb"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(rgb, device_target); - return Status::OK(); -} - -MapTargetDevice DecodeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Decode: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h deleted file mode 100644 index a8cc61f34..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2020-2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kDecodeOperation[] = "Decode"; - -class DecodeOperation : public TensorOperation { - public: - explicit DecodeOperation(bool rgb, const std::string &device_target = "CPU"); - - ~DecodeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - bool rgb_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.cc deleted file mode 100644 index 2f700c03b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.cc +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/decode_video_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// DecodeVideoOperation -DecodeVideoOperation::DecodeVideoOperation() {} - -DecodeVideoOperation::~DecodeVideoOperation() = default; - -std::string DecodeVideoOperation::Name() const { return kDecodeVideoOperation; } - -Status DecodeVideoOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr DecodeVideoOperation::Build() { return std::make_shared(); } - -Status DecodeVideoOperation::to_json(nlohmann::json *out_json) { return Status::OK(); } - -Status DecodeVideoOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - *operation = std::make_shared(); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h deleted file mode 100644 index 567a02aaa..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/decode_video_ir.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_VIDEO_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_VIDEO_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kDecodeVideoOperation[] = "DecodeVideo"; - -class DecodeVideoOperation : public TensorOperation { - public: - DecodeVideoOperation(); - - ~DecodeVideoOperation(); - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_DECODE_VIDEO_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.cc deleted file mode 100644 index 935a4d8cb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.cc +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/equalize_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_equalize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// EqualizeOperation -EqualizeOperation::EqualizeOperation(const std::string &device_target) : device_target_(device_target) {} - -EqualizeOperation::~EqualizeOperation() = default; - -std::string EqualizeOperation::Name() const { return kEqualizeOperation; } - -Status EqualizeOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Equalize: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr EqualizeOperation::Build() { - if (device_target_ == "CPU") { - return std::make_shared(); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(); -#endif - } else { - MS_LOG(ERROR) << "Equalize: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status EqualizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status EqualizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kEqualizeOperation)); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(device_target); - return Status::OK(); -} - -MapTargetDevice EqualizeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Equalize: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h deleted file mode 100644 index 604772643..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/equalize_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_EQUALIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_EQUALIZE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kEqualizeOperation[] = "Equalize"; - -class EqualizeOperation : public TensorOperation { - public: - explicit EqualizeOperation(const std::string &device_target = "CPU"); - - ~EqualizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_EQUALIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.cc deleted file mode 100644 index 49c3f4fc6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.cc +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h" - -#include "mindspore-lite/minddata/dataset/audio/ir/validators.h" -#include "mindspore-lite/minddata/dataset/kernels/image/erase_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_erase_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// EraseOperation -EraseOperation::EraseOperation(int32_t top, int32_t left, int32_t height, int32_t width, - const std::vector &value, bool inplace, const std::string &device_target) - : top_(top), - left_(left), - height_(height), - width_(width), - value_(value), - inplace_(inplace), - device_target_(device_target) {} - -EraseOperation::~EraseOperation() = default; - -std::string EraseOperation::Name() const { return kEraseOperation; } - -Status EraseOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateIntScalarNonNegative("Erase", "top", top_)); - RETURN_IF_NOT_OK(ValidateIntScalarNonNegative("Erase", "left", left_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("Erase", "height", height_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("Erase", "width", width_)); - constexpr float kValueMax = 255.0; - constexpr float kValueMin = 0.; - const size_t kMaxFillValueSize = 3; - if (value_.empty() || (value_.size() != 1 && value_.size() != kMaxFillValueSize)) { - std::string err_msg = "Erase: value expecting size 1 or 3, got value.size(): " + std::to_string(value_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (float val : value_) { - if (val < kValueMin || val > kValueMax) { - std::string err_msg = "Erase: value has to be between 0. and 255., got:" + std::to_string(val); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Erase: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr EraseOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(top_, left_, height_, width_, value_, inplace_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::vector value_cast(value_.begin(), value_.end()); - std::shared_ptr dvpp_tensor_op = - std::make_shared(top_, left_, height_, width_, value_cast); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Erase: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status EraseOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["top"] = top_; - args["left"] = left_; - args["height"] = height_; - args["width"] = width_; - args["value"] = value_; - args["inplace"] = inplace_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status EraseOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "top", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "left", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "height", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "width", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "value", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "inplace", kEraseOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kEraseOperation)); - - int32_t top = op_params["top"]; - int32_t left = op_params["left"]; - int32_t height = op_params["height"]; - int32_t width = op_params["width"]; - std::vector value = op_params["value"]; - bool inplace = op_params["inplace"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(top, left, height, width, value, inplace, device_target); - return Status::OK(); -} - -MapTargetDevice EraseOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Erase: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h deleted file mode 100644 index c4571cb30..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/erase_ir.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ERASE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ERASE_IR_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kEraseOperation[] = "Erase"; - -class EraseOperation : public TensorOperation { - public: - EraseOperation(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &value, - bool inplace, const std::string &device_target = "CPU"); - - ~EraseOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - std::vector value_; - bool inplace_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ERASE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.cc deleted file mode 100644 index 40976a56c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.cc +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/gaussian_blur_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_gaussian_blur_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr int sigma_size = 2; - -GaussianBlurOperation::GaussianBlurOperation(const std::vector &kernel_size, const std::vector &sigma, - const std::string &device_target) - : kernel_size_(kernel_size), sigma_(sigma), device_target_(device_target) {} - -GaussianBlurOperation::~GaussianBlurOperation() = default; - -std::string GaussianBlurOperation::Name() const { return kGaussianBlurOperation; } - -Status GaussianBlurOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("GaussianBlur", kernel_size_)); - RETURN_IF_NOT_OK(ValidateVectorOdd("GaussianBlur", "kernel_size", kernel_size_)); - RETURN_IF_NOT_OK(ValidateVectorSigma("GaussianBlur", sigma_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "GaussianBlur: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr GaussianBlurOperation::Build() { - int32_t kernel_x = kernel_size_[0]; - int32_t kernel_y = kernel_size_[0]; - // User has specified kernel_y. - if (kernel_size_.size() == 2) { - kernel_y = kernel_size_[1]; - } - - float sigma_x = sigma_[0] <= 0.0 ? static_cast(kernel_x) * 0.15F + 0.35F : sigma_[0]; - float sigma_y = sigma_x; - - // User has specified sigma_y. - if (sigma_.size() == sigma_size) { - sigma_y = sigma_[1] <= 0.0 ? static_cast(kernel_y) * 0.15F + 0.35F : sigma_[1]; - } - - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(kernel_x, kernel_y, sigma_x, sigma_y); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = - std::make_shared(kernel_x, kernel_y, sigma_x, sigma_y); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "GaussianBlur: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status GaussianBlurOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["kernel_size"] = kernel_size_; - args["sigma"] = sigma_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status GaussianBlurOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "kernel_size", kGaussianBlurOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "sigma", kGaussianBlurOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kGaussianBlurOperation)); - std::vector kernel_size = op_params["kernel_size"]; - std::vector sigma = op_params["sigma"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(kernel_size, sigma, device_target); - return Status::OK(); -} - -MapTargetDevice GaussianBlurOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "GaussianBlur: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h deleted file mode 100644 index ce1930e95..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_GAUSSIAN_BLUR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_GAUSSIAN_BLUR_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kGaussianBlurOperation[] = "GaussianBlur"; - -class GaussianBlurOperation : public TensorOperation { - public: - GaussianBlurOperation(const std::vector &kernel_size, const std::vector &sigma, - const std::string &device_target = "CPU"); - - ~GaussianBlurOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector kernel_size_; - std::vector sigma_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_GAUSSIAN_BLUR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.cc deleted file mode 100644 index 65a2542eb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/horizontal_flip_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_horizontal_flip_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// VerticalFlipOperation -HorizontalFlipOperation::HorizontalFlipOperation(const std::string &device_target) : device_target_(device_target) {} - -HorizontalFlipOperation::~HorizontalFlipOperation() = default; - -std::string HorizontalFlipOperation::Name() const { return kHorizontalFlipOperation; } - -Status HorizontalFlipOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "HorizontalFlip: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr HorizontalFlipOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "HorizontalFlip: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status HorizontalFlipOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status HorizontalFlipOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kHorizontalFlipOperation)); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(device_target); - return Status::OK(); -} - -MapTargetDevice HorizontalFlipOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "HorizontalFlip: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h deleted file mode 100644 index f1591b8ea..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HORIZONTAL_FLIP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HORIZONTAL_FLIP_IR_H_ - -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kHorizontalFlipOperation[] = "HorizontalFlip"; - -class HorizontalFlipOperation : public TensorOperation { - public: - explicit HorizontalFlipOperation(const std::string &device_target = "CPU"); - - ~HorizontalFlipOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HORIZONTAL_FLIP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.cc deleted file mode 100644 index 3503fe752..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.cc +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/hwc_to_chw_op.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// HwcToChwOperation -HwcToChwOperation::HwcToChwOperation() : TensorOperation() {} - -HwcToChwOperation::~HwcToChwOperation() = default; - -std::string HwcToChwOperation::Name() const { return kHwcToChwOperation; } - -Status HwcToChwOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr HwcToChwOperation::Build() { return std::make_shared(); } - -Status HwcToChwOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h deleted file mode 100644 index 237e0aee7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HWC_TO_CHW_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HWC_TO_CHW_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kHwcToChwOperation[] = "HwcToChw"; - -class HwcToChwOperation : public TensorOperation { - public: - HwcToChwOperation(); - - ~HwcToChwOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_HWC_TO_CHW_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.cc deleted file mode 100644 index fd5f8cfa9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.cc +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/invert_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_invert_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// InvertOperation -InvertOperation::InvertOperation(const std::string &device_target) : device_target_(device_target) {} - -InvertOperation::~InvertOperation() = default; - -std::string InvertOperation::Name() const { return kInvertOperation; } - -Status InvertOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Invert: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr InvertOperation::Build() { - if (device_target_ == "CPU") { - return std::make_shared(); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Invert: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status InvertOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["device_target"] = device_target_; - return Status::OK(); -} - -Status InvertOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kInvertOperation)); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(device_target); - return Status::OK(); -} - -MapTargetDevice InvertOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Invert: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h deleted file mode 100644 index 95f6f48a4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/invert_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_INVERT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_INVERT_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kInvertOperation[] = "Invert"; - -class InvertOperation : public TensorOperation { - public: - explicit InvertOperation(const std::string &device_target = "CPU"); - - ~InvertOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_INVERT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.cc deleted file mode 100644 index b2adb3559..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.cc +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/mixup_batch_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// MixUpOperation -MixUpBatchOperation::MixUpBatchOperation(float alpha) : alpha_(alpha) {} - -MixUpBatchOperation::~MixUpBatchOperation() = default; - -std::string MixUpBatchOperation::Name() const { return kMixUpBatchOperation; } - -Status MixUpBatchOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateFloatScalarPositive("MixUpBatch", "alpha", alpha_)); - return Status::OK(); -} - -std::shared_ptr MixUpBatchOperation::Build() { return std::make_shared(alpha_); } - -Status MixUpBatchOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["alpha"] = alpha_; - return Status::OK(); -} - -Status MixUpBatchOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "alpha", kMixUpBatchOperation)); - float alpha = op_params["alpha"]; - *operation = std::make_shared(alpha); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h deleted file mode 100644 index afb00777d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/mixup_batch_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_MIXUP_BATCH_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_MIXUP_BATCH_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kMixUpBatchOperation[] = "MixUpBatch"; - -class MixUpBatchOperation : public TensorOperation { - public: - explicit MixUpBatchOperation(float alpha); - - ~MixUpBatchOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float alpha_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_MIXUP_BATCH_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.cc deleted file mode 100644 index 6793e22f5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.cc +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/normalize_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_normalize_v2_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// NormalizeOperation -NormalizeOperation::NormalizeOperation(const std::vector &mean, const std::vector &std, bool is_hwc, - const std::string &device_target) - : mean_(mean), std_(std), is_hwc_(is_hwc), device_target_(device_target) {} - -NormalizeOperation::~NormalizeOperation() = default; - -std::string NormalizeOperation::Name() const { return kNormalizeOperation; } - -Status NormalizeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorMeanStd("Normalize", mean_, std_)); - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Normalize: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr NormalizeOperation::Build() { - if (device_target_ == "CPU") { - return std::make_shared(mean_, std_, is_hwc_); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(mean_, std_, is_hwc_); -#endif - } else { - MS_LOG(ERROR) << "Normalize: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status NormalizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["mean"] = mean_; - args["std"] = std_; - args["is_hwc"] = is_hwc_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status NormalizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "mean", kNormalizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "std", kNormalizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "is_hwc", kNormalizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kNormalizeOperation)); - std::vector mean = op_params["mean"]; - std::vector std = op_params["std"]; - bool is_hwc = op_params["is_hwc"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(mean, std, is_hwc, device_target); - return Status::OK(); -} - -MapTargetDevice NormalizeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Normalize: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h deleted file mode 100644 index 7b01db6f9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_ir.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kNormalizeOperation[] = "Normalize"; - -class NormalizeOperation : public TensorOperation { - public: - NormalizeOperation(const std::vector &mean, const std::vector &std, bool is_hwc, - const std::string &device_target = "CPU"); - - ~NormalizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector mean_; - std::vector std_; - bool is_hwc_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.cc deleted file mode 100644 index 835e5228e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.cc +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/normalize_pad_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// NormalizePadOperation -NormalizePadOperation::NormalizePadOperation(const std::vector &mean, const std::vector &std, - const std::string &dtype, bool is_hwc) - : mean_(mean), std_(std), dtype_(dtype), is_hwc_(is_hwc) {} - -NormalizePadOperation::~NormalizePadOperation() = default; - -std::string NormalizePadOperation::Name() const { return kNormalizePadOperation; } - -Status NormalizePadOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorMeanStd("NormalizePad", mean_, std_)); - if (dtype_ != "float32" && dtype_ != "float16") { - std::string err_msg = "NormalizePad: dtype must be float32 or float16, but got: " + dtype_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr NormalizePadOperation::Build() { - return std::make_shared(mean_, std_, dtype_, is_hwc_); -} - -Status NormalizePadOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["mean"] = mean_; - args["std"] = std_; - args["dtype"] = dtype_; - args["is_hwc"] = is_hwc_; - *out_json = args; - return Status::OK(); -} - -Status NormalizePadOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "mean", kNormalizePadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "std", kNormalizePadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "dtype", kNormalizePadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "is_hwc", kNormalizePadOperation)); - std::vector mean = op_params["mean"]; - std::vector std = op_params["std"]; - std::string dtype = op_params["dtype"]; - bool is_hwc = op_params["is_hwc"]; - *operation = std::make_shared(mean, std, dtype, is_hwc); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h deleted file mode 100644 index 76bd0598f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/normalize_pad_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_PAD_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_PAD_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kNormalizePadOperation[] = "NormalizePad"; - -class NormalizePadOperation : public TensorOperation { - public: - NormalizePadOperation(const std::vector &mean, const std::vector &std, const std::string &dtype, - bool is_hwc); - - ~NormalizePadOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector mean_; - std::vector std_; - std::string dtype_; - bool is_hwc_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_NORMALIZE_PAD_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.cc deleted file mode 100644 index bf244c42b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.cc +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h" - -#include - -#if !defined(ENABLE_ANDROID) -#include "mindspore-lite/minddata/dataset/kernels/image/pad_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_pad_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#if !defined(ENABLE_ANDROID) -// PadOperation -PadOperation::PadOperation(const std::vector &padding, const std::vector &fill_value, - BorderType padding_mode, const std::string &device_target) - : padding_(padding), fill_value_(fill_value), padding_mode_(padding_mode), device_target_(device_target) {} - -PadOperation::~PadOperation() = default; - -std::string PadOperation::Name() const { return kPadOperation; } - -Status PadOperation::ValidateParams() { - // padding - RETURN_IF_NOT_OK(ValidateVectorPadding("Pad", padding_)); - // fill_value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("Pad", fill_value_)); - // padding_mode - if (padding_mode_ != BorderType::kConstant && padding_mode_ != BorderType::kEdge && - padding_mode_ != BorderType::kReflect && padding_mode_ != BorderType::kSymmetric) { - std::string err_msg = "Pad: Invalid BorderType, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Pad: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr PadOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t dimension_two = 2; - constexpr size_t dimension_three = 3; - constexpr size_t size_one = 1; - constexpr size_t size_two = 2; - constexpr size_t size_three = 3; - int32_t pad_top, pad_bottom, pad_left, pad_right; - switch (padding_.size()) { - case size_one: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_bottom = padding_[dimension_zero]; - break; - case size_two: - pad_left = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_bottom = padding_[dimension_one]; - break; - default: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_right = padding_[dimension_two]; - pad_bottom = padding_[dimension_three]; - } - uint8_t fill_r, fill_g, fill_b; - - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_zero]; - fill_b = fill_value_[dimension_zero]; - - if (fill_value_.size() == size_three) { - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_one]; - fill_b = fill_value_[dimension_two]; - } - - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = - std::make_shared(pad_top, pad_bottom, pad_left, pad_right, padding_mode_, fill_r, fill_g, fill_b); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = - std::make_shared(pad_top, pad_bottom, pad_left, pad_right, padding_mode_, fill_r, fill_g, fill_b); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Pad: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status PadOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["padding"] = padding_; - args["fill_value"] = fill_value_; - args["padding_mode"] = padding_mode_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status PadOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding", kPadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kPadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding_mode", kPadOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kPadOperation)); - std::vector padding = op_params["padding"]; - std::vector fill_value = op_params["fill_value"]; - auto padding_mode = static_cast(op_params["padding_mode"]); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(padding, fill_value, padding_mode, device_target); - return Status::OK(); -} - -MapTargetDevice PadOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Pad: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h deleted file mode 100644 index 86bb415bb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_ir.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kPadOperation[] = "Pad"; - -class PadOperation : public TensorOperation { - public: - PadOperation(const std::vector &padding, const std::vector &fill_value, BorderType padding_mode, - const std::string &device_target = "CPU"); - - ~PadOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector padding_; - std::vector fill_value_; - BorderType padding_mode_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.cc deleted file mode 100644 index beb5c2bf5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/pad_to_size_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// PadOperation -PadToSizeOperation::PadToSizeOperation(const std::vector &size, const std::vector &offset, - const std::vector &fill_value, BorderType padding_mode) - : size_(size), offset_(offset), fill_value_(fill_value), padding_mode_(padding_mode) {} - -PadToSizeOperation::~PadToSizeOperation() = default; - -std::string PadToSizeOperation::Name() const { return kPadToSizeOperation; } - -Status PadToSizeOperation::ValidateParams() { - constexpr size_t kTargetSize = 2; - CHECK_FAIL_RETURN_SYNTAX_ERROR( - !size_.empty() && size_.size() <= kTargetSize, - "PadToSize: size should be in length of 1 or 2, but got: " + std::to_string(size_.size())); - RETURN_IF_NOT_OK(ValidateVectorPositive("PadToSize", "size", size_)); - CHECK_FAIL_RETURN_SYNTAX_ERROR( - offset_.size() <= kTargetSize, - "PadToSize: offset should be empty or in length of 1 or 2, but got: " + std::to_string(offset_.size())); - RETURN_IF_NOT_OK(ValidateVectorNonNegative("PadToSize", "offset", offset_)); - RETURN_IF_NOT_OK(ValidateVectorFillvalue("PadToSize", fill_value_)); - if (padding_mode_ != BorderType::kConstant && padding_mode_ != BorderType::kEdge && - padding_mode_ != BorderType::kReflect && padding_mode_ != BorderType::kSymmetric) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("PadToSize: invalid BorderType, check input value of enum."); - } - return Status::OK(); -} - -std::shared_ptr PadToSizeOperation::Build() { - // If fill_value size is 1, use it to fill all R, G, B channels. - if (fill_value_.size() == 1) { - fill_value_.push_back(fill_value_[0]); - fill_value_.push_back(fill_value_[0]); - } - return std::make_shared(size_, offset_, fill_value_, padding_mode_); -} - -Status PadToSizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["offset"] = offset_; - args["fill_value"] = fill_value_; - args["padding_mode"] = padding_mode_; - *out_json = args; - return Status::OK(); -} - -Status PadToSizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kPadToSizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "offset", kPadToSizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kPadToSizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding_mode", kPadToSizeOperation)); - std::vector size = op_params["size"]; - std::vector offset = op_params["offset"]; - std::vector fill_value = op_params["fill_value"]; - auto padding_mode = static_cast(op_params["padding_mode"]); - *operation = std::make_shared(size, offset, fill_value, padding_mode); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h deleted file mode 100644 index 19ffc946f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/pad_to_size_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_TO_SIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_TO_SIZE_IR_H_ - -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kPadToSizeOperation[] = "PadToSize"; - -class PadToSizeOperation : public TensorOperation { - public: - PadToSizeOperation(const std::vector &size, const std::vector &offset, - const std::vector &fill_value, BorderType padding_mode); - - ~PadToSizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - std::vector offset_; - std::vector fill_value_; - BorderType padding_mode_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PAD_TO_SIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.cc deleted file mode 100644 index 15e0e3428..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.cc +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/perspective_op.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_perspective_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -PerspectiveOperation::PerspectiveOperation(const std::vector> &start_points, - const std::vector> &end_points, - InterpolationMode interpolation, const std::string &device_target) - : start_points_(start_points), - end_points_(end_points), - interpolation_(interpolation), - device_target_(device_target) {} - -PerspectiveOperation::~PerspectiveOperation() = default; - -Status PerspectiveOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Perspective: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "Perspective: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - const size_t kListSize = 4; - const size_t kPointSize = 2; - CHECK_FAIL_RETURN_SYNTAX_ERROR( - start_points_.size() == kListSize, - "Perspective: start_points should be in length of 4, but got: " + std::to_string(start_points_.size())); - - CHECK_FAIL_RETURN_SYNTAX_ERROR( - end_points_.size() == kListSize, - "Perspective: end_points should be in length of 4, but got: " + std::to_string(end_points_.size())); - - for (int i = 0; i < kListSize; i++) { - if (start_points_[i].size() != kPointSize) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Perspective: each element in start_points should be in length of 2."); - } - if (end_points_[i].size() != kPointSize) { - LOG_AND_RETURN_STATUS_SYNTAX_ERROR("Perspective: each element in end_points should be in length of 2."); - } - } - - return Status::OK(); -} - -std::shared_ptr PerspectiveOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = - std::make_shared(start_points_, end_points_, interpolation_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = - std::make_shared(start_points_, end_points_, interpolation_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Perspective: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} // namespace vision - -Status PerspectiveOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["start_points"] = start_points_; - args["end_points"] = end_points_; - args["interpolation"] = interpolation_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status PerspectiveOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "start_points", kPerspectiveOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "end_points", kPerspectiveOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kPerspectiveOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kPerspectiveOperation)); - std::vector> start_points = op_params["start_points"]; - std::vector> end_points = op_params["end_points"]; - auto interpolation = static_cast(op_params["interpolation"]); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(start_points, end_points, interpolation, device_target); - return Status::OK(); -} - -MapTargetDevice PerspectiveOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Perspective: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h deleted file mode 100644 index c32bff05f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/perspective_ir.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PERSPECTIVE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PERSPECTIVE_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kPerspectiveOperation[] = "Perspective"; - -class PerspectiveOperation : public TensorOperation { - public: - PerspectiveOperation(const std::vector> &start_points, - const std::vector> &end_points, InterpolationMode interpolation, - const std::string &device_target = "CPU"); - - ~PerspectiveOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kPerspectiveOperation; } - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector> start_points_; - std::vector> end_points_; - InterpolationMode interpolation_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_PERSPECTIVE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.cc deleted file mode 100644 index d008bf516..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.cc +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/posterize_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_posterize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// PosterizeOperation -PosterizeOperation::PosterizeOperation(uint8_t bits, const std::string &device_target) - : bits_(bits), device_target_(device_target) {} - -PosterizeOperation::~PosterizeOperation() = default; - -Status PosterizeOperation::ValidateParams() { - constexpr uint8_t kMinimumBitValue = 0; - constexpr uint8_t kMaximumBitValue = 8; - - if (bits_ < kMinimumBitValue || bits_ > kMaximumBitValue) { - std::string err_msg = "Posterize: bits is out of range [0, 8], got: " + std::to_string(bits_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Posterize: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - return Status::OK(); -} - -std::shared_ptr PosterizeOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(bits_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(bits_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Posterize: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status PosterizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["bits"] = bits_; - (*out_json)["device_target"] = device_target_; - return Status::OK(); -} - -Status PosterizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "bits", kPosterizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kPosterizeOperation)); - uint8_t bits_ = op_params["bits"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(bits_, device_target); - return Status::OK(); -} - -MapTargetDevice PosterizeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Posterize: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h deleted file mode 100644 index 728bafb1e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/posterize_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_POSTERIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_POSTERIZE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kPosterizeOperation[] = "Posterize"; - -class PosterizeOperation : public TensorOperation { - public: - explicit PosterizeOperation(uint8_t bits, const std::string &device_target = "CPU"); - - ~PosterizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kPosterizeOperation; }; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - uint8_t bits_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_POSTERIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.cc deleted file mode 100644 index 8a5f170ee..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.cc +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandAugmentOperation -RandAugmentOperation::RandAugmentOperation(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, - InterpolationMode interpolation, const std::vector &fill_value) - : num_ops_(num_ops), - magnitude_(magnitude), - num_magnitude_bins_(num_magnitude_bins), - interpolation_(interpolation), - fill_value_(fill_value) {} - -RandAugmentOperation::~RandAugmentOperation() = default; - -std::shared_ptr RandAugmentOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(num_ops_, magnitude_, num_magnitude_bins_, interpolation_, fill_value_); - return tensor_op; -} - -Status RandAugmentOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("RandAugment", "num_ops", num_ops_)); - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("RandAugment", "magnitude", magnitude_)); - RETURN_IF_NOT_OK(ValidateScalar("RandAugment", "num_magnitude_bins", num_magnitude_bins_, {1}, true)); - CHECK_FAIL_RETURN_UNEXPECTED(magnitude_ < num_magnitude_bins_, - "RandAugment: magnitude should be smaller than num_magnitude_bins."); - - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea) { - std::string err_msg = - "RandAugment: Invalid InterpolationMode. Use InterpolationMode::kLinear, InterpolationMode::kNearestNeighbour," - " InterpolationMode::kCubic or InterpolationMode::kArea."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandAugment", fill_value_)); - return Status::OK(); -} - -std::string RandAugmentOperation::Name() const { return kRandAugmentOperation; } - -Status RandAugmentOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_ops"] = num_ops_; - args["magnitude"] = magnitude_; - args["num_magnitude_bins"] = num_magnitude_bins_; - args["interpolation"] = interpolation_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status RandAugmentOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_ops", kRandAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "magnitude", kRandAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_magnitude_bins", kRandAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kRandAugmentOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRandAugmentOperation)); - int32_t num_ops = op_params["num_ops"]; - int32_t magnitude = op_params["magnitude"]; - int32_t num_magnitude_bins = op_params["num_magnitude_bins"]; - InterpolationMode interpolation = op_params["interpolation"]; - std::vector fill_value = op_params["fill_value"]; - *operation = - std::make_shared(num_ops, magnitude, num_magnitude_bins, interpolation, fill_value); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h deleted file mode 100644 index 116755b7c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rand_augment_ir.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RAND_AUGMENT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RAND_AUGMENT_IR_H_ - -#include -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/kernels/image/rand_augment_op.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandAugmentOperation[] = "RandAugment"; - -class RandAugmentOperation : public TensorOperation { - public: - RandAugmentOperation(int32_t num_ops, int32_t magnitude, int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value); - - ~RandAugmentOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t num_ops_; - int32_t magnitude_; - int32_t num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RAND_AUGMENT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.cc deleted file mode 100644 index 7aab0c3a2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.cc +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_adjust_sharpness_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomAdjustSharpnessOperation -RandomAdjustSharpnessOperation::RandomAdjustSharpnessOperation(float degree, float prob) - : degree_(degree), probability_(prob) {} - -RandomAdjustSharpnessOperation::~RandomAdjustSharpnessOperation() = default; - -std::string RandomAdjustSharpnessOperation::Name() const { return kRandomAdjustSharpnessOperation; } - -Status RandomAdjustSharpnessOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("RandomAdjustSharpness", "degree", degree_)); - RETURN_IF_NOT_OK(ValidateProbability("RandomAdjustSharpness", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomAdjustSharpnessOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(degree_, probability_); - return tensor_op; -} - -Status RandomAdjustSharpnessOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["degree"] = degree_; - args["prob"] = probability_; - *out_json = args; - return Status::OK(); -} - -Status RandomAdjustSharpnessOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degree", kRandomAdjustSharpnessOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomAdjustSharpnessOperation)); - float degree = op_params["degree"]; - float prob = op_params["prob"]; - *operation = std::make_shared(degree, prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h deleted file mode 100644 index 8813d1b67..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ADJUST_SHARPNESS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ADJUST_SHARPNESS_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomAdjustSharpnessOperation[] = "RandomAdjustSharpness"; - -class RandomAdjustSharpnessOperation : public TensorOperation { - public: - RandomAdjustSharpnessOperation(float degree, float prob); - - ~RandomAdjustSharpnessOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float degree_; - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ADJUST_SHARPNESS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.cc deleted file mode 100644 index 5ea27d744..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.cc +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/random_affine_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr size_t dimension_zero = 0; -constexpr size_t dimension_one = 1; -constexpr size_t dimension_two = 2; -constexpr size_t dimension_three = 3; -constexpr size_t size_two = 2; -constexpr size_t size_three = 3; -constexpr size_t size_four = 4; - -// RandomAffineOperation -RandomAffineOperation::RandomAffineOperation(const std::vector °rees, - const std::vector &translate_range, - const std::vector &scale_range, - const std::vector &shear_ranges, InterpolationMode interpolation, - const std::vector &fill_value) - : degrees_(degrees), - translate_range_(translate_range), - scale_range_(scale_range), - shear_ranges_(shear_ranges), - interpolation_(interpolation), - fill_value_(fill_value) { - random_op_ = true; -} - -RandomAffineOperation::~RandomAffineOperation() = default; - -std::string RandomAffineOperation::Name() const { return kRandomAffineOperation; } - -Status RandomAffineOperation::ValidateParams() { - // Degrees - if (degrees_.size() != size_two) { - std::string err_msg = - "RandomAffine: degrees expecting size 2, got: degrees.size() = " + std::to_string(degrees_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (degrees_[dimension_zero] > degrees_[dimension_one]) { - std::string err_msg = "RandomAffine: minimum of degrees range is greater than maximum: min = " + - std::to_string(degrees_[dimension_zero]) + - ", max = " + std::to_string(degrees_[dimension_one]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // Translate - if (translate_range_.size() != size_two && translate_range_.size() != size_four) { - std::string err_msg = "RandomAffine: 'translate'(translate_range) expecting size 2 or 4, got size: " + - std::to_string(translate_range_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (translate_range_[dimension_zero] > translate_range_[dimension_one]) { - std::string err_msg = "RandomAffine: minimum of 'translate'(translate_range) on x is greater than maximum: min = " + - std::to_string(translate_range_[dimension_zero]) + - ", max = " + std::to_string(translate_range_[dimension_one]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK( - ValidateScalar("RandomAffine", "translate", translate_range_[dimension_zero], {-1, 1}, false, false)); - RETURN_IF_NOT_OK(ValidateScalar("RandomAffine", "translate", translate_range_[dimension_one], {-1, 1}, false, false)); - if (translate_range_.size() == size_four) { - if (translate_range_[dimension_two] > translate_range_[dimension_three]) { - std::string err_msg = - "RandomAffine: minimum of 'translate'(translate range) on y is greater than maximum: min = " + - std::to_string(translate_range_[dimension_two]) + - ", max = " + std::to_string(translate_range_[dimension_three]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK( - ValidateScalar("RandomAffine", "translate", translate_range_[dimension_two], {-1, 1}, false, false)); - RETURN_IF_NOT_OK( - ValidateScalar("RandomAffine", "translate", translate_range_[dimension_three], {-1, 1}, false, false)); - } - // Scale - RETURN_IF_NOT_OK(ValidateVectorScale("RandomAffine", scale_range_)); - // Shear - if (shear_ranges_.size() != size_two && shear_ranges_.size() != size_four) { - std::string err_msg = - "RandomAffine: 'shear'(shear_range) expecting size 2 or 4, got size:" + std::to_string(shear_ranges_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (shear_ranges_[dimension_zero] > shear_ranges_[dimension_one]) { - std::string err_msg = "RandomAffine: minimum of horizontal 'shear'(shear_range) is greater than maximum: min = " + - std::to_string(shear_ranges_[dimension_zero]) + - ", max = " + std::to_string(shear_ranges_[dimension_one]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (shear_ranges_.size() == size_four && shear_ranges_[dimension_two] > shear_ranges_[dimension_three]) { - std::string err_msg = "RandomAffine: minimum of vertical 'shear'(shear_range) is greater than maximum: min = " + - std::to_string(shear_ranges_[dimension_two]) + - ", max = " + std::to_string(scale_range_[dimension_three]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // Fill Value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomAffine", fill_value_)); - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea) { - std::string err_msg = "RandomAffine: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomAffineOperation::Build() { - if (shear_ranges_.size() == size_two) { - shear_ranges_.resize(size_four); - } - if (translate_range_.size() == size_two) { - translate_range_.resize(size_four); - } - std::vector fill_value = {fill_value_[dimension_zero], fill_value_[dimension_zero], - fill_value_[dimension_zero]}; - if (fill_value_.size() == size_three) { - fill_value[dimension_one] = fill_value_[dimension_one]; - fill_value[dimension_two] = fill_value_[dimension_two]; - } - - auto tensor_op = std::make_shared(degrees_, translate_range_, scale_range_, shear_ranges_, - interpolation_, fill_value); - return tensor_op; -} - -Status RandomAffineOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["degrees"] = degrees_; - args["translate"] = translate_range_; - args["scale"] = scale_range_; - args["shear"] = shear_ranges_; - args["resample"] = interpolation_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status RandomAffineOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degrees", kRandomAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "translate", kRandomAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "scale", kRandomAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "shear", kRandomAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "resample", kRandomAffineOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRandomAffineOperation)); - std::vector degrees = op_params["degrees"]; - std::vector translate_range = op_params["translate"]; - std::vector scale_range = op_params["scale"]; - std::vector shear_ranges = op_params["shear"]; - auto interpolation = static_cast(op_params["resample"]); - std::vector fill_value = op_params["fill_value"]; - *operation = std::make_shared(degrees, translate_range, scale_range, shear_ranges, - interpolation, fill_value); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h deleted file mode 100644 index 08f398b4e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_affine_ir.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AFFINE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AFFINE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomAffineOperation[] = "RandomAffine"; - -class RandomAffineOperation : public TensorOperation { - public: - RandomAffineOperation(const std::vector °rees, const std::vector &translate_range, - const std::vector &scale_range, const std::vector &shear_ranges, - InterpolationMode interpolation, const std::vector &fill_value); - - ~RandomAffineOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector degrees_; // min_degree, max_degree - std::vector translate_range_; // maximum x translation percentage, maximum y translation percentage - std::vector scale_range_; // min_scale, max_scale - std::vector shear_ranges_; // min_x_shear, max_x_shear, min_y_shear, max_y_shear - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AFFINE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.cc deleted file mode 100644 index 470db715a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_auto_contrast_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomAutoContrastOperation -RandomAutoContrastOperation::RandomAutoContrastOperation(float cutoff, const std::vector &ignore, float prob) - : cutoff_(cutoff), ignore_(ignore), probability_(prob) {} - -RandomAutoContrastOperation::~RandomAutoContrastOperation() = default; - -std::string RandomAutoContrastOperation::Name() const { return kRandomAutoContrastOperation; } - -Status RandomAutoContrastOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateScalar("RandomAutoContrast", "cutoff", cutoff_, {0, 50}, false, true)); - - for (auto i = 0; i < ignore_.size(); i++) { - RETURN_IF_NOT_OK(ValidateScalar("RandomAutoContrast", "ignore[" + std::to_string(i) + "]", ignore_[i], {0, 255})); - } - - RETURN_IF_NOT_OK(ValidateProbability("RandomAutoContrast", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomAutoContrastOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(cutoff_, ignore_, probability_); - return tensor_op; -} - -Status RandomAutoContrastOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["cutoff"] = cutoff_; - args["ignore"] = ignore_; - args["prob"] = probability_; - *out_json = args; - return Status::OK(); -} - -Status RandomAutoContrastOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "cutoff", kRandomAutoContrastOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ignore", kRandomAutoContrastOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomAutoContrastOperation)); - float cutoff = op_params["cutoff"]; - std::vector ignore = op_params["ignore"]; - float prob = op_params["prob"]; - *operation = std::make_shared(cutoff, ignore, prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h deleted file mode 100644 index 636c8d84e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AUTO_CONTRAST_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AUTO_CONTRAST_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomAutoContrastOperation[] = "RandomAutoContrast"; - -class RandomAutoContrastOperation : public TensorOperation { - public: - RandomAutoContrastOperation(float cutoff, const std::vector &ignore, float prob); - - ~RandomAutoContrastOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float cutoff_; - std::vector ignore_; - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_AUTO_CONTRAST_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.cc deleted file mode 100644 index 5e04af69a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.cc +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_color_adjust_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr size_t dimension_zero = 0; -constexpr size_t dimension_one = 1; -constexpr size_t size_two = 2; - -#ifndef ENABLE_ANDROID -// RandomColorAdjustOperation. -RandomColorAdjustOperation::RandomColorAdjustOperation(const std::vector &brightness, - const std::vector &contrast, - const std::vector &saturation, - const std::vector &hue) - : brightness_(brightness), contrast_(contrast), saturation_(saturation), hue_(hue) { - random_op_ = true; -} - -RandomColorAdjustOperation::~RandomColorAdjustOperation() = default; - -std::string RandomColorAdjustOperation::Name() const { return kRandomColorAdjustOperation; } - -Status RandomColorAdjustOperation::ValidateParams() { - // brightness - RETURN_IF_NOT_OK(ValidateVectorColorAttribute("RandomColorAdjust", "brightness", brightness_, {0})); - // contrast - RETURN_IF_NOT_OK(ValidateVectorColorAttribute("RandomColorAdjust", "contrast", contrast_, {0})); - // saturation - RETURN_IF_NOT_OK(ValidateVectorColorAttribute("RandomColorAdjust", "saturation", saturation_, {0})); - // hue - RETURN_IF_NOT_OK(ValidateVectorColorAttribute("RandomColorAdjust", "hue", hue_, {-0.5, 0.5})); - return Status::OK(); -} - -std::shared_ptr RandomColorAdjustOperation::Build() { - float brightness_lb, brightness_ub, contrast_lb, contrast_ub, saturation_lb, saturation_ub, hue_lb, hue_ub; - - brightness_lb = brightness_[dimension_zero]; - brightness_ub = brightness_[dimension_zero]; - - if (brightness_.size() == size_two) { - brightness_ub = brightness_[dimension_one]; - } - - contrast_lb = contrast_[dimension_zero]; - contrast_ub = contrast_[dimension_zero]; - - if (contrast_.size() == size_two) { - contrast_ub = contrast_[dimension_one]; - } - - saturation_lb = saturation_[dimension_zero]; - saturation_ub = saturation_[dimension_zero]; - - if (saturation_.size() == size_two) { - saturation_ub = saturation_[dimension_one]; - } - - hue_lb = hue_[dimension_zero]; - hue_ub = hue_[dimension_zero]; - - if (hue_.size() == size_two) { - hue_ub = hue_[dimension_one]; - } - - std::shared_ptr tensor_op = std::make_shared( - brightness_lb, brightness_ub, contrast_lb, contrast_ub, saturation_lb, saturation_ub, hue_lb, hue_ub); - return tensor_op; -} - -Status RandomColorAdjustOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["brightness"] = brightness_; - args["contrast"] = contrast_; - args["saturation"] = saturation_; - args["hue"] = hue_; - *out_json = args; - return Status::OK(); -} - -Status RandomColorAdjustOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "brightness", kRandomColorAdjustOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "contrast", kRandomColorAdjustOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "saturation", kRandomColorAdjustOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "hue", kRandomColorAdjustOperation)); - std::vector brightness = op_params["brightness"]; - std::vector contrast = op_params["contrast"]; - std::vector saturation = op_params["saturation"]; - std::vector hue = op_params["hue"]; - *operation = std::make_shared(brightness, contrast, saturation, hue); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h deleted file mode 100644 index 25ef6231f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_ADJUST_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_ADJUST_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomColorAdjustOperation[] = "RandomColorAdjust"; - -class RandomColorAdjustOperation : public TensorOperation { - public: - RandomColorAdjustOperation(const std::vector &brightness, const std::vector &contrast, - const std::vector &saturation, const std::vector &hue); - - ~RandomColorAdjustOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector brightness_; - std::vector contrast_; - std::vector saturation_; - std::vector hue_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_ADJUST_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.cc deleted file mode 100644 index fd580aab5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_color_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomColorOperation. -RandomColorOperation::RandomColorOperation(float t_lb, float t_ub) : t_lb_(t_lb), t_ub_(t_ub) { random_op_ = true; } - -RandomColorOperation::~RandomColorOperation() = default; - -std::string RandomColorOperation::Name() const { return kRandomColorOperation; } - -Status RandomColorOperation::ValidateParams() { - if (t_lb_ < 0.0 || t_ub_ < 0.0) { - std::string err_msg = - "RandomColor: lower bound or upper bound must be greater than or equal to 0, got 'degree'(t_lb): " + - std::to_string(t_lb_) + ", 'degree'(t_ub): " + std::to_string(t_ub_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (t_lb_ > t_ub_) { - std::string err_msg = - "RandomColor: lower bound must be less or equal to upper bound, got 'degree'(t_lb): " + std::to_string(t_lb_) + - ", 'degree'(t_ub): " + std::to_string(t_ub_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomColorOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(t_lb_, t_ub_); - return tensor_op; -} - -Status RandomColorOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["degrees"] = std::vector{t_lb_, t_ub_}; - return Status::OK(); -} - -Status RandomColorOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degrees", kRandomColorOperation)); - std::vector degrees = op_params["degrees"]; - CHECK_FAIL_RETURN_UNEXPECTED(degrees.size() == 2, "The number of degrees should be 2"); - float t_lb = degrees[0]; - float t_ub = degrees[1]; - *operation = std::make_shared(t_lb, t_ub); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h deleted file mode 100644 index 80ddabe38..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_color_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomColorOperation[] = "RandomColor"; - -class RandomColorOperation : public TensorOperation { - public: - RandomColorOperation(float t_lb, float t_ub); - - ~RandomColorOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float t_lb_; - float t_ub_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_COLOR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc deleted file mode 100644 index b5f048ea0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_decode_resize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomCropDecodeResizeOperation -RandomCropDecodeResizeOperation::RandomCropDecodeResizeOperation(const std::vector &size, - const std::vector &scale, - const std::vector &ratio, - InterpolationMode interpolation, int32_t max_attempts) - : RandomResizedCropOperation(size, scale, ratio, interpolation, max_attempts) {} - -RandomCropDecodeResizeOperation::~RandomCropDecodeResizeOperation() = default; - -std::string RandomCropDecodeResizeOperation::Name() const { return kRandomCropDecodeResizeOperation; } - -std::shared_ptr RandomCropDecodeResizeOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - int32_t crop_height = size_[dimension_zero]; - int32_t crop_width = size_[dimension_zero]; - - // User has specified the crop_width value. - if (size_.size() == size_two) { - crop_width = size_[dimension_one]; - } - - float scale_lower_bound = scale_[dimension_zero]; - float scale_upper_bound = scale_[dimension_one]; - - float aspect_lower_bound = ratio_[dimension_zero]; - float aspect_upper_bound = ratio_[dimension_one]; - - auto tensor_op = - std::make_shared(crop_height, crop_width, scale_lower_bound, scale_upper_bound, - aspect_lower_bound, aspect_upper_bound, interpolation_, max_attempts_); - return tensor_op; -} - -RandomCropDecodeResizeOperation::RandomCropDecodeResizeOperation(const RandomResizedCropOperation &base) - : RandomResizedCropOperation(base) {} - -Status RandomCropDecodeResizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["scale"] = scale_; - args["ratio"] = ratio_; - args["interpolation"] = interpolation_; - args["max_attempts"] = max_attempts_; - *out_json = args; - return Status::OK(); -} - -Status RandomCropDecodeResizeOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomCropDecodeResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "scale", kRandomCropDecodeResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ratio", kRandomCropDecodeResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kRandomCropDecodeResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "max_attempts", kRandomCropDecodeResizeOperation)); - std::vector size = op_params["size"]; - std::vector scale = op_params["scale"]; - std::vector ratio = op_params["ratio"]; - auto interpolation = static_cast(op_params["interpolation"]); - int32_t max_attempts = op_params["max_attempts"]; - *operation = - std::make_shared(size, scale, ratio, interpolation, max_attempts); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h deleted file mode 100644 index 7c79b46dd..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_DECODE_RESIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_DECODE_RESIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomCropDecodeResizeOperation[] = "RandomCropDecodeResize"; - -class RandomCropDecodeResizeOperation : public RandomResizedCropOperation { - public: - RandomCropDecodeResizeOperation(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, - int32_t max_attempts); - - explicit RandomCropDecodeResizeOperation(const RandomResizedCropOperation &base); - - ~RandomCropDecodeResizeOperation() override; - - std::shared_ptr Build() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_DECODE_RESIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.cc deleted file mode 100644 index bf0ebea39..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.cc +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomCropOperation -RandomCropOperation::RandomCropOperation(const std::vector &size, const std::vector &padding, - bool pad_if_needed, const std::vector &fill_value, - BorderType padding_mode) - : TensorOperation(true), - size_(size), - padding_(padding), - pad_if_needed_(pad_if_needed), - fill_value_(fill_value), - padding_mode_(padding_mode) { - random_op_ = true; -} - -RandomCropOperation::~RandomCropOperation() = default; - -std::string RandomCropOperation::Name() const { return kRandomCropOperation; } - -Status RandomCropOperation::ValidateParams() { - // size - RETURN_IF_NOT_OK(ValidateVectorSize("RandomCrop", size_)); - // padding - RETURN_IF_NOT_OK(ValidateVectorPadding("RandomCrop", padding_)); - // fill_value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomCrop", fill_value_)); - // padding_mode - if (padding_mode_ != BorderType::kConstant && padding_mode_ != BorderType::kEdge && - padding_mode_ != BorderType::kReflect && padding_mode_ != BorderType::kSymmetric) { - std::string err_msg = "RandomCrop: Invalid BorderType, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomCropOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t dimension_two = 2; - constexpr size_t dimension_three = 3; - constexpr size_t size_one = 1; - constexpr size_t size_two = 2; - constexpr size_t size_three = 3; - - int32_t crop_height = size_[dimension_zero]; - int32_t crop_width = size_[dimension_zero]; - - // User has specified the crop_width value. - if (size_.size() == size_two) { - crop_width = size_[dimension_one]; - } - - int32_t pad_top, pad_bottom, pad_left, pad_right; - switch (padding_.size()) { - case size_one: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_bottom = padding_[dimension_zero]; - break; - case size_two: - pad_left = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_bottom = padding_[dimension_one]; - break; - default: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_right = padding_[dimension_two]; - pad_bottom = padding_[dimension_three]; - } - - uint8_t fill_r, fill_g, fill_b; - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_zero]; - fill_b = fill_value_[dimension_zero]; - - if (fill_value_.size() == size_three) { - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_one]; - fill_b = fill_value_[dimension_two]; - } - - auto tensor_op = std::make_shared(crop_height, crop_width, pad_top, pad_bottom, pad_left, pad_right, - pad_if_needed_, padding_mode_, fill_r, fill_g, fill_b); - return tensor_op; -} - -Status RandomCropOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["padding"] = padding_; - args["pad_if_needed"] = pad_if_needed_; - args["fill_value"] = fill_value_; - args["padding_mode"] = padding_mode_; - *out_json = args; - return Status::OK(); -} - -Status RandomCropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding", kRandomCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_if_needed", kRandomCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRandomCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding_mode", kRandomCropOperation)); - std::vector size = op_params["size"]; - std::vector padding = op_params["padding"]; - bool pad_if_needed = op_params["pad_if_needed"]; - std::vector fill_value = op_params["fill_value"]; - auto padding_mode = static_cast(op_params["padding_mode"]); - *operation = std::make_shared(size, padding, pad_if_needed, fill_value, padding_mode); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h deleted file mode 100644 index f4edfe4e1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_ir.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomCropOperation[] = "RandomCrop"; - -class RandomCropOperation : public TensorOperation { - public: - RandomCropOperation(const std::vector &size, const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, BorderType padding_mode); - - ~RandomCropOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - std::vector padding_; - bool pad_if_needed_; - std::vector fill_value_; - BorderType padding_mode_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc deleted file mode 100644 index bb849eb5b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomCropWithBBoxOperation -RandomCropWithBBoxOperation::RandomCropWithBBoxOperation(const std::vector &size, - const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, - BorderType padding_mode) - : TensorOperation(true), - size_(size), - padding_(padding), - pad_if_needed_(pad_if_needed), - fill_value_(fill_value), - padding_mode_(padding_mode) {} - -RandomCropWithBBoxOperation::~RandomCropWithBBoxOperation() = default; - -std::string RandomCropWithBBoxOperation::Name() const { return kRandomCropWithBBoxOperation; } - -Status RandomCropWithBBoxOperation::ValidateParams() { - // size - RETURN_IF_NOT_OK(ValidateVectorSize("RandomCropWithBBox", size_)); - // padding - RETURN_IF_NOT_OK(ValidateVectorPadding("RandomCropWithBBox", padding_)); - // fill_value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomCropWithBBox", fill_value_)); - // padding_mode - if (padding_mode_ != BorderType::kConstant && padding_mode_ != BorderType::kEdge && - padding_mode_ != BorderType::kReflect && padding_mode_ != BorderType::kSymmetric) { - std::string err_msg = "RandomCropWithBBox: Invalid BorderType, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomCropWithBBoxOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t dimension_two = 2; - constexpr size_t dimension_three = 3; - constexpr size_t size_one = 1; - constexpr size_t size_two = 2; - constexpr size_t size_three = 3; - - int32_t crop_height = size_[dimension_zero]; - int32_t crop_width = size_[dimension_zero]; - - // User has specified the crop_width value. - if (size_.size() == size_two) { - crop_width = size_[dimension_one]; - } - - int32_t pad_top, pad_bottom, pad_left, pad_right; - switch (padding_.size()) { - case size_one: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_bottom = padding_[dimension_zero]; - break; - case size_two: - pad_left = padding_[dimension_zero]; - pad_right = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_bottom = padding_[dimension_one]; - break; - default: - pad_left = padding_[dimension_zero]; - pad_top = padding_[dimension_one]; - pad_right = padding_[dimension_two]; - pad_bottom = padding_[dimension_three]; - } - - uint8_t fill_r, fill_g, fill_b; - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_zero]; - fill_b = fill_value_[dimension_zero]; - - if (fill_value_.size() == size_three) { - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_one]; - fill_b = fill_value_[dimension_two]; - } - - auto tensor_op = - std::make_shared(crop_height, crop_width, pad_top, pad_bottom, pad_left, pad_right, - pad_if_needed_, padding_mode_, fill_r, fill_g, fill_b); - return tensor_op; -} - -Status RandomCropWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["padding"] = padding_; - args["pad_if_needed"] = pad_if_needed_; - args["fill_value"] = fill_value_; - args["padding_mode"] = padding_mode_; - *out_json = args; - return Status::OK(); -} - -Status RandomCropWithBBoxOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding", kRandomCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "pad_if_needed", kRandomCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRandomCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "padding_mode", kRandomCropWithBBoxOperation)); - std::vector size = op_params["size"]; - std::vector padding = op_params["padding"]; - bool pad_if_needed = op_params["pad_if_needed"]; - std::vector fill_value = op_params["fill_value"]; - auto padding_mode = static_cast(op_params["padding_mode"]); - *operation = - std::make_shared(size, padding, pad_if_needed, fill_value, padding_mode); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h deleted file mode 100644 index ee80b613e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_WITH_BBOX_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomCropWithBBoxOperation[] = "RandomCropWithBBox"; - -class RandomCropWithBBoxOperation : public TensorOperation { - public: - RandomCropWithBBoxOperation(const std::vector &size, const std::vector &padding, bool pad_if_needed, - const std::vector &fill_value, BorderType padding_mode); - - ~RandomCropWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - std::vector padding_; - bool pad_if_needed_; - std::vector fill_value_; - BorderType padding_mode_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_CROP_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.cc deleted file mode 100644 index 791c972ce..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.cc +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_equalize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomEqualizeOperation -RandomEqualizeOperation::RandomEqualizeOperation(float prob) : TensorOperation(true), probability_(prob) {} - -RandomEqualizeOperation::~RandomEqualizeOperation() = default; - -std::string RandomEqualizeOperation::Name() const { return kRandomEqualizeOperation; } - -Status RandomEqualizeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomEqualize", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomEqualizeOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(probability_); - return tensor_op; -} - -Status RandomEqualizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomEqualizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomEqualizeOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h deleted file mode 100644 index edf31282b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_equalize_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_EQUALIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_EQUALIZE_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomEqualizeOperation[] = "RandomEqualize"; - -class RandomEqualizeOperation : public TensorOperation { - public: - explicit RandomEqualizeOperation(float prob); - - ~RandomEqualizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_EQUALIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.cc deleted file mode 100644 index 70694d299..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomHorizontalFlipOperation -RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float prob) : TensorOperation(true), probability_(prob) {} - -RandomHorizontalFlipOperation::~RandomHorizontalFlipOperation() = default; - -std::string RandomHorizontalFlipOperation::Name() const { return kRandomHorizontalFlipOperation; } - -Status RandomHorizontalFlipOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomHorizontalFlip", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomHorizontalFlipOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(probability_); - return tensor_op; -} - -Status RandomHorizontalFlipOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomHorizontalFlipOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomHorizontalFlipOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h deleted file mode 100644 index 2a528d314..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomHorizontalFlipOperation[] = "RandomHorizontalFlip"; - -class RandomHorizontalFlipOperation : public TensorOperation { - public: - explicit RandomHorizontalFlipOperation(float prob); - - ~RandomHorizontalFlipOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc deleted file mode 100644 index f6ca83e90..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomHorizontalFlipWithBBoxOperation -RandomHorizontalFlipWithBBoxOperation::RandomHorizontalFlipWithBBoxOperation(float probability) - : TensorOperation(true), probability_(probability) {} - -RandomHorizontalFlipWithBBoxOperation::~RandomHorizontalFlipWithBBoxOperation() = default; - -std::string RandomHorizontalFlipWithBBoxOperation::Name() const { return kRandomHorizontalFlipWithBBoxOperation; } - -Status RandomHorizontalFlipWithBBoxOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomHorizontalFlipWithBBox", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomHorizontalFlipWithBBoxOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(probability_); - return tensor_op; -} - -Status RandomHorizontalFlipWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomHorizontalFlipWithBBoxOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomHorizontalFlipWithBBoxOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h deleted file mode 100644 index 5cb86247c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_WITH_BBOX_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomHorizontalFlipWithBBoxOperation[] = "RandomHorizontalFlipWithBBox"; - -class RandomHorizontalFlipWithBBoxOperation : public TensorOperation { - public: - explicit RandomHorizontalFlipWithBBoxOperation(float probability); - - ~RandomHorizontalFlipWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_HORIZONTAL_FLIP_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.cc deleted file mode 100644 index 2546dae69..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_invert_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomInvertOperation -RandomInvertOperation::RandomInvertOperation(float prob) : TensorOperation(true), probability_(prob) {} - -RandomInvertOperation::~RandomInvertOperation() = default; - -std::string RandomInvertOperation::Name() const { return kRandomInvertOperation; } - -Status RandomInvertOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomInvert", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomInvertOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(probability_); - return tensor_op; -} - -Status RandomInvertOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomInvertOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomInvertOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h deleted file mode 100644 index 841cf1b3c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_invert_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_INVERT_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_INVERT_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomInvertOperation[] = "RandomInvert"; - -class RandomInvertOperation : public TensorOperation { - public: - explicit RandomInvertOperation(float prob); - - ~RandomInvertOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_INVERT_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.cc deleted file mode 100644 index 022853ce0..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.cc +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_lighting_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomLightingOperation. -RandomLightingOperation::RandomLightingOperation(float alpha) : TensorOperation(true), alpha_(alpha) {} - -RandomLightingOperation::~RandomLightingOperation() = default; - -std::string RandomLightingOperation::Name() const { return kRandomLightingOperation; } - -Status RandomLightingOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("RandomLighting", "alpha", alpha_)); - return Status::OK(); -} - -std::shared_ptr RandomLightingOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(alpha_); - return tensor_op; -} - -Status RandomLightingOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["alpha"] = alpha_; - *out_json = args; - return Status::OK(); -} - -Status RandomLightingOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "alpha", kRandomLightingOperation)); - float alpha = op_params["alpha"]; - *operation = std::make_shared(alpha); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h deleted file mode 100644 index da7cee060..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_lighting_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_LIGHTING_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_LIGHTING_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomLightingOperation[] = "RandomLighting"; - -class RandomLightingOperation : public TensorOperation { - public: - explicit RandomLightingOperation(float alpha); - - ~RandomLightingOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float alpha_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_LIGHTING_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.cc deleted file mode 100644 index cd2b359bf..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_posterize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomPosterizeOperation -RandomPosterizeOperation::RandomPosterizeOperation(const std::vector &bit_range) - : TensorOperation(true), bit_range_(bit_range) {} - -RandomPosterizeOperation::~RandomPosterizeOperation() = default; - -std::string RandomPosterizeOperation::Name() const { return kRandomPosterizeOperation; } - -Status RandomPosterizeOperation::ValidateParams() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - constexpr uint8_t kMinimumBitValue = 1; - constexpr uint8_t kMaximumBitValue = 8; - - if (bit_range_.size() != size_two) { - std::string err_msg = - "RandomPosterize: bit_range needs to be of size 2 but is of size: " + std::to_string(bit_range_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (bit_range_[dimension_zero] < kMinimumBitValue || bit_range_[dimension_zero] > kMaximumBitValue) { - std::string err_msg = - "RandomPosterize: min_bit value is out of range [1-8]: " + std::to_string(bit_range_[dimension_zero]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (bit_range_[dimension_one] < kMinimumBitValue || bit_range_[dimension_one] > kMaximumBitValue) { - std::string err_msg = - "RandomPosterize: max_bit value is out of range [1-8]: " + std::to_string(bit_range_[dimension_one]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (bit_range_[dimension_one] < bit_range_[dimension_zero]) { - std::string err_msg = - "RandomPosterize: max_bit value is less than min_bit: max =" + std::to_string(bit_range_[dimension_one]) + - ", min = " + std::to_string(bit_range_[dimension_zero]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomPosterizeOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(bit_range_); - return tensor_op; -} - -Status RandomPosterizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["bits"] = bit_range_; - return Status::OK(); -} - -Status RandomPosterizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "bits", kRandomPosterizeOperation)); - std::vector bit_range = op_params["bits"]; - *operation = std::make_shared(bit_range); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h deleted file mode 100644 index ad24fb801..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_posterize_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_POSTERIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_POSTERIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomPosterizeOperation[] = "RandomPosterize"; - -class RandomPosterizeOperation : public TensorOperation { - public: - explicit RandomPosterizeOperation(const std::vector &bit_range); - - ~RandomPosterizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector bit_range_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_POSTERIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.cc deleted file mode 100644 index 95f7f259c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_resize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomResizeOperation -RandomResizeOperation::RandomResizeOperation(const std::vector &size) : TensorOperation(true), size_(size) {} - -RandomResizeOperation::~RandomResizeOperation() = default; - -std::string RandomResizeOperation::Name() const { return kRandomResizeOperation; } - -Status RandomResizeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("RandomResize", size_)); - return Status::OK(); -} - -std::shared_ptr RandomResizeOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - int32_t height = size_[dimension_zero]; - int32_t width = 0; - - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - - std::shared_ptr tensor_op = std::make_shared(height, width); - return tensor_op; -} - -Status RandomResizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["size"] = size_; - return Status::OK(); -} - -Status RandomResizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomResizeOperation)); - std::vector size = op_params["size"]; - *operation = std::make_shared(size); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h deleted file mode 100644 index be21f9b93..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomResizeOperation[] = "RandomResize"; - -class RandomResizeOperation : public TensorOperation { - public: - explicit RandomResizeOperation(const std::vector &size); - - ~RandomResizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc deleted file mode 100644 index 67cc1df57..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_resize_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomResizeWithBBoxOperation -RandomResizeWithBBoxOperation::RandomResizeWithBBoxOperation(const std::vector &size) - : TensorOperation(true), size_(size) {} - -RandomResizeWithBBoxOperation::~RandomResizeWithBBoxOperation() = default; - -std::string RandomResizeWithBBoxOperation::Name() const { return kRandomResizeWithBBoxOperation; } - -Status RandomResizeWithBBoxOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("RandomResizeWithBBox", size_)); - return Status::OK(); -} - -std::shared_ptr RandomResizeWithBBoxOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - int32_t height = size_[dimension_zero]; - int32_t width = 0; - - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - - std::shared_ptr tensor_op = std::make_shared(height, width); - return tensor_op; -} - -Status RandomResizeWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["size"] = size_; - return Status::OK(); -} - -Status RandomResizeWithBBoxOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomResizeWithBBoxOperation)); - std::vector size = op_params["size"]; - *operation = std::make_shared(size); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h deleted file mode 100644 index cdce4e0c1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_WITH_BBOX_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomResizeWithBBoxOperation[] = "RandomResizeWithBBox"; - -class RandomResizeWithBBoxOperation : public TensorOperation { - public: - explicit RandomResizeWithBBoxOperation(const std::vector &size); - - ~RandomResizeWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZE_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.cc deleted file mode 100644 index 475c7baaf..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.cc +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -RandomResizedCropOperation::RandomResizedCropOperation(const RandomResizedCropOperation &) = default; - -// RandomResizedCropOperation -RandomResizedCropOperation::RandomResizedCropOperation(const std::vector &size, - const std::vector &scale, const std::vector &ratio, - InterpolationMode interpolation, int32_t max_attempts) - : TensorOperation(true), - size_(size), - scale_(scale), - ratio_(ratio), - interpolation_(interpolation), - max_attempts_(max_attempts) {} - -RandomResizedCropOperation::~RandomResizedCropOperation() = default; - -std::string RandomResizedCropOperation::Name() const { return kRandomResizedCropOperation; } - -Status RandomResizedCropOperation::ValidateParams() { - // size - RETURN_IF_NOT_OK(ValidateVectorSize(Name(), size_)); - // scale - RETURN_IF_NOT_OK(ValidateVectorScale(Name(), scale_)); - // ratio - RETURN_IF_NOT_OK(ValidateVectorRatio(Name(), ratio_)); - // max_attempts - if (max_attempts_ < 1) { - std::string err_msg = - Name() + ": max_attempts must be greater than or equal to 1, got: " + std::to_string(max_attempts_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "RandomResizedCrop: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomResizedCropOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - int32_t height = size_[dimension_zero]; - int32_t width = size_[dimension_zero]; - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - std::shared_ptr tensor_op = std::make_shared( - height, width, scale_[dimension_zero], scale_[dimension_one], ratio_[dimension_zero], ratio_[dimension_one], - interpolation_, max_attempts_); - return tensor_op; -} - -Status RandomResizedCropOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["scale"] = scale_; - args["ratio"] = ratio_; - args["interpolation"] = interpolation_; - args["max_attempts"] = max_attempts_; - *out_json = args; - return Status::OK(); -} - -Status RandomResizedCropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "scale", kRandomResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ratio", kRandomResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kRandomResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "max_attempts", kRandomResizedCropOperation)); - std::vector size = op_params["size"]; - std::vector scale = op_params["scale"]; - std::vector ratio = op_params["ratio"]; - auto interpolation = static_cast(op_params["interpolation"]); - int32_t max_attempts = op_params["max_attempts"]; - *operation = std::make_shared(size, scale, ratio, interpolation, max_attempts); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h deleted file mode 100644 index 388f730fe..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomResizedCropOperation[] = "RandomResizedCrop"; - -class RandomResizedCropOperation : public TensorOperation { - public: - RandomResizedCropOperation(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, int32_t max_attempts); - - /// \brief default copy constructor - RandomResizedCropOperation(const RandomResizedCropOperation &); - - // Copy assignment operator - RandomResizedCropOperation &operator=(const RandomResizedCropOperation &) = default; - - ~RandomResizedCropOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - protected: - std::vector size_; - std::vector scale_; - std::vector ratio_; - InterpolationMode interpolation_; - int32_t max_attempts_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc deleted file mode 100644 index ff3301d88..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomResizedCropWithBBoxOperation -RandomResizedCropWithBBoxOperation::RandomResizedCropWithBBoxOperation(const std::vector &size, - const std::vector &scale, - const std::vector &ratio, - InterpolationMode interpolation, - int32_t max_attempts) - : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} - -RandomResizedCropWithBBoxOperation::~RandomResizedCropWithBBoxOperation() = default; - -std::string RandomResizedCropWithBBoxOperation::Name() const { return kRandomResizedCropWithBBoxOperation; } - -Status RandomResizedCropWithBBoxOperation::ValidateParams() { - // size - RETURN_IF_NOT_OK(ValidateVectorSize("RandomResizedCropWithBBox", size_)); - // scale - RETURN_IF_NOT_OK(ValidateVectorScale("RandomResizedCropWithBBox", scale_)); - // ratio - RETURN_IF_NOT_OK(ValidateVectorRatio("RandomResizedCropWithBBox", ratio_)); - // max_attempts - if (max_attempts_ < 1) { - std::string err_msg = "RandomResizedCropWithBBox: max_attempts must be greater than or equal to 1, got: " + - std::to_string(max_attempts_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "RandomResizedCropWithBBox: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomResizedCropWithBBoxOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - int32_t height = size_[dimension_zero]; - int32_t width = size_[dimension_zero]; - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - std::shared_ptr tensor_op = std::make_shared( - height, width, scale_[dimension_zero], scale_[dimension_one], ratio_[dimension_zero], ratio_[dimension_one], - interpolation_, max_attempts_); - return tensor_op; -} - -Status RandomResizedCropWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["scale"] = scale_; - args["ratio"] = ratio_; - args["interpolation"] = interpolation_; - args["max_attempts"] = max_attempts_; - *out_json = args; - return Status::OK(); -} - -Status RandomResizedCropWithBBoxOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kRandomResizedCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "scale", kRandomResizedCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "ratio", kRandomResizedCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kRandomResizedCropWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "max_attempts", kRandomResizedCropWithBBoxOperation)); - std::vector size = op_params["size"]; - std::vector scale = op_params["scale"]; - std::vector ratio = op_params["ratio"]; - auto interpolation = static_cast(op_params["interpolation"]); - int32_t max_attempts = op_params["max_attempts"]; - *operation = - std::make_shared(size, scale, ratio, interpolation, max_attempts); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h deleted file mode 100644 index a76136057..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_WITH_BBOX_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomResizedCropWithBBoxOperation[] = "RandomResizedCropWithBBox"; - -class RandomResizedCropWithBBoxOperation : public TensorOperation { - public: - RandomResizedCropWithBBoxOperation(const std::vector &size, const std::vector &scale, - const std::vector &ratio, InterpolationMode interpolation, - int32_t max_attempts); - - ~RandomResizedCropWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - std::vector scale_; - std::vector ratio_; - InterpolationMode interpolation_; - int32_t max_attempts_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_RESIZED_CROP_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.cc deleted file mode 100644 index 9054a7d7d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.cc +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_rotation_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -constexpr size_t dimension_zero = 0; -constexpr size_t dimension_one = 1; -constexpr size_t dimension_two = 2; -constexpr size_t size_one = 1; -constexpr size_t size_two = 2; -constexpr size_t size_three = 3; - -// Function to create RandomRotationOperation. -RandomRotationOperation::RandomRotationOperation(const std::vector °rees, InterpolationMode resample, - bool expand, const std::vector ¢er, - const std::vector &fill_value) - : TensorOperation(true), - degrees_(degrees), - interpolation_mode_(resample), - expand_(expand), - center_(center), - fill_value_(fill_value) {} - -RandomRotationOperation::~RandomRotationOperation() = default; - -std::string RandomRotationOperation::Name() const { return kRandomRotationOperation; } - -Status RandomRotationOperation::ValidateParams() { - // degrees - if (degrees_.size() != size_two && degrees_.size() != size_one) { - std::string err_msg = - "RandomRotation: degrees must be a vector of one or two values, got: " + std::to_string(degrees_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (degrees_.size() == size_two && degrees_[dimension_one] < degrees_[dimension_zero]) { - std::string err_msg = "RandomRotation: degrees must be in the format of (min, max), got: (" + - std::to_string(degrees_[dimension_zero]) + ", " + std::to_string(degrees_[dimension_one]) + - ")"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } else if (degrees_.size() == size_one && degrees_[dimension_zero] < 0.0) { - std::string err_msg = - "RandomRotation: if degrees only has one value, it must be greater than or equal to 0, got: " + - std::to_string(degrees_[dimension_zero]); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // center - if (!center_.empty() && center_.size() != size_two) { - std::string err_msg = - "RandomRotation: center must be a vector of two values or empty, got: " + std::to_string(center_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - // fill_value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomRotation", fill_value_)); - // interpolation - if (interpolation_mode_ != InterpolationMode::kLinear && - interpolation_mode_ != InterpolationMode::kNearestNeighbour && interpolation_mode_ != InterpolationMode::kCubic && - interpolation_mode_ != InterpolationMode::kArea) { - std::string err_msg = "RandomRotation: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomRotationOperation::Build() { - float start_degree, end_degree; - if (degrees_.size() == size_one) { - start_degree = -degrees_[dimension_zero]; - end_degree = degrees_[dimension_zero]; - } else if (degrees_.size() == size_two) { - start_degree = degrees_[dimension_zero]; - end_degree = degrees_[dimension_one]; - } - - uint8_t fill_r, fill_g, fill_b; - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_zero]; - fill_b = fill_value_[dimension_zero]; - - if (fill_value_.size() == size_three) { - fill_r = fill_value_[dimension_zero]; - fill_g = fill_value_[dimension_one]; - fill_b = fill_value_[dimension_two]; - } - - std::shared_ptr tensor_op = std::make_shared( - start_degree, end_degree, interpolation_mode_, expand_, center_, fill_r, fill_g, fill_b); - return tensor_op; -} - -Status RandomRotationOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["degrees"] = degrees_; - args["resample"] = interpolation_mode_; - args["expand"] = expand_; - args["center"] = center_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status RandomRotationOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degrees", kRandomRotationOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "resample", kRandomRotationOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "expand", kRandomRotationOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "center", kRandomRotationOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRandomRotationOperation)); - std::vector degrees = op_params["degrees"]; - auto resample = static_cast(op_params["resample"]); - bool expand = op_params["expand"]; - std::vector center = op_params["center"]; - std::vector fill_value = op_params["fill_value"]; - *operation = std::make_shared(degrees, resample, expand, center, fill_value); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h deleted file mode 100644 index f3821f0db..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_rotation_ir.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ROTATION_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ROTATION_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomRotationOperation[] = "RandomRotation"; - -class RandomRotationOperation : public TensorOperation { - public: - RandomRotationOperation(const std::vector °rees, InterpolationMode resample, bool expand, - const std::vector ¢er, const std::vector &fill_value); - - ~RandomRotationOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector degrees_; - InterpolationMode interpolation_mode_; - std::vector center_; - bool expand_; - std::vector fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_ROTATION_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.cc deleted file mode 100644 index 0569d46b2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.cc +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#include "mindspore-lite/minddata/dataset/kernels/image/random_select_subpolicy_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomSelectSubpolicyOperation. -RandomSelectSubpolicyOperation::RandomSelectSubpolicyOperation( - const std::vector, double>>> &policy) - : TensorOperation(true), policy_(policy) {} - -RandomSelectSubpolicyOperation::~RandomSelectSubpolicyOperation() = default; - -std::string RandomSelectSubpolicyOperation::Name() const { return kRandomSelectSubpolicyOperation; } - -Status RandomSelectSubpolicyOperation::ValidateParams() { - if (policy_.empty()) { - std::string err_msg = "RandomSelectSubpolicy: policy must not be empty"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < policy_.size(); i++) { - if (policy_[i].empty()) { - std::string err_msg = "RandomSelectSubpolicy: policy[" + std::to_string(i) + "] must not be empty"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t j = 0; j < policy_[i].size(); j++) { - if (policy_[i][j].first == nullptr) { - std::string transform_pos = "[" + std::to_string(i) + "]" + "[" + std::to_string(j) + "]"; - std::string err_msg = "RandomSelectSubpolicy: transform in policy" + transform_pos + " must not be null"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } else { - RETURN_IF_NOT_OK(policy_[i][j].first->ValidateParams()); - } - if (policy_[i][j].second < 0.0 || policy_[i][j].second > 1.0) { - std::string transform_pos = "[" + std::to_string(i) + "]" + "[" + std::to_string(j) + "]"; - std::string err_msg = "RandomSelectSubpolicy: probability of transform in policy" + transform_pos + - " must be between 0.0 and 1.0, got: " + std::to_string(policy_[i][j].second); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - } - return Status::OK(); -} - -std::shared_ptr RandomSelectSubpolicyOperation::Build() { - std::vector policy_tensor_ops; - for (auto &sub_policy : policy_) { - Subpolicy sub_policy_tensor_ops; - (void)std::transform(sub_policy.begin(), sub_policy.end(), std::back_inserter(sub_policy_tensor_ops), - [](const auto &op_pair) -> std::pair, double> { - return std::make_pair(op_pair.first->Build(), op_pair.second); - }); - policy_tensor_ops.push_back(sub_policy_tensor_ops); - } - std::shared_ptr tensor_op = std::make_shared(policy_tensor_ops); - return tensor_op; -} - -Status RandomSelectSubpolicyOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - auto policy_tensor_ops = nlohmann::json::array(); - for (auto &sub_policy : policy_) { - auto sub_policy_tensor_ops = nlohmann::json::array(); - for (auto &op_pair : sub_policy) { - nlohmann::json policy, args; - auto tensor_op = op_pair.first; - RETURN_IF_NOT_OK(tensor_op->to_json(&args)); - policy["tensor_op"]["tensor_op_params"] = args; - policy["tensor_op"]["tensor_op_name"] = tensor_op->Name(); - policy["prob"] = op_pair.second; - sub_policy_tensor_ops.push_back(policy); - } - policy_tensor_ops.push_back(sub_policy_tensor_ops); - } - (*out_json)["policy"] = policy_tensor_ops; - return Status::OK(); -} - -Status RandomSelectSubpolicyOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "policy", kRandomSelectSubpolicyOperation)); - nlohmann::json policy_json = op_params["policy"]; - std::vector, double>>> policy; - std::vector, double>> policy_items; - for (const nlohmann::json &item : policy_json) { - for (nlohmann::json item_pair : item) { - RETURN_IF_NOT_OK(ValidateParamInJson(item_pair, "prob", kRandomSelectSubpolicyOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(item_pair, "tensor_op", kRandomSelectSubpolicyOperation)); - std::vector> operations; - std::pair, double> policy_pair; - nlohmann::json tensor_op_json; - double prob = item_pair["prob"]; - tensor_op_json.push_back(item_pair["tensor_op"]); - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(tensor_op_json, &operations)); - CHECK_FAIL_RETURN_UNEXPECTED(operations.size() == 1, "There should be only 1 tensor operation"); - policy_pair = std::make_pair(operations[0], prob); - policy_items.push_back(policy_pair); - } - policy.push_back(policy_items); - } - *operation = std::make_shared(policy); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h deleted file mode 100644 index 6fd37c1eb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SELECT_SUBPOLICY_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SELECT_SUBPOLICY_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomSelectSubpolicyOperation[] = "RandomSelectSubpolicy"; - -class RandomSelectSubpolicyOperation : public TensorOperation { - public: - explicit RandomSelectSubpolicyOperation( - const std::vector, double>>> &policy); - - ~RandomSelectSubpolicyOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector, double>>> policy_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SELECT_SUBPOLICY_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.cc deleted file mode 100644 index 724b81412..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_sharpness_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -constexpr size_t dimension_zero = 0; -constexpr size_t dimension_one = 1; -constexpr size_t size_two = 2; - -// Function to create RandomSharpness. -RandomSharpnessOperation::RandomSharpnessOperation(const std::vector °rees) - : TensorOperation(true), degrees_(degrees) {} - -RandomSharpnessOperation::~RandomSharpnessOperation() = default; - -std::string RandomSharpnessOperation::Name() const { return kRandomSharpnessOperation; } - -Status RandomSharpnessOperation::ValidateParams() { - if (degrees_.size() != size_two || degrees_[dimension_zero] < 0.0 || degrees_[dimension_one] < 0.0) { - std::string err_msg = "RandomSharpness: degrees must be a vector of two values and greater than or equal to 0."; - MS_LOG(ERROR) << "RandomSharpness: degrees must be a vector of two values and greater than or equal to 0, got: " - << degrees_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (degrees_[dimension_one] < degrees_[dimension_zero]) { - std::string err_msg = "RandomSharpness: degrees must be in the format of (min, max)."; - MS_LOG(ERROR) << "RandomSharpness: degrees must be in the format of (min, max), got: " << degrees_; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomSharpnessOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(degrees_[dimension_zero], degrees_[dimension_one]); - return tensor_op; -} - -Status RandomSharpnessOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["degrees"] = degrees_; - return Status::OK(); -} - -Status RandomSharpnessOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degrees", kRandomSharpnessOperation)); - std::vector degrees = op_params["degrees"]; - *operation = std::make_shared(degrees); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h deleted file mode 100644 index f5e8d6d84..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_sharpness_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SHARPNESS_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SHARPNESS_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomSharpnessOperation[] = "RandomSharpness"; - -class RandomSharpnessOperation : public TensorOperation { - public: - explicit RandomSharpnessOperation(const std::vector °rees); - - ~RandomSharpnessOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector degrees_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SHARPNESS_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.cc deleted file mode 100644 index b05412587..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.cc +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_solarize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomSolarizeOperation. -RandomSolarizeOperation::RandomSolarizeOperation(const std::vector &threshold) - : TensorOperation(true), threshold_(threshold) {} - -RandomSolarizeOperation::~RandomSolarizeOperation() = default; - -std::string RandomSolarizeOperation::Name() const { return kRandomSolarizeOperation; } - -Status RandomSolarizeOperation::ValidateParams() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - constexpr uint8_t kThresholdMax = 255; - - if (threshold_.size() != size_two) { - std::string err_msg = - "RandomSolarize: threshold must be a vector of two values, got: " + std::to_string(threshold_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (auto threshold_value : threshold_) { - if (threshold_value < 0 || threshold_value > kThresholdMax) { - std::string err_msg = - "RandomSolarize: threshold has to be between 0 and 255, got:" + std::to_string(threshold_value); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (threshold_[dimension_zero] > threshold_[dimension_one]) { - std::string err_msg = "RandomSolarize: threshold must be passed in a (min, max) format"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RandomSolarizeOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(threshold_); - return tensor_op; -} - -Status RandomSolarizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["threshold"] = threshold_; - return Status::OK(); -} - -Status RandomSolarizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "threshold", kRandomSolarizeOperation)); - std::vector threshold = op_params["threshold"]; - *operation = std::make_shared(threshold); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h deleted file mode 100644 index faa8a8549..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_solarize_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SOLARIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SOLARIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomSolarizeOperation[] = "RandomSolarize"; - -class RandomSolarizeOperation : public TensorOperation { - public: - explicit RandomSolarizeOperation(const std::vector &threshold); - - ~RandomSolarizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector threshold_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_SOLARIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.cc deleted file mode 100644 index 5ac0377b4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomVerticalFlipOperation -RandomVerticalFlipOperation::RandomVerticalFlipOperation(float prob) : TensorOperation(true), probability_(prob) {} - -RandomVerticalFlipOperation::~RandomVerticalFlipOperation() = default; - -std::string RandomVerticalFlipOperation::Name() const { return kRandomVerticalFlipOperation; } - -Status RandomVerticalFlipOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomVerticalFlip", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomVerticalFlipOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(probability_); - return tensor_op; -} - -Status RandomVerticalFlipOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomVerticalFlipOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomVerticalFlipOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h deleted file mode 100644 index a900b701e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomVerticalFlipOperation[] = "RandomVerticalFlip"; - -class RandomVerticalFlipOperation : public TensorOperation { - public: - explicit RandomVerticalFlipOperation(float prob); - - ~RandomVerticalFlipOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc deleted file mode 100644 index c7e8715df..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RandomVerticalFlipWithBBoxOperation -RandomVerticalFlipWithBBoxOperation::RandomVerticalFlipWithBBoxOperation(float prob) - : TensorOperation(true), probability_(prob) {} - -RandomVerticalFlipWithBBoxOperation::~RandomVerticalFlipWithBBoxOperation() = default; - -std::string RandomVerticalFlipWithBBoxOperation::Name() const { return kRandomVerticalFlipWithBBoxOperation; } - -Status RandomVerticalFlipWithBBoxOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateProbability("RandomVerticalFlipWithBBox", probability_)); - return Status::OK(); -} - -std::shared_ptr RandomVerticalFlipWithBBoxOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(probability_); - return tensor_op; -} - -Status RandomVerticalFlipWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - (*out_json)["prob"] = probability_; - return Status::OK(); -} - -Status RandomVerticalFlipWithBBoxOperation::from_json(nlohmann::json op_params, - std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "prob", kRandomVerticalFlipWithBBoxOperation)); - float prob = op_params["prob"]; - *operation = std::make_shared(prob); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h deleted file mode 100644 index ab6756e8c..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_WITH_BBOX_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRandomVerticalFlipWithBBoxOperation[] = "RandomVerticalFlipWithBBox"; - -class RandomVerticalFlipWithBBoxOperation : public TensorOperation { - public: - explicit RandomVerticalFlipWithBBoxOperation(float prob); - - ~RandomVerticalFlipWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float probability_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RANDOM_VERTICAL_FLIP_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.cc deleted file mode 100644 index 515c58594..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.cc +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h" - -#if !defined(ENABLE_ANDROID) -#include "mindspore-lite/minddata/dataset/kernels/image/rescale_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#if !defined(ENABLE_ANDROID) -// RescaleOperation -RescaleOperation::RescaleOperation(float rescale, float shift) : rescale_(rescale), shift_(shift) {} - -RescaleOperation::~RescaleOperation() = default; - -std::string RescaleOperation::Name() const { return kRescaleOperation; } - -Status RescaleOperation::ValidateParams() { - if (rescale_ < 0.0) { - std::string err_msg = "Rescale: rescale must be greater than or equal to 0, got: " + std::to_string(rescale_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr RescaleOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(rescale_, shift_); - return tensor_op; -} - -Status RescaleOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["rescale"] = rescale_; - args["shift"] = shift_; - *out_json = args; - return Status::OK(); -} - -Status RescaleOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "rescale", kRescaleOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "shift", kRescaleOperation)); - float rescale = op_params["rescale"]; - float shift = op_params["shift"]; - *operation = std::make_shared(rescale, shift); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h deleted file mode 100644 index 1f5edf07d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rescale_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESCALE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESCALE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRescaleOperation[] = "Rescale"; - -class RescaleOperation : public TensorOperation { - public: - RescaleOperation(float rescale, float shift); - - ~RescaleOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - float rescale_; - float shift_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESCALE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.cc deleted file mode 100644 index bfc7ffa61..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.cc +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/resize_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_resize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// ResizeOperation -ResizeOperation::ResizeOperation(const std::vector &size, InterpolationMode interpolation, - const std::string &device_target) - : size_(size), interpolation_(interpolation), device_target_(device_target) {} - -ResizeOperation::~ResizeOperation() = default; - -std::string ResizeOperation::Name() const { return kResizeOperation; } - -Status ResizeOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("Resize", size_)); - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "Resize: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Resize: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ResizeOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - // If size is a single value, the smaller edge of the image will be - // resized to this value with the same image aspect ratio. - int32_t height = size_[dimension_zero]; - int32_t width = 0; - - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - - if (device_target_ == "CPU") { - return std::make_shared(height, width, interpolation_); -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - return std::make_shared(height, width, interpolation_); -#endif - } else { - MS_LOG(ERROR) << "Resize: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -MapTargetDevice ResizeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Resize: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} - -Status ResizeOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["interpolation"] = interpolation_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status ResizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kResizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kResizeOperation)); - std::vector size = op_params["size"]; - auto interpolation = static_cast(op_params["interpolation"]); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(size, interpolation, device_target); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h deleted file mode 100644 index 4eb4b4f70..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_ir.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kResizeOperation[] = "Resize"; - -class ResizeOperation : public TensorOperation { - public: - ResizeOperation(const std::vector &size, InterpolationMode interpolation, - const std::string &device_target = "CPU"); - - ~ResizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector size_; - InterpolationMode interpolation_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.cc deleted file mode 100644 index 5a4b7e098..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.cc +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/resize_preserve_ar_op.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// ResizePreserveAROperation -ResizePreserveAROperation::ResizePreserveAROperation(int32_t height, int32_t width, int32_t img_orientation) - : height_(height), width_(width), img_orientation_(img_orientation) {} - -ResizePreserveAROperation::~ResizePreserveAROperation() = default; - -std::string ResizePreserveAROperation::Name() const { return kResizePreserveAROperation; } - -Status ResizePreserveAROperation::ValidateParams() { - constexpr int64_t max_img_orientation = 8; - if (img_orientation_ < 1 || img_orientation_ > max_img_orientation) { - std::string err_msg = - "ResizePreserveAR: img_orientation must be in range of [1, 8], got: " + std::to_string(img_orientation_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ResizePreserveAROperation::Build() { - return std::make_shared(height_, width_, img_orientation_); -} - -Status ResizePreserveAROperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["height"] = height_; - args["width"] = width_; - args["img_orientation"] = img_orientation_; - *out_json = args; - return Status::OK(); -} - -Status ResizePreserveAROperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "height", kResizePreserveAROperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "width", kResizePreserveAROperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "img_orientation", kResizePreserveAROperation)); - int32_t height = op_params["height"]; - int32_t width = op_params["width"]; - int32_t img_orientation = op_params["img_orientation"]; - *operation = std::make_shared(height, width, img_orientation); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h deleted file mode 100644 index 79185f4fb..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_PRESERVE_AR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_PRESERVE_AR_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kResizePreserveAROperation[] = "ResizePreserveAR"; - -class ResizePreserveAROperation : public TensorOperation { - public: - ResizePreserveAROperation(int32_t height, int32_t width, int32_t img_orientation); - - ~ResizePreserveAROperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t height_; - int32_t width_; - int32_t img_orientation_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_PRESERVE_AR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.cc deleted file mode 100644 index d0ced340a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.cc +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/resize_with_bbox_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// ResizeWithBBoxOperation -ResizeWithBBoxOperation::ResizeWithBBoxOperation(const std::vector &size, InterpolationMode interpolation) - : size_(size), interpolation_(interpolation) {} - -ResizeWithBBoxOperation::~ResizeWithBBoxOperation() = default; - -std::string ResizeWithBBoxOperation::Name() const { return kResizeWithBBoxOperation; } - -Status ResizeWithBBoxOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateVectorSize("ResizeWithBBox", size_)); - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "ResizeWithBBox: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ResizeWithBBoxOperation::Build() { - constexpr size_t dimension_zero = 0; - constexpr size_t dimension_one = 1; - constexpr size_t size_two = 2; - - int32_t height = size_[dimension_zero]; - int32_t width = 0; - - // User specified the width value. - if (size_.size() == size_two) { - width = size_[dimension_one]; - } - - return std::make_shared(height, width, interpolation_); -} - -Status ResizeWithBBoxOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["size"] = size_; - args["interpolation"] = interpolation_; - *out_json = args; - return Status::OK(); -} - -Status ResizeWithBBoxOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kResizeWithBBoxOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kResizeWithBBoxOperation)); - std::vector size = op_params["size"]; - auto interpolation = static_cast(op_params["interpolation"]); - *operation = std::make_shared(size, interpolation); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h deleted file mode 100644 index 37552ed3e..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_WITH_BBOX_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_WITH_BBOX_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kResizeWithBBoxOperation[] = "ResizeWithBBox"; - -class ResizeWithBBoxOperation : public TensorOperation { - public: - ResizeWithBBoxOperation(const std::vector &size, InterpolationMode interpolation_mode); - - ~ResizeWithBBoxOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector size_; - InterpolationMode interpolation_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZE_WITH_BBOX_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.cc deleted file mode 100644 index fa08b5507..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.cc +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/resized_crop_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_resized_crop_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// ResizedCropOperation -ResizedCropOperation::ResizedCropOperation(int32_t top, int32_t left, int32_t height, int32_t width, - const std::vector &size, InterpolationMode interpolation, - const std::string &device_target) - : top_(top), - left_(left), - height_(height), - width_(width), - size_(size), - interpolation_(interpolation), - device_target_(device_target) {} - -ResizedCropOperation::~ResizedCropOperation() = default; - -std::string ResizedCropOperation::Name() const { return kResizedCropOperation; } - -Status ResizedCropOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateIntScalarNonNegative("ResizedCrop", "top", top_)); - RETURN_IF_NOT_OK(ValidateIntScalarNonNegative("ResizedCrop", "left", left_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("ResizedCrop", "height", height_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("ResizedCrop", "width", width_)); - RETURN_IF_NOT_OK(ValidateVectorSize("ResizedCrop", size_)); - - // interpolation - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea && - interpolation_ != InterpolationMode::kCubicPil) { - std::string err_msg = "ResizedCrop: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "ResizedCrop: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ResizedCropOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = - std::make_shared(top_, left_, height_, width_, size_, interpolation_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = - std::make_shared(top_, left_, height_, width_, size_, interpolation_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "ResizedCrop: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status ResizedCropOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["top"] = top_; - args["left"] = left_; - args["height"] = height_; - args["width"] = width_; - args["size"] = size_; - args["interpolation"] = interpolation_; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status ResizedCropOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "top", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "left", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "height", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "width", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "size", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kResizedCropOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kResizedCropOperation)); - int32_t top = op_params["top"]; - int32_t left = op_params["left"]; - int32_t height = op_params["height"]; - int32_t width = op_params["width"]; - std::vector size = op_params["size"]; - auto interpolation = static_cast(op_params["interpolation"]); - std::string device_target = op_params["device_target"]; - - *operation = std::make_shared(top, left, height, width, size, interpolation, device_target); - return Status::OK(); -} - -MapTargetDevice ResizedCropOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "ResizedCrop: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h deleted file mode 100644 index d35b2a010..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/resized_crop_ir.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZED_CROP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZED_CROP_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kResizedCropOperation[] = "ResizedCrop"; - -class ResizedCropOperation : public TensorOperation { - public: - ResizedCropOperation(int32_t top, int32_t left, int32_t height, int32_t width, const std::vector &size, - InterpolationMode interpolation, const std::string &device_target = "CPU"); - - ~ResizedCropOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - int32_t top_; - int32_t left_; - int32_t height_; - int32_t width_; - std::vector size_; - InterpolationMode interpolation_; - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RESIZED_CROP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.cc deleted file mode 100644 index dea1b885f..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.cc +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/rgb_to_bgr_op.h" - -namespace mindspore { -namespace dataset { -namespace vision { -RgbToBgrOperation::RgbToBgrOperation() = default; - -// RGB2BGROperation -RgbToBgrOperation::~RgbToBgrOperation() = default; - -std::string RgbToBgrOperation::Name() const { return kRgbToBgrOperation; } - -Status RgbToBgrOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr RgbToBgrOperation::Build() { return std::make_shared(); } - -Status RgbToBgrOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h deleted file mode 100644 index 6167a579b..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_BGR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_BGR_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRgbToBgrOperation[] = "RgbToBgr"; - -class RgbToBgrOperation : public TensorOperation { - public: - RgbToBgrOperation(); - - ~RgbToBgrOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_BGR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.cc deleted file mode 100644 index a49ec7809..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.cc +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/rgb_to_gray_op.h" - -namespace mindspore { -namespace dataset { -namespace vision { -RgbToGrayOperation::RgbToGrayOperation() = default; - -// RGB2GRAYOperation -RgbToGrayOperation::~RgbToGrayOperation() = default; - -std::string RgbToGrayOperation::Name() const { return kRgbToGrayOperation; } - -Status RgbToGrayOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr RgbToGrayOperation::Build() { return std::make_shared(); } - -Status RgbToGrayOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h deleted file mode 100644 index b9d7fdac4..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_GRAY_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_GRAY_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRgbToGrayOperation[] = "RgbToGray"; - -class RgbToGrayOperation : public TensorOperation { - public: - RgbToGrayOperation(); - - ~RgbToGrayOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGB_TO_GRAY_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.cc deleted file mode 100644 index 8690a0ab6..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/rgba_to_bgr_op.h" -#endif - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RgbaToBgrOperation. -RgbaToBgrOperation::RgbaToBgrOperation() = default; - -RgbaToBgrOperation::~RgbaToBgrOperation() = default; - -std::string RgbaToBgrOperation::Name() const { return kRgbaToBgrOperation; } - -Status RgbaToBgrOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr RgbaToBgrOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(); - return tensor_op; -} - -Status RgbaToBgrOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h deleted file mode 100644 index d63abf169..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_BGR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_BGR_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRgbaToBgrOperation[] = "RgbaToBgr"; - -class RgbaToBgrOperation : public TensorOperation { - public: - RgbaToBgrOperation(); - - ~RgbaToBgrOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_BGR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.cc deleted file mode 100644 index 3023df388..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/rgba_to_rgb_op.h" -#endif - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// RgbaToRgbOperation. -RgbaToRgbOperation::RgbaToRgbOperation() = default; - -RgbaToRgbOperation::~RgbaToRgbOperation() = default; - -std::string RgbaToRgbOperation::Name() const { return kRgbaToRgbOperation; } - -Status RgbaToRgbOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr RgbaToRgbOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(); - return tensor_op; -} - -Status RgbaToRgbOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h deleted file mode 100644 index 3228db135..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_RGB_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_RGB_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRgbaToRgbOperation[] = "RgbaToRgb"; - -class RgbaToRgbOperation : public TensorOperation { - public: - RgbaToRgbOperation(); - - ~RgbaToRgbOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_RGBA_TO_RGB_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.cc deleted file mode 100644 index b63349edf..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.cc +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/rotate_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_rotate_op.h" -#endif - -namespace mindspore { -namespace dataset { -namespace vision { -// RotateOperation -RotateOperation::RotateOperation(FixRotationAngle angle) - : angle_id_(static_cast(angle)), - degrees_(0.0), - interpolation_mode_(InterpolationMode::kLinear), - expand_(false), - center_({}), - fill_value_({}) {} - -RotateOperation::RotateOperation(float degrees, InterpolationMode resample, bool expand, - const std::vector ¢er, const std::vector &fill_value, - const std::string &device_target) - : angle_id_(0), - degrees_(degrees), - interpolation_mode_(resample), - expand_(expand), - center_(center), - fill_value_(fill_value), - device_target_(device_target) {} - -RotateOperation::~RotateOperation() = default; - -std::string RotateOperation::Name() const { return kRotateOperation; } - -Status RotateOperation::ValidateParams() { -#ifndef ENABLE_ANDROID - // center - constexpr auto kCenterSize = 2; - if (!center_.empty() && center_.size() != kCenterSize) { - std::string err_msg = - "Rotate: center must be a vector of two values or empty, got: " + std::to_string(center_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // fill_value - RETURN_IF_NOT_OK(ValidateVectorFillvalue("Rotate", fill_value_)); - - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Rotate: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // interpolation - if (device_target_ == "CPU") { - if (interpolation_mode_ != InterpolationMode::kLinear && - interpolation_mode_ != InterpolationMode::kNearestNeighbour && - interpolation_mode_ != InterpolationMode::kCubic && interpolation_mode_ != InterpolationMode::kArea) { - std::string err_msg = "Rotate: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } else { - if (interpolation_mode_ != InterpolationMode::kLinear && - interpolation_mode_ != InterpolationMode::kNearestNeighbour) { - std::string err_msg = "DvppRotate: Invalid Interpolation mode, only support BILINEAR and NEAREST."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } -#else - if (angle_id_ < 1 || angle_id_ > 8) { - std::string err_msg = "Rotate: angle_id must be in range of [1, 8], got: " + std::to_string(angle_id_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } -#endif - return Status::OK(); -} - -std::shared_ptr RotateOperation::Build() { -#ifndef ENABLE_ANDROID - uint8_t fill_r, fill_g, fill_b; - fill_r = fill_value_[0]; - fill_g = fill_value_[0]; - fill_b = fill_value_[0]; - - if (fill_value_.size() == 3) { - fill_r = fill_value_[0]; - fill_g = fill_value_[1]; - fill_b = fill_value_[2]; - } - - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = - std::make_shared(degrees_, interpolation_mode_, expand_, center_, fill_r, fill_g, fill_b); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared( - degrees_, interpolation_mode_, expand_, center_, fill_r, fill_g, fill_b); // need change shenwei - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Rotate: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -#else - rotate_op_ = std::make_shared(0); - setAngle(angle_id_); - return rotate_op_; -#endif -} - -Status RotateOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; -#ifndef ENABLE_ANDROID - args["degree"] = degrees_; - args["resample"] = interpolation_mode_; - args["expand"] = expand_; - args["center"] = center_; - args["fill_value"] = fill_value_; - args["device_target"] = device_target_; -#else - args["angle_id"] = angle_id_; -#endif - *out_json = args; - return Status::OK(); -} - -Status RotateOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); -#ifndef ENABLE_ANDROID - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "degree", kRotateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "resample", kRotateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "expand", kRotateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "center", kRotateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kRotateOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kRotateOperation)); - float degrees = op_params["degree"]; - auto resample = static_cast(op_params["resample"]); - bool expand = op_params["expand"]; - std::vector center = op_params["center"]; - std::vector fill_value = op_params["fill_value"]; - *operation = std::make_shared(degrees, resample, expand, center, fill_value); -#else - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "angle_id", kRotateOperation)); - uint64_t angle_id = op_params["angle_id"]; - std::shared_ptr rotate_operation = - std::make_shared(FixRotationAngle::k0Degree); - rotate_operation.get()->setAngle(angle_id); - *operation = rotate_operation; -#endif - return Status::OK(); -} - -void RotateOperation::setAngle(uint64_t angle_id) { - std::dynamic_pointer_cast(rotate_op_)->setAngle(angle_id); -} - -MapTargetDevice RotateOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Rotate: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h deleted file mode 100644 index a6215b3e1..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/rotate_ir.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ROTATE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ROTATE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kRotateOperation[] = "Rotate"; - -class RotateOperation : public TensorOperation { - public: - explicit RotateOperation(FixRotationAngle angle); - - RotateOperation(float degrees, InterpolationMode resample, bool expand, const std::vector ¢er, - const std::vector &fill_value, const std::string &device_target = "CPU"); - - ~RotateOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - void setAngle(uint64_t angle_id); - - MapTargetDevice Type() override; - - private: - uint64_t angle_id_; - float degrees_; - InterpolationMode interpolation_mode_; - bool expand_; - std::vector center_; - std::vector fill_value_; - std::shared_ptr rotate_op_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ROTATE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.cc deleted file mode 100644 index 6d935b501..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h" - -#include - -#include "mindspore-lite/minddata/dataset/kernels/image/slice_patches_op.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -// SlicePatchesOperation -SlicePatchesOperation::SlicePatchesOperation(int32_t num_height, int32_t num_width, SliceMode slice_mode, - uint8_t fill_value) - : TensorOperation(), - num_height_(num_height), - num_width_(num_width), - slice_mode_(slice_mode), - fill_value_(fill_value) {} - -SlicePatchesOperation::~SlicePatchesOperation() = default; - -std::string SlicePatchesOperation::Name() const { return kSlicePatchesOperation; } - -Status SlicePatchesOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateIntScalarPositive("SlicePatches", "num_height", num_height_)); - RETURN_IF_NOT_OK(ValidateIntScalarPositive("SlicePatches", "num_width", num_width_)); - return Status::OK(); -} - -std::shared_ptr SlicePatchesOperation::Build() { - auto tensor_op = std::make_shared(num_height_, num_width_, slice_mode_, fill_value_); - return tensor_op; -} - -Status SlicePatchesOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_height"] = num_height_; - args["num_width"] = num_width_; - args["slice_mode"] = slice_mode_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status SlicePatchesOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_height", kSlicePatchesOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_width", kSlicePatchesOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "slice_mode", kSlicePatchesOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kSlicePatchesOperation)); - int32_t num_height = op_params["num_height"]; - int32_t num_width = op_params["num_width"]; - auto slice_mode = static_cast(op_params["slice_mode"]); - uint8_t fill_value = op_params["fill_value"]; - *operation = std::make_shared(num_height, num_width, slice_mode, fill_value); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h deleted file mode 100644 index d02effed7..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/slice_patches_ir.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SLICE_PATCHES_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SLICE_PATCHES_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kSlicePatchesOperation[] = "SlicePatches"; - -class SlicePatchesOperation : public TensorOperation { - public: - SlicePatchesOperation(int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value); - - ~SlicePatchesOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t num_height_; - int32_t num_width_; - SliceMode slice_mode_; - uint8_t fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SLICE_PATCHES_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.cc deleted file mode 100644 index ce298d7d9..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.cc +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/solarize_op.h" -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_solarize_op.h" -#endif -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// SolarizeOperation -SolarizeOperation::SolarizeOperation(const std::vector &threshold, const std::string &device_target) - : threshold_(threshold), device_target_(device_target) {} - -SolarizeOperation::~SolarizeOperation() = default; - -Status SolarizeOperation::ValidateParams() { - constexpr size_t kThresholdSize = 2; - constexpr float kThresholdMax = 255.0; - - if (threshold_.size() != kThresholdSize) { - std::string err_msg = - "Solarize: threshold must be a vector of two values, got: " + std::to_string(threshold_.size()); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (float threshold_value : threshold_) { - if (threshold_value < 0 || threshold_value > kThresholdMax) { - std::string err_msg = "Solarize: threshold has to be between 0 and 255, got:" + std::to_string(threshold_value); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } - if (threshold_[0] > threshold_[1]) { - std::string err_msg = "Solarize: threshold must be passed in a (min, max) format"; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "Solarize: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr SolarizeOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(threshold_); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(threshold_); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "Solarize: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status SolarizeOperation::to_json(nlohmann::json *out_json) { - (*out_json)["threshold"] = threshold_; - (*out_json)["device_target"] = device_target_; - return Status::OK(); -} - -Status SolarizeOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "threshold", kSolarizeOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kSolarizeOperation)); - std::vector threshold = op_params["threshold"]; - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(threshold, device_target); - return Status::OK(); -} - -MapTargetDevice SolarizeOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "Solarize: Invalid device target. It's not CPU or Ascend."; - } - return MapTargetDevice::kInvalid; -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h deleted file mode 100644 index 6a6ce22ce..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/solarize_ir.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SOLARIZE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SOLARIZE_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kSolarizeOperation[] = "Solarize"; - -class SolarizeOperation : public TensorOperation { - public: - explicit SolarizeOperation(const std::vector &threshold, const std::string &device_target = "CPU"); - - ~SolarizeOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override { return kSolarizeOperation; }; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::vector threshold_; - std::string device_target_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SOLARIZE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.cc deleted file mode 100644 index ef5deded5..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h" - -#if !defined(ENABLE_ANDROID) -#include "mindspore-lite/minddata/dataset/kernels/image/swap_red_blue_op.h" -#endif - -namespace mindspore { -namespace dataset { -namespace vision { -#if !defined(ENABLE_ANDROID) -// SwapRedBlueOperation. -SwapRedBlueOperation::SwapRedBlueOperation() = default; - -SwapRedBlueOperation::~SwapRedBlueOperation() = default; - -std::string SwapRedBlueOperation::Name() const { return kSwapRedBlueOperation; } - -Status SwapRedBlueOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr SwapRedBlueOperation::Build() { - std::shared_ptr tensor_op = std::make_shared(); - return tensor_op; -} - -Status SwapRedBlueOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - *operation = std::make_shared(); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h deleted file mode 100644 index f39835e28..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SWAP_RED_BLUE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SWAP_RED_BLUE_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kSwapRedBlueOperation[] = "SwapRedBlue"; - -class SwapRedBlueOperation : public TensorOperation { - public: - SwapRedBlueOperation(); - - ~SwapRedBlueOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_SWAP_RED_BLUE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.cc deleted file mode 100644 index 2d0577f99..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h" - -#include "mindspore-lite/minddata/dataset/kernels/image/to_tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -ToTensorOperation::ToTensorOperation(const std::string &output_type) { - DataType temp_output_type(output_type); - output_type_ = temp_output_type; -} - -ToTensorOperation::ToTensorOperation(const DataType &output_type) { output_type_ = output_type; } - -ToTensorOperation::~ToTensorOperation() = default; - -std::string ToTensorOperation::Name() const { return kToTensorOperation; } - -Status ToTensorOperation::ValidateParams() { - if (output_type_ == DataType::DE_UNKNOWN) { - std::string err_msg = "ToTensor: Invalid data type for output_type parameter."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr ToTensorOperation::Build() { return std::make_shared(output_type_); } - -Status ToTensorOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["output_type"] = output_type_.ToString(); - *out_json = args; - return Status::OK(); -} - -Status ToTensorOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "output_type", kToTensorOperation)); - std::string output_type = op_params["output_type"]; - *operation = std::make_shared(output_type); - return Status::OK(); -} -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h deleted file mode 100644 index 588f23424..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/to_tensor_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TO_TENSOR_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TO_TENSOR_IR_H_ - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kToTensorOperation[] = "ToTensor"; - -class ToTensorOperation : public TensorOperation { - public: - explicit ToTensorOperation(const std::string &output_type); - explicit ToTensorOperation(const DataType &output_type); - - ~ToTensorOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - DataType output_type_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TO_TENSOR_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.cc deleted file mode 100644 index b381f2796..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/trivial_augment_wide_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// TrivialAugmentWideOperation -TrivialAugmentWideOperation::TrivialAugmentWideOperation(int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value) - : num_magnitude_bins_(num_magnitude_bins), interpolation_(interpolation), fill_value_(fill_value) {} - -TrivialAugmentWideOperation::~TrivialAugmentWideOperation() = default; - -std::string TrivialAugmentWideOperation::Name() const { return kTrivialAugmentWideOperation; } - -Status TrivialAugmentWideOperation::ValidateParams() { - RETURN_IF_NOT_OK(ValidateScalar("TrivialAugmentWide", "num_magnitude_bins", num_magnitude_bins_, {1}, true)); - if (interpolation_ != InterpolationMode::kLinear && interpolation_ != InterpolationMode::kNearestNeighbour && - interpolation_ != InterpolationMode::kCubic && interpolation_ != InterpolationMode::kArea) { - std::string err_msg = "TrivialAugmentWide: Invalid InterpolationMode, check input value of enum."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - RETURN_IF_NOT_OK(ValidateVectorFillvalue("TrivialAugmentWide", fill_value_)); - return Status::OK(); -} - -std::shared_ptr TrivialAugmentWideOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(num_magnitude_bins_, interpolation_, fill_value_); - return tensor_op; -} - -Status TrivialAugmentWideOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["num_magnitude_bins"] = num_magnitude_bins_; - args["interpolation"] = interpolation_; - args["fill_value"] = fill_value_; - *out_json = args; - return Status::OK(); -} - -Status TrivialAugmentWideOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_magnitude_bins", kTrivialAugmentWideOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "interpolation", kTrivialAugmentWideOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "fill_value", kTrivialAugmentWideOperation)); - float num_magnitude_bins = op_params["num_magnitude_bins"]; - InterpolationMode interpolation = op_params["interpolation"]; - std::vector fill_value = op_params["fill_value"]; - *operation = std::make_shared(num_magnitude_bins, interpolation, fill_value); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h deleted file mode 100644 index cc97e75e8..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/trivial_augment_wide_ir.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2022-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TRIVIAL_AUGMENT_WIDE_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TRIVIAL_AUGMENT_WIDE_IR_H_ - -#include -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kTrivialAugmentWideOperation[] = "TrivialAugmentWide"; - -class TrivialAugmentWideOperation : public TensorOperation { - public: - TrivialAugmentWideOperation(int32_t num_magnitude_bins, InterpolationMode interpolation, - const std::vector &fill_value); - - ~TrivialAugmentWideOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - int32_t num_magnitude_bins_; - InterpolationMode interpolation_; - std::vector fill_value_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_TRIVIAL_AUGMENT_WIDE_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.cc deleted file mode 100644 index 1be2ccb92..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.cc +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h" - -#include - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/engine/serdes.h" -#include "mindspore-lite/minddata/dataset/kernels/image/uniform_aug_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// UniformAugOperation -UniformAugOperation::UniformAugOperation(const std::vector> &transforms, - int32_t num_ops) - : transforms_(transforms), num_ops_(num_ops) {} - -UniformAugOperation::~UniformAugOperation() = default; - -std::string UniformAugOperation::Name() const { return kUniformAugOperation; } - -Status UniformAugOperation::ValidateParams() { - // transforms - RETURN_IF_NOT_OK(ValidateVectorTransforms("UniformAugment", transforms_)); - // num_ops - RETURN_IF_NOT_OK(ValidateIntScalarPositive("UniformAugment", "num_ops", num_ops_)); - if (num_ops_ > transforms_.size()) { - std::string err_msg = - "UniformAugment: num_ops must be less than or equal to transforms size, but got: " + std::to_string(num_ops_); - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr UniformAugOperation::Build() { - std::vector> tensor_ops; - (void)std::transform( - transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops), - [](const std::shared_ptr &op) -> std::shared_ptr { return op->Build(); }); - std::shared_ptr tensor_op = std::make_shared(tensor_ops, num_ops_); - return tensor_op; -} - -Status UniformAugOperation::to_json(nlohmann::json *out_json) { - CHECK_FAIL_RETURN_UNEXPECTED(out_json != nullptr, "parameter out_json is nullptr"); - nlohmann::json args; - std::vector transforms; - for (const auto &op : transforms_) { - nlohmann::json op_item, op_args; - RETURN_IF_NOT_OK(op->to_json(&op_args)); - op_item["tensor_op_params"] = op_args; - op_item["tensor_op_name"] = op->Name(); - transforms.push_back(op_item); - } - args["transforms"] = transforms; - args["num_ops"] = num_ops_; - *out_json = args; - return Status::OK(); -} - -Status UniformAugOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "transforms", kUniformAugOperation)); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "num_ops", kUniformAugOperation)); - std::vector> transforms = {}; - RETURN_IF_NOT_OK(Serdes::ConstructTensorOps(op_params["transforms"], &transforms)); - int32_t num_ops = op_params["num_ops"]; - *operation = std::make_shared(transforms, num_ops); - return Status::OK(); -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h deleted file mode 100644 index 8437bbb32..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/uniform_aug_ir.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_UNIFORM_AUG_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_UNIFORM_AUG_IR_H_ - -#include -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kUniformAugOperation[] = "UniformAugment"; - -class UniformAugOperation : public TensorOperation { - public: - explicit UniformAugOperation(const std::vector> &transforms, int32_t num_ops); - - ~UniformAugOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - private: - std::vector> transforms_; - int32_t num_ops_; -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_UNIFORM_AUG_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.cc b/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.cc deleted file mode 100644 index aa36e0e6a..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/kernels/image/vertical_flip_op.h" -#endif -#if !defined(BUILD_LITE) && defined(ENABLE_D) -#include "mindspore-lite/minddata/dataset/kernels/image/dvpp/ascend910b/dvpp_vertical_flip_op.h" -#endif -#include "mindspore-lite/minddata/dataset/kernels/ir/validators.h" -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -namespace vision { -#ifndef ENABLE_ANDROID -// VerticalFlipOperation -VerticalFlipOperation::VerticalFlipOperation(const std::string &device_target) : device_target_(device_target) {} - -VerticalFlipOperation::~VerticalFlipOperation() = default; - -std::string VerticalFlipOperation::Name() const { return kVerticalFlipOperation; } - -Status VerticalFlipOperation::ValidateParams() { - // device target - if (device_target_ != "CPU" && device_target_ != "Ascend") { - std::string err_msg = "VerticalFlip: Invalid device target. It's not CPU or Ascend."; - LOG_AND_RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - return Status::OK(); -} - -std::shared_ptr VerticalFlipOperation::Build() { - if (device_target_ == "CPU") { - std::shared_ptr tensor_op = std::make_shared(); - return tensor_op; -#if !defined(BUILD_LITE) && defined(ENABLE_D) - } else if (device_target_ == "Ascend") { - std::shared_ptr dvpp_tensor_op = std::make_shared(); - return dvpp_tensor_op; -#endif - } else { - MS_LOG(ERROR) << "VerticalFlip: Invalid device target. It's not CPU or Ascend."; - return nullptr; - } -} - -Status VerticalFlipOperation::to_json(nlohmann::json *out_json) { - RETURN_UNEXPECTED_IF_NULL(out_json); - nlohmann::json args; - args["device_target"] = device_target_; - *out_json = args; - return Status::OK(); -} - -Status VerticalFlipOperation::from_json(nlohmann::json op_params, std::shared_ptr *operation) { - RETURN_UNEXPECTED_IF_NULL(operation); - RETURN_IF_NOT_OK(ValidateParamInJson(op_params, "device_target", kVerticalFlipOperation)); - std::string device_target = op_params["device_target"]; - *operation = std::make_shared(device_target); - return Status::OK(); -} - -MapTargetDevice VerticalFlipOperation::Type() { - if (device_target_ == "CPU") { - return MapTargetDevice::kCpu; - } else if (device_target_ == "Ascend") { - return MapTargetDevice::kAscend910B; - } else { - MS_LOG(ERROR) << "VerticalFlip: Invalid device target. It's not CPU or Ascend."; - return MapTargetDevice::kInvalid; - } -} -#endif -} // namespace vision -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h b/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h deleted file mode 100644 index 14bc07cbc..000000000 --- a/mindspore-lite/minddata/dataset/kernels/ir/vision/vertical_flip_ir.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2021-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_VERTICAL_FLIP_IR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_VERTICAL_FLIP_IR_H_ - -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/include/dataset/transforms.h" -#include "mindspore-lite/minddata/dataset/kernels/ir/tensor_operation.h" - -namespace mindspore { -namespace dataset { -namespace vision { -constexpr char kVerticalFlipOperation[] = "VerticalFlip"; - -class VerticalFlipOperation : public TensorOperation { - public: - explicit VerticalFlipOperation(const std::string &device_target = "CPU"); - - ~VerticalFlipOperation() override; - - std::shared_ptr Build() override; - - Status ValidateParams() override; - - std::string Name() const override; - - Status to_json(nlohmann::json *out_json) override; - - static Status from_json(nlohmann::json op_params, std::shared_ptr *operation); - - MapTargetDevice Type() override; - - private: - std::string device_target_; // CPU, Ascend -}; -} // namespace vision -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_VERTICAL_FLIP_IR_H_ diff --git a/mindspore-lite/minddata/dataset/kernels/tensor_op.cc b/mindspore-lite/minddata/dataset/kernels/tensor_op.cc deleted file mode 100644 index 24e8f51d2..000000000 --- a/mindspore-lite/minddata/dataset/kernels/tensor_op.cc +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" - -#include -#include - -namespace mindspore { -namespace dataset { -// Name: Compute() -// Description: This Compute() take 1 Tensor and produce 1 Tensor. -// The derived class should override this function otherwise error. -Status TensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!OneToOne()) { - RETURN_STATUS_UNEXPECTED("Wrong Compute() function is called. This is not 1-1 TensorOp."); - } else { - RETURN_STATUS_UNEXPECTED("Is this TensorOp 1-1? If yes, please implement this Compute() in the derived class."); - } -} - -// Name: Compute() -// Description: This Compute() take multiple Tensors from different columns and produce multiple Tensors too. -// The derived class should override this function otherwise error. -Status TensorOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - if (OneToOne()) { - CHECK_FAIL_RETURN_UNEXPECTED(input.size() == 1, "The op is OneToOne, can only accept one tensor as input."); - output->resize(1); - return Compute(input[0], &(*output)[0]); - } - - RETURN_STATUS_UNEXPECTED("Is this TensorOp oneToOne? If no, please implement this Compute() in the derived class."); -} - -Status TensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_STATUS_UNEXPECTED( - "Wrong Compute() function is called. This is a function for operators which can be executed by" - " Ascend310 device. If so, please implement it in the derived class."); -} - -Status TensorOp::OutputShape(const std::vector &inputs, std::vector &outputs) { - if (inputs.size() != NumInput()) { - RETURN_STATUS_UNEXPECTED("The size of the input argument vector does not match the number of inputs"); - } - outputs = inputs; - return Status::OK(); -} - -Status TensorOp::OutputType(const std::vector &inputs, std::vector &outputs) { - if (inputs.size() != NumInput()) { - RETURN_STATUS_UNEXPECTED("The size of the input argument vector does not match the number of inputs"); - } - outputs = inputs; - return Status::OK(); -} - -Status TensorOp::SetAscendResource(const std::shared_ptr &resource) { - RETURN_STATUS_UNEXPECTED("This is a CPU operator which doesn't have Ascend Resource. Please verify your context"); -} - -RandomTensorOp::RandomTensorOp() { - is_deterministic_ = false; - random_generator_.seed(GetSeed()); -} - -void RandomTensorOp::SetSeed(uint32_t seed) { random_generator_.seed(seed); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/kernels/tensor_op.h b/mindspore-lite/minddata/dataset/kernels/tensor_op.h deleted file mode 100644 index c4981fd8d..000000000 --- a/mindspore-lite/minddata/dataset/kernels/tensor_op.h +++ /dev/null @@ -1,367 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_TENSOR_OP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_TENSOR_OP_H_ - -#include -#include -#include - -#include "nlohmann/json.hpp" - -#include "mindspore-lite/minddata/dataset/core/device_resource.h" -#include "mindspore-lite/minddata/dataset/core/device_tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/engine/perf/info_collector.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -#define IO_CHECK(input, output) \ - do { \ - if (input == nullptr || output == nullptr) { \ - RETURN_STATUS_UNEXPECTED("input or output is null."); \ - } \ - } while (false) - -#define IO_CHECK_VECTOR(input, output) \ - do { \ - if (output == nullptr) { \ - RETURN_STATUS_UNEXPECTED("output is null."); \ - } \ - for (auto &_i : input) { \ - if (_i == nullptr) { \ - RETURN_STATUS_UNEXPECTED("input is null."); \ - } \ - } \ - } while (false) - -namespace mindspore { -namespace dataset { -// base class -constexpr char kTensorOp[] = "TensorOp"; - -// image -constexpr char kAdjustBrightnessOp[] = "AdjustBrightnessOp"; -constexpr char kAdjustContrastOp[] = "AdjustContrastOp"; -constexpr char kAdjustGammaOp[] = "AdjustGammaOp"; -constexpr char kAdjustHueOp[] = "AdjustHueOp"; -constexpr char kAdjustSaturationOp[] = "AdjustSaturationOp"; -constexpr char kAffineOp[] = "AffineOp"; -constexpr char kAutoAugmentOp[] = "AutoAugmentOp"; -constexpr char kAutoContrastOp[] = "AutoContrastOp"; -constexpr char kBoundingBoxAugmentOp[] = "BoundingBoxAugmentOp"; -constexpr char kDecodeOp[] = "DecodeOp"; -#ifdef ENABLE_FFMPEG -constexpr char kDecodeVideoOp[] = "DecodeVideoOp"; -#endif -constexpr char kCenterCropOp[] = "CenterCropOp"; -constexpr char kConvertColorOp[] = "ConvertColorOp"; -constexpr char kCutMixBatchOp[] = "CutMixBatchOp"; -constexpr char kCutOutOp[] = "CutOutOp"; -constexpr char kCropOp[] = "CropOp"; -// Ascend310 DVPP just support C++ Interface API -constexpr char kDvppCropJpegOp[] = "DvppCropJpegOp"; -constexpr char kDvppDecodeResizeCropJpegOp[] = "DvppDecodeResizeCropJpegOp"; -constexpr char kDvppDecodeResizeJpegOp[] = "DvppDecodeResizeJpegOp"; -constexpr char kDvppDecodeJpegOp[] = "DvppDecodeJpegOp"; -constexpr char kDvppDecodePngOp[] = "DvppDecodePngOp"; -constexpr char kDvppNormalizeOp[] = "DvppNormalizeOp"; // used by Ascend310 and Ascend910B -constexpr char kDvppResizeJpegOp[] = "DvppResizeJpegOp"; -// Ascend910B DVPP used for Python Interface API -constexpr char kDvppAdjustBrightnessOp[] = "DvppAdjustBrightnessOp"; -constexpr char kDvppAdjustContrastOp[] = "DvppAdjustContrastOp"; -constexpr char kDvppAdjustHueOp[] = "DvppAdjustHueOp"; -constexpr char kDvppAdjustSaturationOp[] = "DvppAdjustSaturationOp"; -constexpr char kDvppAdjustSharpnessOp[] = "DvppAdjustSharpnessOp"; -constexpr char kDvppAffineOp[] = "DvppAffineOp"; -constexpr char kDvppAutoContrastOp[] = "DvppAutoContrastOp"; -constexpr char kDvppConvertColorOp[] = "DvppConvertColorOp"; -constexpr char kDvppCropOp[] = "DvppCropOp"; -constexpr char kDvppDecodeOp[] = "DvppDecodeOp"; -constexpr char kDvppEqualizeOp[] = "DvppEqualizeOp"; -constexpr char kDvppEraseOp[] = "DvppEraseOp"; -constexpr char kDvppGaussianBlurOp[] = "DvppGaussianBlurOp"; -constexpr char kDvppHorizontalFlipOp[] = "DvppHorizontalFlipOp"; -constexpr char kDvppInvertOp[] = "DvppInvertOp"; -constexpr char kDvppPadOp[] = "DvppPadOp"; -constexpr char kDvppPerspectiveOp[] = "DvppPerspectiveOp"; -constexpr char kDvppPosterizeOp[] = "DvppPosterizeOp"; -constexpr char kDvppResizeOp[] = "DvppResizeOp"; -constexpr char kDvppResizedCropOp[] = "DvppResizedCropOp"; -constexpr char kDvppRotateOp[] = "DvppRotateOp"; -constexpr char kDvppSolarizeOp[] = "DvppSolarizeOp"; -constexpr char kDvppVerticalFlipOp[] = "DvppVerticalFlipOp"; -constexpr char kEqualizeOp[] = "EqualizeOp"; -constexpr char kEraseOp[] = "EraseOp"; -constexpr char kGaussianBlurOp[] = "GaussianBlurOp"; -constexpr char kHorizontalFlipOp[] = "HorizontalFlipOp"; -constexpr char kHwcToChwOp[] = "HWC2CHWOp"; -constexpr char kInvertOp[] = "InvertOp"; -constexpr char kMixUpBatchOp[] = "MixUpBatchOp"; -constexpr char kNormalizeOp[] = "NormalizeOp"; -constexpr char kNormalizePadOp[] = "NormalizePadOp"; -constexpr char kPadOp[] = "PadOp"; -constexpr char kPadToSizeOp[] = "PadToSizeOp"; -constexpr char kPerspectiveOp[] = "PerspectiveOp"; -constexpr char kPosterizeOp[] = "PosterizeOp"; -constexpr char kRandAugmentOp[] = "RandAugmentOp"; -constexpr char kRandomAdjustSharpnessOp[] = "RandomAdjustSharpnessOp"; -constexpr char kRandomAffineOp[] = "RandomAffineOp"; -constexpr char kRandomAutoContrastOp[] = "RandomAutoContrastOp"; -constexpr char kRandomColorAdjustOp[] = "RandomColorAdjustOp"; -constexpr char kRandomColorOp[] = "RandomColorOp"; -constexpr char kRandomCropAndResizeOp[] = "RandomCropAndResizeOp"; -constexpr char kRandomCropAndResizeWithBBoxOp[] = "RandomCropAndResizeWithBBoxOp"; -constexpr char kRandomCropDecodeResizeOp[] = "RandomCropDecodeResizeOp"; -constexpr char kRandomCropOp[] = "RandomCropOp"; -constexpr char kRandomCropWithBBoxOp[] = "RandomCropWithBBoxOp"; -constexpr char kRandomEqualizeOp[] = "RandomEqualizeOp"; -constexpr char kRandomHorizontalFlipWithBBoxOp[] = "RandomHorizontalFlipWithBBoxOp"; -constexpr char kRandomHorizontalFlipOp[] = "RandomHorizontalFlipOp"; -constexpr char kRandomInvertOp[] = "RandomInvertOp"; -constexpr char kRandomLightingOp[] = "RandomLightingOp"; -constexpr char kRandomPosterizeOp[] = "RandomPosterizeOp"; -constexpr char kRandomResizeOp[] = "RandomResizeOp"; -constexpr char kRandomResizeWithBBoxOp[] = "RandomResizeWithBBoxOp"; -constexpr char kRandomRotationOp[] = "RandomRotationOp"; -constexpr char kRandomSolarizeOp[] = "RandomSolarizeOp"; -constexpr char kRandomSharpnessOp[] = "RandomSharpnessOp"; -constexpr char kRandomVerticalFlipOp[] = "RandomVerticalFlipOp"; -constexpr char kRandomVerticalFlipWithBBoxOp[] = "RandomVerticalFlipWithBBoxOp"; -constexpr char kRescaleOp[] = "RescaleOp"; -constexpr char kResizeBilinearOp[] = "ResizeBilinearOp"; -constexpr char kResizedCropOp[] = "ResizedCropOp"; -constexpr char kResizeOp[] = "ResizeOp"; -constexpr char kResizePreserveAROp[] = "ResizePreserveAROp"; -constexpr char kResizeWithBBoxOp[] = "ResizeWithBBoxOp"; -constexpr char kRgbaToBgrOp[] = "RgbaToBgrOp"; -constexpr char kRgbaToRgbOp[] = "RgbaToRgbOp"; -constexpr char kRgbToBgrOp[] = "RgbToBgrOp"; -constexpr char kRgbToGrayOp[] = "RgbToGrayOp"; -constexpr char kRotateOp[] = "RotateOp"; -constexpr char kSharpnessOp[] = "SharpnessOp"; -constexpr char kSlicePatchesOp[] = "SlicePatchesOp"; -constexpr char kSolarizeOp[] = "SolarizeOp"; -constexpr char kSwapRedBlueOp[] = "SwapRedBlueOp"; -constexpr char kToTensorOp[] = "ToTensorOp"; -constexpr char kUniformAugOp[] = "UniformAugmentOp"; -constexpr char kVerticalFlipOp[] = "VerticalFlipOp"; - -// video -constexpr char kDvppDecodeVideoOp[] = "DvppDecodeVideoOp"; - -// text -constexpr char kAddTokenOp[] = "AddTokenOp"; -constexpr char kBasicTokenizerOp[] = "BasicTokenizerOp"; -constexpr char kBertTokenizerOp[] = "BertTokenizerOp"; -constexpr char kCaseFoldOp[] = "CaseFoldOp"; -constexpr char kFilterWikipediaXMLOp[] = "FilterWikipediaXMLOp"; -constexpr char kJiebaTokenizerOp[] = "JiebaTokenizerOp"; -constexpr char kLookupOp[] = "LookupOp"; -constexpr char kNgramOp[] = "NgramOp"; -constexpr char kSlidingWindowOp[] = "SlidingWindowOp"; -constexpr char kNormalizeUTF8Op[] = "NormalizeUTF8Op"; -constexpr char kRegexReplaceOp[] = "RegexReplaceOp"; -constexpr char kRegexTokenizerOp[] = "RegexTokenizerOp"; -constexpr char kToNumberOp[] = "ToNumberOp"; -constexpr char kToVectorsOp[] = "ToVectorsOp"; -constexpr char kTruncateOp[] = "TruncateOp"; -constexpr char kTruncateSequencePairOp[] = "TruncateSequencePairOp"; -constexpr char kUnicodeCharTokenizerOp[] = "UnicodeCharTokenizerOp"; -constexpr char kUnicodeScriptTokenizerOp[] = "UnicodeScriptTokenizerOp"; -constexpr char kWhitespaceTokenizerOp[] = "WhitespaceTokenizerOp"; -constexpr char kWordpieceTokenizerOp[] = "WordpieceTokenizerOp"; -constexpr char kRandomChoiceOp[] = "RandomChoiceOp"; -constexpr char kRandomApplyOp[] = "RandomApplyOp"; -constexpr char kComposeOp[] = "ComposeOp"; -constexpr char kRandomSelectSubpolicyOp[] = "RandomSelectSubpolicyOp"; -constexpr char kSentencepieceTokenizerOp[] = "SentencepieceTokenizerOp"; - -// audio -constexpr char kAllpassBiquadOp[] = "AllpassBiquadOp"; -constexpr char kAmplitudeToDBOp[] = "AmplitudeToDBOp"; -constexpr char kAngleOp[] = "AngleOp"; -constexpr char kBandBiquadOp[] = "BandBiquadOp"; -constexpr char kBandpassBiquadOp[] = "BandpassBiquadOp"; -constexpr char kBandrejectBiquadOp[] = "BandrejectBiquadOp"; -constexpr char kBassBiquadOp[] = "BassBiquadOp"; -constexpr char kBiquadOp[] = "BiquadOp"; -constexpr char kComplexNormOp[] = "ComplexNormOp"; -constexpr char kComputeDeltasOp[] = "ComputeDeltasOp"; -constexpr char kContrastOp[] = "ContrastOp"; -constexpr char kDBToAmplitudeOp[] = " DBToAmplitudeOp"; -constexpr char kDCShiftOp[] = "DCShiftOp"; -constexpr char kDeemphBiquadOp[] = "DeemphBiquadOp"; -constexpr char kDetectPitchFrequencyOp[] = "DetectPitchFrequencyOp"; -constexpr char kDitherOp[] = "DitherOp"; -constexpr char kEqualizerBiquadOp[] = "EqualizerBiquadOp"; -constexpr char kFadeOp[] = "FadeOp"; -constexpr char kFiltfiltOp[] = "FiltfiltOp"; -constexpr char kFlangerOp[] = "FlangerOp"; -constexpr char kFrequencyMaskingOp[] = "FrequencyMaskingOp"; -constexpr char kGainOp[] = "GainOp"; -constexpr char kGriffinLimOp[] = "GriffinLimOp"; -constexpr char kHighpassBiquadOp[] = "HighpassBiquadOp"; -constexpr char kInverseMelScaleOp[] = "InverseMelScaleOp"; -constexpr char kInverseSpectrogramOp[] = "InverseSpectrogramOp"; -constexpr char kLFCCOp[] = "LFCCOp"; -constexpr char kLFilterOp[] = "LFilterOp"; -constexpr char kLowpassBiquadOp[] = "LowpassBiquadOp"; -constexpr char kMagphaseOp[] = "MagphaseOp"; -constexpr char kMaskAlongAxisIIDOp[] = "MaskAlongAxisIIDOp"; -constexpr char kMaskAlongAxisOp[] = "MaskAlongAxisOp"; -constexpr char kMelScaleOp[] = "MelScaleOp"; -constexpr char kMelSpectrogramOp[] = "MelSpectrogramOp"; -constexpr char kMFCCOp[] = "MFCCOp"; -constexpr char kMuLawDecodingOp[] = "MuLawDecodingOp"; -constexpr char kMuLawEncodingOp[] = "MuLawEncodingOp"; -constexpr char kOverdriveOp[] = "OverdriveOp"; -constexpr char kPhaserOp[] = "PhaserOp"; -constexpr char kPhaseVocoderOp[] = "PhaseVocoderOp"; -constexpr char kPitchShiftOp[] = "PitchShiftOp"; -constexpr char kResampleOp[] = "ResampleOp"; -constexpr char kRiaaBiquadOp[] = "RiaaBiquadOp"; -constexpr char kSlidingWindowCmnOp[] = "SlidingWindowCmnOp"; -constexpr char kSpectralCentroidOp[] = "SpectralCentroidOp"; -constexpr char kSpectrogramOp[] = "SpectrogramOp"; -constexpr char kTimeMaskingOp[] = "TimeMaskingOp"; -constexpr char kTimeStretchOp[] = "TimeStretchOp"; -constexpr char kTrebleBiquadOp[] = "TrebleBiquadOp"; -constexpr char kVadOp[] = "VadOp"; -constexpr char kVolOp[] = "VolOp"; - -// data -constexpr char kConcatenateOp[] = "ConcatenateOp"; -constexpr char kDuplicateOp[] = "DuplicateOp"; -constexpr char kFillOp[] = "FillOp"; -constexpr char kMaskOp[] = "MaskOp"; -constexpr char kOneHotOp[] = "OneHotOp"; -constexpr char kPadEndOp[] = "PadEndOp"; -constexpr char kParseExampleOp[] = "ParseExampleOp"; -constexpr char kSliceOp[] = "SliceOp"; -constexpr char kToFloat16Op[] = "ToFloat16Op"; -constexpr char kTypeCastOp[] = "TypeCastOp"; -constexpr char kUniqueOp[] = "UniqueOp"; - -// other -constexpr char kCFuncOp[] = "CFuncOp"; -constexpr char kPyFuncOp[] = "PyFuncOp"; -constexpr char kPluginOp[] = "PluginOp"; -constexpr char kNoOp[] = "NoOp"; - -// A class that does a computation on a Tensor -class TensorOp { - public: - TensorOp() = default; - - virtual ~TensorOp() = default; - - // A function that prints info about the tensor operation - // @param out - virtual void Print(std::ostream &out) const { out << Name() << std::endl; } - - // Provide stream operator for displaying it - // @param output stream - // @param so the TensorOp object to be printed - // @return output stream - friend std::ostream &operator<<(std::ostream &out, const TensorOp &so) { - so.Print(out); - return out; - } - - // Perform an operation on one Tensor and produce one Tensor. This is for 1-to-1 column MapOp - // @param input shares the ownership of the Tensor (increase the ref count). - // @param output the address to a shared_ptr where the result will be placed. - // @return Status - virtual Status Compute(const std::shared_ptr &input, std::shared_ptr *output); - - // Perform an operation on Tensors from multiple columns, and produce multiple Tensors. - // This is for m-to-n column MapOp. - // @param input is a vector of shared_ptr to Tensor (pass by const reference). - // @param output is the address to an empty vector of shared_ptr to Tensor. - // @return Status - virtual Status Compute(const TensorRow &input, TensorRow *output); - - // Perform an operation on one DeviceTensor and produce one DeviceTensor. This is for 1-to-1 column MapOp - // @param input shares the ownership of the DeviceTensor (increase the ref count). - // @param output the address to a shared_ptr where the result will be placed. - // @return Status - virtual Status Compute(const std::shared_ptr &input, std::shared_ptr *output); - - // Returns true oif the TensorOp takes one input and returns one output. - // @return true/false - bool OneToOne() { return NumInput() == 1 && NumOutput() == 1; } - - // Returns true oif the TensorOp produces deterministic result. - // @return true/false - bool Deterministic() const { return is_deterministic_; } - - // Function to determine the number of inputs the TensorOp can take. 0: means undefined. - // @return uint32_t - virtual uint32_t NumInput() { return 1; } - - // Function to determine the number of output the TensorOp generates. 0: means undefined. - // @return uint32_t - virtual uint32_t NumOutput() { return 1; } - - // Function to determine the shapes of the output tensor given the input tensors' shapes. - // If a subclass did not override this function, it means that the shape does not change. - // @param inputs in: vector of the shapes of the input tensors. - // @param outputs out: vector of the shapes of the output tensors to be filled. - // @return Status - virtual Status OutputShape(const std::vector &inputs, std::vector &outputs); - - // Function to determine the types of the output tensor given the input tensor's types. - // If a subclass did not override this function, it means that the type does not change. - // @param inputs in: vector of the types of the input tensors. - // @param outputs out: vector of the types of the output tensors to be filled. - // @return Status - virtual Status OutputType(const std::vector &inputs, std::vector &outputs); - - virtual std::string Name() const = 0; - - virtual Status to_json(nlohmann::json *out_json) { return Status::OK(); } - - virtual Status SetAscendResource(const std::shared_ptr &resource); - - virtual bool IsDvppOp() { return false; } - - virtual bool IsHWC() { return true; } // the input of the op is HWC in default - - // Currently, it's used by PyFuncOp which can release global executor when map with thread/process mode - virtual Status ReleaseResource() { return Status::OK(); } - - virtual void SetSeed(uint32_t seed) {} - - protected: - bool is_deterministic_{true}; -}; - -class RandomTensorOp : public TensorOp { - public: - RandomTensorOp(); - - ~RandomTensorOp() override = default; - - protected: - void SetSeed(uint32_t seed) override; - - std::mt19937 random_generator_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_TENSOR_OP_H_ diff --git a/mindspore-lite/minddata/dataset/liteapi/include/datasets.h b/mindspore-lite/minddata/dataset/liteapi/include/datasets.h deleted file mode 100644 index b8b201a41..000000000 --- a/mindspore-lite/minddata/dataset/liteapi/include/datasets.h +++ /dev/null @@ -1,712 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_LITEAPI_INCLUDE_DATASETS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_LITEAPI_INCLUDE_DATASETS_H_ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/api/dual_abi_helper.h" -#include "include/api/visible.h" -#include "include/dataset/iterator.h" -#include "include/dataset/samplers.h" -#include "include/dataset/transforms.h" - -namespace mindspore { -namespace dataset { -class Tensor; -class TensorShape; -class TreeAdapter; -class TreeAdapterLite; -class TreeGetters; - -class DatasetCache; -class DatasetNode; - -class Iterator; - -class TensorOperation; -class SchemaObj; -class SamplerObj; - -// Dataset classes (in alphabetical order) -class BatchDataset; -class MapDataset; -class ProjectDataset; -class ShuffleDataset; -class DSCallback; - -/// \class Dataset datasets.h -/// \brief A base class to represent a dataset in the data pipeline. -class DATASET_API Dataset : public std::enable_shared_from_this { - public: - // need friend class so they can access the children_ field - friend class Iterator; - friend class DataQueueNode; - - /// \brief Constructor - Dataset(); - - /// \brief Destructor - virtual ~Dataset() = default; - - /// \brief Gets the dataset size - /// \param[in] estimate This is only supported by some of the ops and it's used to speed up the process of getting - /// dataset size at the expense of accuracy. - /// \return dataset size. If failed, return -1 - int64_t GetDatasetSize(bool estimate = false); - - /// \brief Gets the output type - /// \return a vector of DataType. If failed, return an empty vector - std::vector GetOutputTypes(); - - /// \brief Gets the output shape - /// \return a vector of TensorShape. If failed, return an empty vector - std::vector> GetOutputShapes(); - - /// \brief Gets the batch size - /// \return int64_t - int64_t GetBatchSize(); - - /// \brief Gets the repeat count - /// \return int64_t - int64_t GetRepeatCount(); - - /// \brief Gets the number of classes - /// \return number of classes. If failed, return -1 - int64_t GetNumClasses(); - - /// \brief Gets the column names - /// \return Names of the columns. If failed, return an empty vector - std::vector GetColumnNames() { return VectorCharToString(GetColumnNamesCharIF()); } - - /// \brief Gets the class indexing - /// \return a map of ClassIndexing. If failed, return an empty map - std::vector>> GetClassIndexing() { - return ClassIndexCharToString(GetClassIndexingCharIF()); - } - - /// \brief Setter function for runtime number of workers - /// \param[in] num_workers The number of threads in this operator - /// \return Shared pointer to the original object - /// \par Example - /// \code - /// /* Set number of workers(threads) to process the dataset in parallel */ - /// std::shared_ptr ds = ImageFolder(folder_path, true); - /// ds = ds->SetNumWorkers(16); - /// \endcode - std::shared_ptr SetNumWorkers(int32_t num_workers); - - /// \brief Function to create an PullBasedIterator over the Dataset - /// \return Shared pointer to the Iterator - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreatePullBasedIterator(); - /// std::unordered_map row; - /// iter->GetNextRow(&row); - /// \endcode - std::shared_ptr CreatePullBasedIterator(); - - /// \brief Function to create an Iterator over the Dataset pipeline - /// \param[in] num_epochs Number of epochs to run through the pipeline, default -1 which means infinite epochs. - /// An empty row is returned at the end of each epoch - /// \return Shared pointer to the Iterator - /// \par Example - /// \code - /// /* dataset is an instance of Dataset object */ - /// std::shared_ptr = dataset->CreateIterator(); - /// std::unordered_map row; - /// iter->GetNextRow(&row); - /// \endcode - std::shared_ptr CreateIterator(int32_t num_epochs = -1) { return CreateIteratorCharIF(num_epochs); } - - /// \brief Function to transfer data through a device. - /// \note If device is Ascend, features of data will be transferred one by one. The limitation - /// of data transmission per time is 256M. - /// \param[in] queue_name Channel name (default="", create new unique name). - /// \param[in] device_type Type of device (default="", get from MSContext). - /// \param[in] device_id id of device (default=1, get from MSContext). - /// \param[in] num_epochs Number of epochs (default=-1, infinite epochs). - /// \param[in] send_epoch_end Whether to send end of sequence to device or not (default=true). - /// \param[in] total_batches Number of batches to be sent to the device (default=0, all data). - /// \param[in] create_data_info_queue Whether to create queue which stores types and shapes - /// of data or not(default=false). - /// \return Returns true if no error encountered else false. - bool DeviceQueue(const std::string &queue_name = "", const std::string &device_type = "", int32_t device_id = 0, - int32_t num_epochs = -1, bool send_epoch_end = true, int32_t total_batches = 0, - bool create_data_info_queue = false) { - return DeviceQueueCharIF(StringToChar(queue_name), StringToChar(device_type), device_id, num_epochs, send_epoch_end, - total_batches, create_data_info_queue); - } - - /// \brief Function to create a Saver to save the dynamic data processed by the dataset pipeline - /// \note Usage restrictions: - /// 1. Supported dataset formats: 'mindrecord' only - /// 2. To save the samples in order, set dataset's shuffle to false and num_files to 1. - /// 3. Before calling the function, do not use batch operator, repeat operator or data augmentation operators - /// with random attribute in map operator. - /// 4. Mindrecord does not support bool, uint64, multi-dimensional uint8(drop dimension) nor - /// multi-dimensional string. - /// \param[in] dataset_path Path to dataset file - /// \param[in] num_files Number of dataset files (default=1) - /// \param[in] dataset_type Dataset format (default="mindrecord") - /// \return Returns true if no error encountered else false - /// \par Example - /// \code - /// /* Create a dataset and save its data into MindRecord */ - /// std::string folder_path = "/path/to/cifar_dataset"; - /// std::shared_ptr ds = Cifar10(folder_path, "all", std::make_shared(0, 10)); - /// std::string save_file = "Cifar10Data.mindrecord"; - /// bool rc = ds->Save(save_file); - /// \endcode - bool Save(const std::string &dataset_path, int32_t num_files = 1, const std::string &dataset_type = "mindrecord") { - return SaveCharIF(StringToChar(dataset_path), num_files, StringToChar(dataset_type)); - } - - /// \brief Function to create a BatchDataset - /// \note Combines batch_size number of consecutive rows into batches - /// \param[in] batch_size The number of rows each batch is created with - /// \param[in] drop_remainder Determines whether or not to drop the last possibly incomplete - /// batch. If true, and if there are less than batch_size rows - /// available to make the last batch, then those rows will - /// be dropped and not propagated to the next node - /// \return Shared pointer to the current BatchDataset - /// \par Example - /// \code - /// /* Create a dataset where every 100 rows is combined into a batch */ - /// std::shared_ptr ds = ImageFolder(folder_path, true); - /// ds = ds->Batch(100, true); - /// \endcode - std::shared_ptr Batch(int32_t batch_size, bool drop_remainder = false); - - /// \brief Function to create a MapDataset - /// \note Applies each operation in operations to this dataset - /// \param[in] operations Vector of raw pointers to TensorTransform objects to be applied on the dataset. Operations - /// are applied in the order they appear in this list - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operator. The default input_columns - /// is the first column - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation - /// This parameter is mandatory if len(input_columns) != len(output_columns) - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current MapDataset - /// \par Example - /// \code - /// // Create objects for the tensor ops - /// std::shared_ptr decode_op = std::make_shared(true); - /// std::shared_ptr random_color_op = std::make_shared(0.0, 0.0); - /// - /// /* 1) Simple map example */ - /// // Apply decode_op on column "image". This column will be replaced by the outputted - /// // column of decode_op. - /// dataset = dataset->Map({decode_op}, {"image"}); - /// - /// // Decode and rename column "image" to "decoded_image". - /// dataset = dataset->Map({decode_op}, {"image"}, {"decoded_image"}); - /// - /// /* 2) Map example with more than one operation */ - /// // Create a dataset where the images are decoded, then randomly color jittered. - /// // decode_op takes column "image" as input and outputs one column. The column - /// // outputted by decode_op is passed as input to random_jitter_op. - /// // random_jitter_op will output one column. Column "image" will be replaced by - /// // the column outputted by random_jitter_op (the very last operation). All other - /// // columns are unchanged. - /// dataset = dataset->Map({decode_op, random_jitter_op}, {"image"}) - /// \endcode - std::shared_ptr Map(const std::vector &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform( - operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](TensorTransform *op) -> std::shared_ptr { return op != nullptr ? op->Parse() : nullptr; }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a MapDataset - /// \note Applies each operation in operations to this dataset - /// \param[in] operations Vector of shared pointers to TensorTransform objects to be applied on the dataset. - /// Operations are applied in the order they appear in this list - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operator. The default input_columns - /// is the first column - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation - /// This parameter is mandatory if len(input_columns) != len(output_columns) - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current MapDataset - std::shared_ptr Map(const std::vector> &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform(operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](const std::shared_ptr &op) -> std::shared_ptr { - return op != nullptr ? op->Parse() : nullptr; - }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a MapDataset - /// \note Applies each operation in operations to this dataset - /// \param[in] operations Vector of TensorTransform objects to be applied on the dataset. Operations are applied in - /// the order they appear in this list - /// \param[in] input_columns Vector of the names of the columns that will be passed to the first - /// operation as input. The size of this list must match the number of - /// input columns expected by the first operator. The default input_columns - /// is the first column - /// \param[in] output_columns Vector of names assigned to the columns outputted by the last operation - /// This parameter is mandatory if len(input_columns) != len(output_columns) - /// The size of this list must match the number of output columns of the - /// last operation. The default output_columns will have the same - /// name as the input columns, i.e., the columns will be replaced - /// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). - /// \param[in] callbacks List of Dataset callbacks to be called. - /// \return Shared pointer to the current MapDataset - std::shared_ptr Map(const std::vector> &operations, - const std::vector &input_columns = {}, - const std::vector &output_columns = {}, - const std::shared_ptr &cache = nullptr, - const std::vector> &callbacks = {}) { - std::vector> transform_ops; - (void)std::transform(operations.begin(), operations.end(), std::back_inserter(transform_ops), - [](TensorTransform &op) -> std::shared_ptr { return op.Parse(); }); - return std::make_shared(shared_from_this(), transform_ops, VectorStringToChar(input_columns), - VectorStringToChar(output_columns), cache, callbacks); - } - - /// \brief Function to create a Project Dataset - /// \note Applies project to the dataset - /// \param[in] columns The name of columns to project - /// \return Shared pointer to the current Dataset - /// \par Example - /// \code - /// /* Reorder the original column names in dataset */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Project({"label", "image"}); - /// \endcode - std::shared_ptr Project(const std::vector &columns) { - return std::make_shared(shared_from_this(), VectorStringToChar(columns)); - } - - /// \brief Function to create a Shuffle Dataset - /// \note Randomly shuffles the rows of this dataset - /// \param[in] buffer_size The size of the buffer (must be larger than 1) for shuffling - /// \return Shared pointer to the current ShuffleDataset - /// \par Example - /// \code - /// /* Rename the original column names in dataset */ - /// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 10)); - /// ds = ds->Rename({"image", "label"}, {"image_output", "label_output"}); - /// \endcode - std::shared_ptr Shuffle(int32_t buffer_size) { - return std::make_shared(shared_from_this(), buffer_size); - } - - std::shared_ptr IRNode() { return ir_node_; } - - protected: - std::shared_ptr tree_getters_; - std::shared_ptr ir_node_; - - private: - // Char interface(CharIF) of GetColumnNames - std::vector> GetColumnNamesCharIF(); - - // Char interface(CharIF) of GetClassIndexing - std::vector, std::vector>> GetClassIndexingCharIF(); - - // Char interface(CharIF) of CreateIterator - std::shared_ptr CreateIteratorCharIF(int32_t num_epochs); - - // Char interface(CharIF) of DeviceQueue - bool DeviceQueueCharIF(const std::vector &queue_name, const std::vector &device_type, int32_t device_id, - int32_t num_epochs, bool send_epoch_end, int32_t total_batches, bool create_data_info_queue); - - // Char interface(CharIF) of Save - bool SaveCharIF(const std::vector &dataset_path, int32_t num_files, const std::vector &dataset_type); -}; - -class DATASET_API SchemaObj { - public: - /// \brief Constructor - explicit SchemaObj(const std::string &schema_file = "") : SchemaObj(StringToChar(schema_file)) {} - - /// \brief Destructor - ~SchemaObj() = default; - - /// \brief SchemaObj Init function - /// \return bool true if schema initialization is successful - Status Init(); - - /// \brief Add new column to the schema with unknown shape of rank 1 - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(mindspore::DataType). - /// \return Status code - Status add_column(const std::string &name, mindspore::DataType ms_type) { - return add_column_char(StringToChar(name), ms_type); - } - - /// \brief Add new column to the schema with unknown shape of rank 1 - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(std::string). - /// \param[in] shape Shape of the column. - /// \return Status code - Status add_column(const std::string &name, const std::string &ms_type) { - return add_column_char(StringToChar(name), StringToChar(ms_type)); - } - - /// \brief Add new column to the schema - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(mindspore::DataType). - /// \param[in] shape Shape of the column. - /// \return Status code - Status add_column(const std::string &name, mindspore::DataType ms_type, const std::vector &shape) { - return add_column_char(StringToChar(name), ms_type, shape); - } - - /// \brief Add new column to the schema - /// \param[in] name Name of the column. - /// \param[in] ms_type Data type of the column(std::string). - /// \param[in] shape Shape of the column. - /// \return Status code - Status add_column(const std::string &name, const std::string &ms_type, const std::vector &shape) { - return add_column_char(StringToChar(name), StringToChar(ms_type), shape); - } - - /// \brief Get a JSON string of the schema - /// \return JSON string of the schema - std::string to_json() { return CharToString(to_json_char()); } - - /// \brief Get a JSON string of the schema - std::string to_string() { return to_json(); } - - /// \brief Set a new value to dataset_type - void set_dataset_type(const std::string &dataset_type); - - /// \brief Set a new value to num_rows - void set_num_rows(int32_t num_rows); - - /// \brief Get the current num_rows - int32_t get_num_rows() const; - - /// \brief Get schema file from JSON file - /// \param[in] json_string Name of JSON file to be parsed. - /// \return Status code - Status FromJSONString(const std::string &json_string) { return FromJSONStringCharIF(StringToChar(json_string)); } - - /// \brief Parse and add column information - /// \param[in] json_string Name of JSON string for column dataset attribute information, decoded from schema file. - /// \return Status code - Status ParseColumnString(const std::string &json_string) { - return ParseColumnStringCharIF(StringToChar(json_string)); - } - - private: - // Char constructor of SchemaObj - explicit SchemaObj(const std::vector &schema_file); - - // Char interface of add_column - Status add_column_char(const std::vector &name, mindspore::DataType ms_type); - - Status add_column_char(const std::vector &name, const std::vector &ms_type); - - Status add_column_char(const std::vector &name, mindspore::DataType ms_type, const std::vector &shape); - - Status add_column_char(const std::vector &name, const std::vector &ms_type, - const std::vector &shape); - - // Char interface of to_json - const std::vector to_json_char(); - - // Char interface of FromJSONString - Status FromJSONStringCharIF(const std::vector &json_string); - - // Char interface of ParseColumnString - Status ParseColumnStringCharIF(const std::vector &json_string); - - struct Data; - std::shared_ptr data_; -}; - -class DATASET_API BatchDataset : public Dataset { - public: - BatchDataset(const std::shared_ptr &input, int32_t batch_size, bool drop_remainder = false); - - ~BatchDataset() override = default; -}; - -class DATASET_API MapDataset : public Dataset { - public: - MapDataset(const std::shared_ptr &input, const std::vector> &operations, - const std::vector> &input_columns, const std::vector> &output_columns, - const std::shared_ptr &cache, const std::vector> &callbacks); - - ~MapDataset() override = default; -}; - -class DATASET_API ProjectDataset : public Dataset { - public: - ProjectDataset(const std::shared_ptr &input, const std::vector> &columns); - - ~ProjectDataset() override = default; -}; - -class DATASET_API ShuffleDataset : public Dataset { - public: - ShuffleDataset(const std::shared_ptr &input, int32_t buffer_size); - - ~ShuffleDataset() override = default; -}; - -/// \brief Function to create a SchemaObj. -/// \param[in] schema_file Path of schema file. -/// \note The reason for using this API is that std::string will be constrained by the -/// compiler option '_GLIBCXX_USE_CXX11_ABI' while char is free of this restriction. -/// \return Shared pointer to the current schema. -std::shared_ptr DATASET_API SchemaCharIF(const std::vector &schema_file); - -/// \brief Function to create a SchemaObj. -/// \param[in] schema_file Path of schema file. -/// \return Shared pointer to the current schema. -inline std::shared_ptr DATASET_API Schema(const std::string &schema_file = "") { - return SchemaCharIF(StringToChar(schema_file)); -} - -class DATASET_API AlbumDataset : public Dataset { - public: - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load, if empty, will read all columns - /// (default = {}). - /// \param[in] decode The option to decode the images in dataset (default = false). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, const std::shared_ptr &sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load. - /// \param[in] decode The option to decode the images in dataset. - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of AlbumDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] data_schema Path to dataset schema file. - /// \param[in] column_names Column names used to specify columns to load. - /// \param[in] decode The option to decode the images in dataset. - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - AlbumDataset(const std::vector &dataset_dir, const std::vector &data_schema, - const std::vector> &column_names, bool decode, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// \brief Destructor of AlbumDataset. - ~AlbumDataset() override = default; -}; - -/// \brief Function to create an AlbumDataset -/// \note The generated dataset is specified through setting a schema -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] data_schema Path to dataset schema file -/// \param[in] column_names Column names used to specify columns to load, if empty, will read all columns. -/// (default = {}) -/// \param[in] decode the option to decode the images in dataset (default = false) -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()) -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Dataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/album_dataset_directory"; -/// std::string schema_file = "/path/to/album_schema_file"; -/// std::vector column_names = {"image", "label", "id"}; -/// std::shared_ptr ds = Album(folder_path, schema_file, column_names); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: As we defined before, each data dictionary owns keys "image", "label" and "id" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Album(const std::string &dataset_dir, const std::string &data_schema, const std::vector &column_names = {}, - bool decode = false, const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -/// \brief Function to create an AlbumDataset -/// \note The generated dataset is specified through setting a schema -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] data_schema Path to dataset schema file -/// \param[in] column_names Column names used to specify columns to load -/// \param[in] decode the option to decode the images in dataset -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Dataset -inline std::shared_ptr DATASET_API Album(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -/// \brief Function to create an AlbumDataset -/// \note The generated dataset is specified through setting a schema -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] data_schema Path to dataset schema file -/// \param[in] column_names Column names used to specify columns to load -/// \param[in] decode the option to decode the images in dataset -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current Dataset -inline std::shared_ptr DATASET_API Album(const std::string &dataset_dir, const std::string &data_schema, - const std::vector &column_names, bool decode, - const std::reference_wrapper sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(data_schema), - VectorStringToChar(column_names), decode, sampler, cache); -} - -class DATASET_API MnistDataset : public Dataset { - public: - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all" (default = "all"). - /// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not - /// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()). - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::shared_ptr &sampler, const std::shared_ptr &cache); - - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". - /// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, const Sampler *sampler, - const std::shared_ptr &cache); - - /// \brief Constructor of MnistDataset. - /// \param[in] dataset_dir Path to the root directory that contains the dataset. - /// \param[in] usage Part of dataset of MNIST, can be "train", "test" or "all". - /// \param[in] sampler Sampler object used to choose samples from the dataset. - /// \param[in] cache Tensor cache to use (default=nullptr which means no cache is used). - MnistDataset(const std::vector &dataset_dir, const std::vector &usage, - const std::reference_wrapper &sampler, const std::shared_ptr &cache); - - /// Destructor of MnistDataset. - ~MnistDataset() override = default; -}; - -/// \brief Function to create a MnistDataset -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] usage of MNIST, can be "train", "test" or "all" (default = "all"). -/// \param[in] sampler Shared pointer to a sampler object used to choose samples from the dataset. If sampler is not -/// given, a `RandomSampler` will be used to randomly iterate the entire dataset (default = RandomSampler()) -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current MnistDataset -/// \par Example -/// \code -/// /* Define dataset path and MindData object */ -/// std::string folder_path = "/path/to/mnist_dataset_directory"; -/// std::shared_ptr ds = Mnist(folder_path, "all", std::make_shared(false, 20)); -/// -/// /* Create iterator to read dataset */ -/// std::shared_ptr iter = ds->CreateIterator(); -/// std::unordered_map row; -/// iter->GetNextRow(&row); -/// -/// /* Note: In MNIST dataset, each dictionary has keys "image" and "label" */ -/// auto image = row["image"]; -/// \endcode -inline std::shared_ptr DATASET_API -Mnist(const std::string &dataset_dir, const std::string &usage = "all", - const std::shared_ptr &sampler = std::make_shared(), - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a MnistDataset -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] usage of MNIST, can be "train", "test" or "all" -/// \param[in] sampler Raw pointer to a sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current MnistDataset -inline std::shared_ptr DATASET_API Mnist(const std::string &dataset_dir, const std::string &usage, - const Sampler *sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} - -/// \brief Function to create a MnistDataset -/// \note The generated dataset has two columns ["image", "label"] -/// \param[in] dataset_dir Path to the root directory that contains the dataset -/// \param[in] usage of MNIST, can be "train", "test" or "all" -/// \param[in] sampler Sampler object used to choose samples from the dataset. -/// \param[in] cache Tensor cache to use. (default=nullptr which means no cache is used). -/// \return Shared pointer to the current MnistDataset -inline std::shared_ptr DATASET_API Mnist(const std::string &dataset_dir, const std::string &usage, - const std::reference_wrapper sampler, - const std::shared_ptr &cache = nullptr) { - return std::make_shared(StringToChar(dataset_dir), StringToChar(usage), sampler, cache); -} -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_INCLUDE_DATASET_DATASETS_H_ diff --git a/mindspore-lite/minddata/dataset/util/allocator.h b/mindspore-lite/minddata/dataset/util/allocator.h deleted file mode 100644 index acd92f201..000000000 --- a/mindspore-lite/minddata/dataset/util/allocator.h +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ALLOCATOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ALLOCATOR_H_ - -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" - -namespace mindspore { -namespace dataset { -// The following conforms to the requirements of -// std::allocator. Do not rename/change any needed -// requirements, e.g. function names, typedef etc. -template -class Allocator { - public: - template - friend class Allocator; - - using value_type = T; - using pointer = T *; - using const_pointer = const T *; - using reference = T &; - using const_reference = const T &; - using size_type = uint64_t; - using difference_type = std::ptrdiff_t; - - template - struct rebind { - using other = Allocator; - }; - - using propagate_on_container_copy_assignment = std::true_type; - using propagate_on_container_move_assignment = std::true_type; - using propagate_on_container_swap = std::true_type; - - explicit Allocator(std::shared_ptr b) : pool_(std::move(b)) {} - - ~Allocator() = default; - - template - explicit Allocator(Allocator const &rhs) : pool_(rhs.pool_) {} - - template - bool operator==(Allocator const &rhs) const { - return pool_ == rhs.pool_; - } - - template - bool operator!=(Allocator const &rhs) const { - return pool_ != rhs.pool_; - } - - pointer allocate(std::size_t n) { - void *p = nullptr; - Status rc = pool_->Allocate(n * sizeof(T), &p); - if (rc.IsOk()) { - return reinterpret_cast(p); - } else if (rc == StatusCode::kMDOutOfMemory) { - MS_LOG(ERROR) << rc.ToString(); - return nullptr; - } else { - MS_LOG(ERROR) << rc.ToString(); - return nullptr; - } - } - - void deallocate(pointer p, std::size_t n = 0) noexcept { - if (pool_ != nullptr) { - pool_->Deallocate(p); - } - } - - size_type max_size() { return pool_->get_max_size(); } - - private: - std::shared_ptr pool_; -}; - -/// \brief It is a wrapper of unique_ptr with a custom Allocator class defined above -template , typename... Args> -Status MakeUnique(std::unique_ptr> *out, C alloc, size_t n, Args &&... args) { - RETURN_UNEXPECTED_IF_NULL(out); - CHECK_FAIL_RETURN_UNEXPECTED(n > 0, "size must be positive"); - T *data = nullptr; - try { - data = alloc.allocate(n); - // Some of our implementation of allocator (e.g. NumaAllocator) don't throw std::bad_alloc. - // So we have to catch for null ptr - if (data == nullptr) { - return Status(StatusCode::kMDOutOfMemory); - } - if (!std::is_arithmetic::value) { - for (size_t i = 0; i < n; i++) { - std::allocator_traits::construct(alloc, &(data[i]), std::forward(args)...); - } - } - auto deleter = [](T *p, C f_alloc, size_t f_n) { - if (!std::is_arithmetic::value && std::is_destructible::value) { - for (size_t i = 0; i < f_n; ++i) { - std::allocator_traits::destroy(f_alloc, &p[i]); - } - } - f_alloc.deallocate(p, f_n); - }; - *out = std::unique_ptr>(data, std::bind(deleter, std::placeholders::_1, alloc, n)); - } catch (const std::bad_alloc &e) { - if (data != nullptr) { - alloc.deallocate(data, n); - } - return Status(StatusCode::kMDOutOfMemory); - } catch (const std::exception &e) { - if (data != nullptr) { - alloc.deallocate(data, n); - } - RETURN_STATUS_UNEXPECTED(e.what()); - } - return Status::OK(); -} - -/// \brief It is a wrapper of the above custom unique_ptr with some additional methods -/// \tparam T The type of object to be allocated -/// \tparam C Allocator. Default to std::allocator -template > -class MemGuard { - public: - using allocator = C; - MemGuard() : n_(0) {} - explicit MemGuard(const allocator &a) : n_(0), alloc_(a) {} - // There is no copy constructor nor assignment operator because the memory is solely owned by this object. - MemGuard(const MemGuard &) = delete; - MemGuard &operator=(const MemGuard &) = delete; - // On the other hand, We can support move constructor - MemGuard(MemGuard &&lhs) noexcept : n_(lhs.n_), alloc_(std::move(lhs.alloc_)), ptr_(std::move(lhs.ptr_)) {} - MemGuard &operator=(MemGuard &&lhs) noexcept { - if (this != &lhs) { - this->deallocate(); - n_ = lhs.n_; - alloc_ = std::move(lhs.alloc_); - ptr_ = std::move(lhs.ptr_); - } - return *this; - } - /// \brief Explicitly deallocate the memory if allocated - void deallocate() { - if (ptr_) { - ptr_.reset(); - } - } - /// \brief Allocate memory (with emplace feature). Previous one will be released. If size is 0, no new memory is - /// allocated. - /// \param n Number of objects of type T to be allocated - /// \tparam Args Extra arguments pass to the constructor of T - template - Status allocate(size_t n, Args &&... args) noexcept { - deallocate(); - n_ = n; - return MakeUnique(&ptr_, alloc_, n, std::forward(args)...); - } - ~MemGuard() noexcept { deallocate(); } - /// \brief Getter function - /// \return The pointer to the memory allocated - T *GetPointer() const { return ptr_.get(); } - /// \brief Getter function - /// \return The pointer to the memory allocated - T *GetMutablePointer() { return ptr_.get(); } - /// \brief Overload [] operator to access a particular element - /// \param x index to the element. Must be less than number of element allocated. - /// \return pointer to the x-th element - T *operator[](size_t x) { return GetMutablePointer() + x; } - /// \brief Overload [] operator to access a particular element - /// \param x index to the element. Must be less than number of element allocated. - /// \return pointer to the x-th element - T *operator[](size_t x) const { return GetPointer() + x; } - /// \brief Return how many bytes are allocated in total - /// \return Number of bytes allocated in total - size_t GetSizeInBytes() const { return n_ * sizeof(T); } - - private: - size_t n_; - allocator alloc_; - std::unique_ptr> ptr_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ALLOCATOR_H_ diff --git a/mindspore-lite/minddata/dataset/util/arena.cc b/mindspore-lite/minddata/dataset/util/arena.cc deleted file mode 100644 index 56d119a03..000000000 --- a/mindspore-lite/minddata/dataset/util/arena.cc +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright 2019-2023 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/arena.h" -#include -#include -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/system_pool.h" -#ifdef WITH_BACKEND -#include "mindspore/ccsrc/runtime/hardware/device_context_manager.h" -#endif - -namespace mindspore { -namespace dataset { -struct MemHdr { - uint32_t sig; - uint64_t addr; - uint64_t blk_size; - MemHdr(uint64_t a, uint64_t sz) : sig(0xDEADBEEF), addr(a), blk_size(sz) {} - static void setHdr(void *p, uint64_t addr, uint64_t sz) { new (p) MemHdr(addr, sz); } - static void getHdr(void *p, MemHdr *hdr) { - auto *tmp = reinterpret_cast(p); - *hdr = *tmp; - } -}; - -ArenaImpl::ArenaImpl(void *ptr, size_t sz) : size_in_bytes_(sz), ptr_(ptr) { - // Divide the memory into blocks. Ignore the last partial block. - uint64_t num_blks = size_in_bytes_ / ARENA_BLK_SZ; - MS_LOG(DEBUG) << "Arena memory pool is created. Number of blocks : " << num_blks << ". Block size : " << ARENA_BLK_SZ - << "."; - tr_.Insert(0, num_blks); -} - -Status ArenaImpl::Allocate(size_t n, void **p) { - RETURN_UNEXPECTED_IF_NULL(p); - if (n == 0) { - *p = nullptr; - return Status::OK(); - } - // Round up n to 1K block - uint64_t req_size = static_cast(n) + ARENA_WALL_OVERHEAD_SZ; - if (req_size > this->get_max_size()) { - return Status(StatusCode::kMDOutOfMemory); - } - uint64_t reqBlk = SizeToBlk(req_size); - // Do a first fit search - auto blk = tr_.Top(); - if (blk.second && reqBlk <= blk.first.priority) { - uint64_t addr = blk.first.key; - uint64_t size = blk.first.priority; - // Trim to the required size and return the rest to the tree. - tr_.Pop(); - if (size > reqBlk) { - tr_.Insert(addr + reqBlk, size - reqBlk); - } - RETURN_UNEXPECTED_IF_NULL(ptr_); - char *q = static_cast(ptr_) + addr * ARENA_BLK_SZ; - MemHdr::setHdr(q, addr, reqBlk); - *p = get_user_addr(q); - } else { - return Status(StatusCode::kMDOutOfMemory); - } - return Status::OK(); -} - -std::pair, bool> ArenaImpl::FindPrevBlk(uint64_t addr) { - for (auto &it : tr_) { - if (it.key + it.priority == addr) { - return std::make_pair(std::make_pair(it.key, it.priority), true); - } else if (it.key > addr) { - break; - } - } - return std::make_pair(std::make_pair(0, 0), false); -} - -void ArenaImpl::Deallocate(void *p) { - if (p == nullptr) { - MS_LOG(ERROR) << "The pointer[p] is null."; - return; - } - auto *q = get_base_addr(p); - MemHdr hdr(0, 0); - MemHdr::getHdr(q, &hdr); - MS_ASSERT(hdr.sig == 0xDEADBEEF); - // We are going to insert a free block back to the treap. But first, check if we can combine - // with the free blocks before and after to form a bigger block. - // Query if we have a free block after us. - auto nextBlk = tr_.Search(hdr.addr + hdr.blk_size); - if (nextBlk.second) { - // Form a bigger block - hdr.blk_size += nextBlk.first.priority; - tr_.DeleteKey(nextBlk.first.key); - } - // Next find a block in front of us. - auto result = FindPrevBlk(hdr.addr); - if (result.second) { - // We can combine with this block - hdr.addr = result.first.first; - hdr.blk_size += result.first.second; - tr_.DeleteKey(result.first.first); - } - // Now we can insert the free node - tr_.Insert(hdr.addr, hdr.blk_size); -} - -bool ArenaImpl::BlockEnlarge(uint64_t *addr, uint64_t old_sz, uint64_t new_sz) { - uint64_t size = old_sz; - // The logic is very much identical to Deallocate. We will see if we can combine with the blocks before and after. - auto next_blk = tr_.Search(*addr + old_sz); - if (next_blk.second) { - size += next_blk.first.priority; - if (size >= new_sz) { - // In this case, we can just enlarge the block without doing any moving. - tr_.DeleteKey(next_blk.first.key); - // Return unused back to the tree. - if (size > new_sz) { - tr_.Insert(*addr + new_sz, size - new_sz); - } - } - return true; - } - // If we still get here, we have to look at the block before us. - auto result = FindPrevBlk(*addr); - if (result.second) { - // We can combine with this block together with the next block (if any) - size += result.first.second; - *addr = result.first.first; - if (size >= new_sz) { - // We can combine with this block together with the next block (if any) - tr_.DeleteKey(*addr); - if (next_blk.second) { - tr_.DeleteKey(next_blk.first.key); - } - // Return unused back to the tree. - if (size > new_sz) { - tr_.Insert(*addr + new_sz, size - new_sz); - } - return true; - } - } - return false; -} - -Status ArenaImpl::FreeAndAlloc(void **pp, size_t old_sz, size_t new_sz) { - RETURN_UNEXPECTED_IF_NULL(pp); - RETURN_UNEXPECTED_IF_NULL(*pp); - void *p = nullptr; - void *q = *pp; - RETURN_IF_NOT_OK(Allocate(new_sz, &p)); - errno_t err = memmove_s(p, new_sz, q, old_sz); - if (err != EOK) { - RETURN_STATUS_UNEXPECTED("Error from memmove: " + std::to_string(err)); - } - *pp = p; - // Free the old one. - Deallocate(q); - return Status::OK(); -} - -Status ArenaImpl::Reallocate(void **pp, size_t old_sz, size_t new_sz) { - RETURN_UNEXPECTED_IF_NULL(pp); - RETURN_UNEXPECTED_IF_NULL(*pp); - uint64_t actual_size = static_cast(new_sz) + ARENA_WALL_OVERHEAD_SZ; - if (actual_size > this->get_max_size()) { - RETURN_STATUS_UNEXPECTED("Request size too big : " + std::to_string(new_sz)); - } - uint64_t req_blk = SizeToBlk(actual_size); - char *oldAddr = reinterpret_cast(*pp); - auto *oldHdr = get_base_addr(oldAddr); - MemHdr hdr(0, 0); - MemHdr::getHdr(oldHdr, &hdr); - MS_ASSERT(hdr.sig == 0xDEADBEEF); - if (hdr.blk_size > req_blk) { - // Refresh the header with the new smaller size. - MemHdr::setHdr(oldHdr, hdr.addr, req_blk); - // Return the unused memory back to the tree. Unlike allocate, we we need to merge with the block after us. - auto next_blk = tr_.Search(hdr.addr + hdr.blk_size); - if (next_blk.second) { - hdr.blk_size += next_blk.first.priority; - tr_.DeleteKey(next_blk.first.key); - } - tr_.Insert(hdr.addr + req_blk, hdr.blk_size - req_blk); - } else if (hdr.blk_size < req_blk) { - uint64_t addr = hdr.addr; - // Attempt a block enlarge. No guarantee it is always successful. - bool success = BlockEnlarge(&addr, hdr.blk_size, req_blk); - if (success) { - RETURN_UNEXPECTED_IF_NULL(ptr_); - auto *newHdr = static_cast(ptr_) + addr * ARENA_BLK_SZ; - MemHdr::setHdr(newHdr, addr, req_blk); - if (addr != hdr.addr) { - errno_t err = - memmove_s(get_user_addr(newHdr), (req_blk * ARENA_BLK_SZ) - ARENA_WALL_OVERHEAD_SZ, oldAddr, old_sz); - if (err != EOK) { - RETURN_STATUS_UNEXPECTED("Error from memmove: " + std::to_string(err)); - } - } - *pp = get_user_addr(newHdr); - return Status::OK(); - } - return FreeAndAlloc(pp, old_sz, new_sz); - } - return Status::OK(); -} - -int ArenaImpl::PercentFree() const { - uint64_t sz = 0; - for (auto &it : tr_) { - sz += it.priority; - } - if (size_in_bytes_ == 0) { - MS_LOG(ERROR) << "size_in_bytes_ can not be zero."; - return 0; - } - double ratio = static_cast(sz * ARENA_BLK_SZ) / static_cast(size_in_bytes_); - return static_cast(ratio * 100.0); -} - -uint64_t ArenaImpl::SizeToBlk(uint64_t sz) { - uint64_t req_blk = sz / ARENA_BLK_SZ; - if (sz % ARENA_BLK_SZ) { - ++req_blk; - } - return req_blk; -} - -std::ostream &operator<<(std::ostream &os, const ArenaImpl &s) { - for (auto &it : s.tr_) { - os << "Address : " << it.key << ". Size : " << it.priority << "\n"; - } - return os; -} - -Status Arena::Init() { - try { - int64_t sz = size_in_MB_ * 1048576L; -#ifdef WITH_BACKEND - if (is_cuda_malloc_) { - auto ms_context = MsContext::GetInstance(); - RETURN_UNEXPECTED_IF_NULL(ms_context); - auto device_context = device::DeviceContextManager::GetInstance().GetOrCreateDeviceContext( - {ms_context->get_param(MS_CTX_DEVICE_TARGET), ms_context->get_param(MS_CTX_DEVICE_ID)}); - RETURN_UNEXPECTED_IF_NULL(device_context); - RETURN_UNEXPECTED_IF_NULL(device_context->device_res_manager_); - ptr_ = device_context->device_res_manager_->AllocateHostMemory(sz); - } else { - ptr_ = std::shared_ptr(::malloc(sz), ::free); - } -#else - ptr_ = std::shared_ptr(::malloc(sz), ::free); -#endif - if (ptr_ == nullptr) { - return Status(StatusCode::kMDOutOfMemory); - } - impl_ = std::make_unique(ptr_.get(), sz); - if (impl_ == nullptr) { - return Status(StatusCode::kMDOutOfMemory); - } - } catch (const std::bad_alloc &e) { - return Status(StatusCode::kMDOutOfMemory); - } - return Status::OK(); -} - -Arena::Arena(size_t val_in_MB, bool is_cuda_malloc) - : ptr_(nullptr), size_in_MB_(val_in_MB), is_cuda_malloc_(is_cuda_malloc) {} - -Status Arena::CreateArena(std::shared_ptr *p_ba, size_t val_in_MB, bool is_cuda_malloc) { - RETURN_UNEXPECTED_IF_NULL(p_ba); - auto ba = new (std::nothrow) Arena(val_in_MB, is_cuda_malloc); - if (ba == nullptr) { - return Status(StatusCode::kMDOutOfMemory); - } - (*p_ba).reset(ba); - RETURN_IF_NOT_OK(ba->Init()); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/arena.h b/mindspore-lite/minddata/dataset/util/arena.h deleted file mode 100644 index d8c060bca..000000000 --- a/mindspore-lite/minddata/dataset/util/arena.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ARENA_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ARENA_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" -#include "mindspore-lite/minddata/dataset/util/treap.h" - -#define ARENA_LOG_BLK_SZ (6u) -#define ARENA_BLK_SZ (static_cast(1u << ARENA_LOG_BLK_SZ)) -#define ARENA_WALL_OVERHEAD_SZ 32 -namespace mindspore { -namespace dataset { -/// This is a memory arena based on a treap data structure. -/// The constructor of the Arena takes the size of the initial memory size (in MB). -/// Internally we divide the memory into multiple blocks. Each block is 64 bytes. -/// The treap contains all the free blocks with the relative memory address as key -/// and the size of the block as priority. -/// -/// Initially the treap has only one root which is the whole memory piece. -/// -/// For memory suballocation, we pop the root node of the treap which contains the largest free block. -/// We allocate what we need and return the rest back to the treap. We search for the first fit instead -/// of the best fit so to give us a constant time in memory allocation. -/// -/// When a block of memory is freed. It is joined with the blocks before and after (if they are available) to -/// form a bigger block. - -/// At the lowest level, we don't really care where the memory is coming from. -/// This allows other class to make use of Arena method and override the origin of the -/// memory, say from some unix shared memory instead. -/// \note Implementation class is not thread safe. Caller needs to ensure proper serialization -class ArenaImpl { - public: - /// Constructor - /// \param ptr The start of the memory address - /// \param sz Size of the memory block we manage - ArenaImpl(void *ptr, size_t sz); - ~ArenaImpl() { ptr_ = nullptr; } - - /// \brief Allocate a sub block - /// \param n Size requested - /// \param p pointer to where the result is stored - /// \return Status object. - Status Allocate(size_t n, void **p); - - /// \brief Enlarge or shrink a sub block - /// \param old_sz Original size - /// \param new_sz New size - /// \return Status object - Status Reallocate(void **, size_t old_sz, size_t new_sz); - - /// \brief Free a sub block - /// \param Address of the block to be freed. - void Deallocate(void *); - - /// \brief Calculate % free of the memory - /// \return Percent free - int PercentFree() const; - - /// \brief What is the maximum we can support in allocate. - /// \return Max value - uint64_t get_max_size() const { return (size_in_bytes_ - ARENA_WALL_OVERHEAD_SZ); } - - /// \brief Get the start of the address. Read only - /// \return Start of the address block - const void *get_base_addr() const { return ptr_; } - - static uint64_t SizeToBlk(uint64_t sz); - friend std::ostream &operator<<(std::ostream &os, const ArenaImpl &s); - - private: - size_t size_in_bytes_; - Treap tr_; - void *ptr_; - - void *get_user_addr(void *base_addr) const { return reinterpret_cast(base_addr) + ARENA_WALL_OVERHEAD_SZ; } - void *get_base_addr(void *user_addr) const { return reinterpret_cast(user_addr) - ARENA_WALL_OVERHEAD_SZ; } - std::pair, bool> FindPrevBlk(uint64_t addr); - bool BlockEnlarge(uint64_t *addr, uint64_t old_sz, uint64_t new_sz); - Status FreeAndAlloc(void **pp, size_t old_sz, size_t new_sz); -}; - -/// \brief This version of Arena allocates from private memory -class Arena : public MemoryPool { - public: - // Disable copy and assignment constructor - Arena(const Arena &) = delete; - Arena &operator=(const Arena &) = delete; - ~Arena() override = default; - - /// As a derived class of MemoryPool, we have to implement the following. - /// But we simply transfer the call to the implementation class - Status Allocate(size_t size, void **pVoid) override { - std::unique_lock lock(mux_); - return impl_->Allocate(size, pVoid); - } - Status Reallocate(void **pVoid, size_t old_sz, size_t new_sz) override { - std::unique_lock lock(mux_); - return impl_->Reallocate(pVoid, old_sz, new_sz); - } - void Deallocate(void *pVoid) override { - std::unique_lock lock(mux_); - if (impl_) { - impl_->Deallocate(pVoid); - } - } - uint64_t get_max_size() const override { return impl_->get_max_size(); } - int PercentFree() const override { - std::unique_lock lock(mux_); - return impl_->PercentFree(); - } - - /// \return Return the start of the memory block - const void *get_base_addr() const { return impl_->get_base_addr(); } - - /// \brief Dump the memory allocation block. - friend std::ostream &operator<<(std::ostream &os, const Arena &s) { - os << *(s.impl_); - return os; - } - /// The only method to create an arena. - static Status CreateArena(std::shared_ptr *p_ba, size_t val_in_MB = 4096, bool is_cuda_malloc = false); - - protected: - mutable std::mutex mux_; - std::unique_ptr impl_; - std::shared_ptr ptr_; - size_t size_in_MB_; - - bool is_cuda_malloc_; - explicit Arena(size_t val_in_MB = 4096, bool is_cuda_malloc = false); - - Status Init(); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_ARENA_H_ diff --git a/mindspore-lite/minddata/dataset/util/auto_index.h b/mindspore-lite/minddata/dataset/util/auto_index.h deleted file mode 100644 index 3a31e150b..000000000 --- a/mindspore-lite/minddata/dataset/util/auto_index.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_AUTO_INDEX_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_AUTO_INDEX_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/btree.h" -#include "mindspore-lite/minddata/dataset/util/system_pool.h" - -namespace mindspore { -namespace dataset { -/// This is a B+ tree with generated int64_t value as key. -/// Use minKey() function to query the min key. -/// Use maxKey() function to query the max key. -/// @tparam T -template , typename T = BPlusTreeTraits> -class AutoIndexObj : public BPlusTree, T> { - public: - using my_tree = BPlusTree, T>; - using key_type = typename my_tree::key_type; - using value_type = typename my_tree::value_type; - - AutoIndexObj() : my_tree::BPlusTree(), inx_(kMinKey) {} - - explicit AutoIndexObj(const Allocator &alloc) : my_tree::BPlusTree(alloc), inx_(kMinKey) {} - - ~AutoIndexObj() = default; - - // Insert an object into the tree. - // @param val - // @return - Status insert(const value_type &val, key_type *key = nullptr) { - key_type my_inx = inx_.fetch_add(1); - if (key != nullptr) { - *key = my_inx; - } - return my_tree::DoInsert(my_inx, val); - } - - Status insert(std::unique_ptr &&val, key_type *key = nullptr) { - key_type my_inx = inx_.fetch_add(1); - if (key != nullptr) { - *key = my_inx; - } - return my_tree::DoInsert(my_inx, std::move(val)); - } - - // Insert a vector of objects into the tree. - // @param v - // @return - Status insert(std::vector v) { - uint64_t num_ele = v.size(); - if (num_ele > 0) { - // reserve a range of keys rather than getting it one by one. - key_type my_inx = inx_.fetch_add(num_ele); - for (uint64_t i = 0; i < num_ele; i++) { - RETURN_IF_NOT_OK(my_tree::DoInsert(my_inx + i, v.at(i))); - } - } - return Status::OK(); - } - - // @return the minimum key - key_type min_key() const { - auto it = this->cbegin(); - return it.key(); - } - - // @return the maximum key - key_type max_key() const { - auto it = this->cend(); - --it; - return it.key(); - } - - private: - static constexpr key_type kMinKey = 0; - std::atomic inx_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_AUTO_INDEX_H_ diff --git a/mindspore-lite/minddata/dataset/util/bit.h b/mindspore-lite/minddata/dataset/util/bit.h deleted file mode 100644 index e4872a366..000000000 --- a/mindspore-lite/minddata/dataset/util/bit.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BIT_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BIT_H_ - -namespace mindspore { -namespace dataset { -template -Enum operator|(Enum lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -template -Enum operator&(Enum lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(lhs) & static_cast(rhs)); -} - -template -Enum operator^(Enum lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(lhs) ^ static_cast(rhs)); -} - -template -Enum &operator|=(Enum &lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - lhs = static_cast(static_cast(lhs) | static_cast(rhs)); - return lhs; -} - -template -Enum &operator&=(Enum &lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - lhs = static_cast(static_cast(lhs) & static_cast(rhs)); - return lhs; -} - -template -Enum &operator^=(Enum &lhs, Enum rhs) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - lhs = static_cast(static_cast(lhs) ^ static_cast(rhs)); - return lhs; -} - -template -Enum operator~(Enum v) { - static_assert(std::is_enum::value, "template parameter is not an enum type"); - using underlying = typename std::underlying_type::type; - return static_cast(~static_cast(v)); -} -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BIT_H_ diff --git a/mindspore-lite/minddata/dataset/util/btree.h b/mindspore-lite/minddata/dataset/util/btree.h deleted file mode 100644 index 35170da97..000000000 --- a/mindspore-lite/minddata/dataset/util/btree.h +++ /dev/null @@ -1,529 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INDEX_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INDEX_H_ - -#include -#include -#include -#include -#include -#include -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/list.h" -#include "mindspore-lite/minddata/dataset/util/lock.h" -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Default traits for a B+ tree -struct BPlusTreeTraits { - // This determines the limit of number of keys in a node. - using slot_type = uint16_t; - // Number of slots in each leaf of the tree. - static constexpr slot_type kLeafSlots = 256; - // Number of slots in each inner node of the tree - static constexpr slot_type kInnerSlots = 128; -}; - -/// Implementation of B+ tree -/// @tparam K -- the type of key -/// @tparam V -- the type of value -/// @tparam A -- allocator -/// @tparam C -- comparison class -/// @tparam T -- trait -template , typename C = std::less, - typename T = BPlusTreeTraits> -class BPlusTree { - public: - enum class IndexRc : char { - kOk = 0, - kDuplicateKey = 1, - kSlotFull = 2, - kKeyNotFound = 3, - kNullPointer = 4, - kOutOfMemory = 5, - kRetry = 6, - kUnexpectedError = 127 - }; -#define RETURN_IF_BAD_RC(_s) \ - do { \ - IndexRc __rc = (_s); \ - if (__rc != IndexRc::kOk) { \ - return __rc; \ - } \ - } while (false) - - Status IndexRc2Status(IndexRc rc) { - if (rc == IndexRc::kOk) { - return Status(StatusCode::kSuccess); - } else if (rc == IndexRc::kOutOfMemory) { - return Status(StatusCode::kMDOutOfMemory); - } else if (rc == IndexRc::kDuplicateKey) { - return Status(StatusCode::kMDDuplicateKey); - } else { - RETURN_STATUS_UNEXPECTED(std::to_string(static_cast(rc))); - } - } - - using key_type = K; - using value_type = V; - using key_compare = C; - using slot_type = typename T::slot_type; - using traits = T; - using value_allocator = A; - using key_allocator = typename value_allocator::template rebind::other; - using slot_allocator = typename value_allocator::template rebind::other; - - BPlusTree(); - - explicit BPlusTree(const Allocator &alloc); - - ~BPlusTree() noexcept; - - BPlusTree(const BPlusTree &) = delete; - - BPlusTree(BPlusTree &&) = delete; - - BPlusTree &operator=(const BPlusTree &) = delete; - - BPlusTree &operator=(BPlusTree &&) = delete; - - key_compare key_comp() const { return key_less_; } - - size_t size() const { return stats_.size_; } - - bool empty() const { return (size() == 0); } - - /// @param key - /// @param value - /// @return - Status DoInsert(const key_type &key, const value_type &value); - Status DoInsert(const key_type &key, std::unique_ptr &&value); - - // Update a new value for a given key. - std::unique_ptr DoUpdate(const key_type &key, const value_type &new_value); - std::unique_ptr DoUpdate(const key_type &key, std::unique_ptr &&new_value); - - // Statistics - struct tree_stats { - std::atomic size_; - uint32_t leaves_; - uint32_t inner_nodes_; - uint32_t level_; - - tree_stats() : size_(0), leaves_(0), inner_nodes_(0), level_(0) {} - }; - - /// \brief Statistics functions - /// \return Return the height of the tree - auto GetHeight() const { return empty() ? 0 : stats_.level_ + 1; } - /// \return Order of the B+ tree - auto GetOrder() const { return traits::kLeafSlots; } - /// \return Number of leaves nodes - auto GetNumLeaves() const { return stats_.leaves_; } - /// \return Number of inner nodes - auto GetNumInnerNodes() const { return stats_.inner_nodes_; } - - /// \brief Toggle locking - /// \note Once locking is off. It is user's responsibility to ensure concurrency - void SetLocking(bool on_off) { - UniqueLock lck(&rw_lock_); - acquire_lock_ = on_off; - } - - void LockShared() { rw_lock_.LockShared(); } - - void LockExclusive() { rw_lock_.LockExclusive(); } - - void Unlock() { rw_lock_.Unlock(); } - - private: - // Abstract class of a node (leaf or inner) - class BaseNode { - public: - friend class BPlusTree; - - virtual bool is_leafnode() const = 0; - - virtual bool is_full() const = 0; - - explicit BaseNode(const value_allocator &alloc) : alloc_(alloc) {} - - virtual ~BaseNode() = default; - - protected: - mutable RWLock rw_lock_; - value_allocator alloc_; - - private: - Node lru_; - }; - - // This control block keeps track of all the nodes we traverse on insert. - // To maximize concurrency, internal nodes are latched S. If a node split - // is required, we must releases all the latches and redo it again and change - // the latch mode from S to X. - struct LockPathCB { - enum class LockMode : char { kShared = 0, kExclusive = 1, kNone = 2 }; - - struct path { - BaseNode *node_; - bool locked_; - - path() : node_(nullptr), locked_(false) {} - - path(BaseNode *p, LockMode lockmode) : node_(p), locked_(false) { - if (lockmode == LockMode::kExclusive) { - p->rw_lock_.LockExclusive(); - locked_ = true; - } else if (lockmode == LockMode::kShared) { - p->rw_lock_.LockShared(); - locked_ = true; - } - } - }; - - LockPathCB(BPlusTree *tree, bool retryWithXlock) : self_(tree), latch_shared_(true) { - if (retryWithXlock) { - latch_shared_ = false; - } - if (latch_shared_) { - tree->rw_lock_.LockShared(); - } else { - tree->rw_lock_.LockExclusive(); - } - } - - ~LockPathCB() noexcept { - // Make sure all locks are released. - while (!paths_.empty()) { - path p = paths_.back(); - paths_.pop_back(); - if (p.locked_) { - p.node_->rw_lock_.Unlock(); - } - } - self_->rw_lock_.Unlock(); - self_ = nullptr; - } - - void LockNode(BaseNode *p, LockMode locktype) { paths_.emplace_back(p, locktype); } - - void UnlockMyParents(const BaseNode *me) { - path p = paths_.front(); - while (p.node_ != me) { - if (p.locked_) { - p.node_->rw_lock_.Unlock(); - } - paths_.pop_front(); - p = paths_.front(); - } - } - - BPlusTree *self_; - std::deque paths_; - bool latch_shared_; - }; - - // Definition of inner node which fans to either inner node or leaf node. - class InnerNode : public BaseNode { - public: - friend class BPlusTree; - - using alloc_type = typename value_allocator::template rebind::other; - - bool is_leafnode() const override { return false; } - - bool is_full() const override { return (slotuse_ == traits::kInnerSlots); } - - IndexRc Sort(); - - // 50/50 split - IndexRc Split(InnerNode *to, key_type *split_key); - - IndexRc InsertIntoSlot(slot_type slot, const key_type &key, BaseNode *ptr); - - explicit InnerNode(const value_allocator &alloc) : BaseNode::BaseNode(alloc), slotuse_(0) {} - - ~InnerNode() = default; - - slot_type slot_dir_[traits::kInnerSlots] = {0}; - key_type keys_[traits::kInnerSlots] = {0}; - BaseNode *data_[traits::kInnerSlots + 1] = {nullptr}; - slot_type slotuse_; - }; - - // Definition of a leaf node which contains the key/value pair - class LeafNode : public BaseNode { - public: - friend class BPlusTree; - - using alloc_type = typename value_allocator::template rebind::other; - Node link_; - - bool is_leafnode() const override { return true; } - - bool is_full() const override { return (slotuse_ == traits::kLeafSlots); } - - IndexRc Sort(); - - // 50/50 split - IndexRc Split(LeafNode *to); - - IndexRc InsertIntoSlot(LockPathCB *insCB, slot_type slot, const key_type &key, std::unique_ptr &&value); - - explicit LeafNode(const value_allocator &alloc) : BaseNode::BaseNode(alloc), slotuse_(0) {} - - ~LeafNode() = default; - - slot_type slot_dir_[traits::kLeafSlots] = {0}; - key_type keys_[traits::kLeafSlots] = {0}; - std::unique_ptr data_[traits::kLeafSlots]; - slot_type slotuse_; - }; - - mutable RWLock rw_lock_; - value_allocator alloc_; - // All the leaf nodes. Used by the iterator to traverse all the key/values. - List leaf_nodes_; - // All the nodes (inner + leaf). Used by the destructor to free the memory of all the nodes. - List all_; - // Pointer to the root of the tree. - BaseNode *root_; - // Key comparison object - key_compare key_less_; - // Stat - tree_stats stats_; - // lock mode - bool acquire_lock_; - - void Init() { - typename LeafNode::alloc_type alloc(alloc_); - LeafNode *p = nullptr; - try { - p = alloc.allocate(1); - } catch (std::bad_alloc &e) { - p = nullptr; - return; - } - root_ = new (p) LeafNode(alloc_); - all_.Prepend(p); - leaf_nodes_.Append(p); - stats_.leaves_++; - } - - bool LessThan(const key_type &a, const key_type &b) const { return key_less_(a, b); } - - bool EqualOrLessThan(const key_type &a, const key_type &b) const { return !key_less_(b, a); } - - bool Equal(const key_type &a, const key_type &b) const { return !key_less_(a, b) && !key_less_(b, a); } - - IndexRc AllocateInner(InnerNode **p); - - IndexRc AllocateLeaf(LeafNode **p); - - template - slot_type FindSlot(const node_type *node, const key_type &key, bool *duplicate = nullptr) const { - slot_type lo = 0; - while (lo < node->slotuse_ && key_comp()(node->keys_[node->slot_dir_[lo]], key)) { - ++lo; - } - bool keymatch = (lo < node->slotuse_ && Equal(key, node->keys_[node->slot_dir_[lo]])); - if (keymatch && !node->is_leafnode()) { - // For an inner node and we match a key during search, we should look into the next slot. - ++lo; - } - if (duplicate != nullptr) { - *duplicate = keymatch; - } - return lo; - } - - IndexRc LeafInsertKeyValue(LockPathCB *ins_cb, LeafNode *node, const key_type &key, - std::unique_ptr &&value, key_type *split_key, LeafNode **split_node); - - IndexRc InnerInsertKeyChild(InnerNode *node, const key_type &key, BaseNode *ptr, key_type *split_key, - InnerNode **split_node); - - inline BaseNode *FindBranch(InnerNode *inner, slot_type slot) const { - BaseNode *child = nullptr; - if (slot == 0) { - child = inner->data_[0]; - } else { - child = inner->data_[inner->slot_dir_[slot - 1] + 1]; - } - return child; - } - - IndexRc InsertKeyValue(LockPathCB *ins_cb, BaseNode *n, const key_type &key, std::unique_ptr &&value, - key_type *split_key, BaseNode **split_node); - - IndexRc Locate(RWLock *parent_lock, bool forUpdate, BaseNode *top, const key_type &key, LeafNode **ln, - slot_type *s) const; - - public: - class Iterator : public std::iterator { - public: - using reference = BPlusTree::value_type &; - using pointer = BPlusTree::value_type *; - - explicit Iterator(BPlusTree *btree) : cur_(btree->leaf_nodes_.head), slot_(0), locked_(false) {} - - Iterator(LeafNode *leaf, slot_type slot, bool locked = false) : cur_(leaf), slot_(slot), locked_(locked) {} - - ~Iterator(); - - explicit Iterator(const Iterator &); - - Iterator &operator=(const Iterator &lhs); - - explicit Iterator(Iterator &&) noexcept; - - Iterator &operator=(Iterator &&lhs); - - pointer operator->() const { return cur_->data_[cur_->slot_dir_[slot_]].get(); } - - reference operator*() const { return *(cur_->data_[cur_->slot_dir_[slot_]].get()); } - - const key_type &key() const { return cur_->keys_[cur_->slot_dir_[slot_]]; } - - value_type &value() const { return *(cur_->data_[cur_->slot_dir_[slot_]].get()); } - - // Prefix++ - Iterator &operator++(); - - // Postfix++ - Iterator operator++(int); - - // Prefix-- - Iterator &operator--(); - - // Postfix-- - Iterator operator--(int); - - bool operator==(const Iterator &x) const { return (x.cur_ == cur_) && (x.slot_ == slot_); } - bool operator!=(const Iterator &x) const { return (x.cur_ != cur_) || (x.slot_ != slot_); } - - void LockShared() { - cur_->rw_lock_.LockShared(); - locked_ = true; - } - - void LockExclusive() { - cur_->rw_lock_.LockExclusive(); - locked_ = true; - } - - void Unlock() { - cur_->rw_lock_.Unlock(); - locked_ = false; - } - - private: - typename BPlusTree::LeafNode *cur_; - slot_type slot_; - bool locked_; - }; - - class ConstIterator : public std::iterator { - public: - using reference = BPlusTree::value_type &; - using pointer = BPlusTree::value_type *; - - explicit ConstIterator(const BPlusTree *btree) : cur_(btree->leaf_nodes_.head), slot_(0), locked_(false) {} - - ~ConstIterator(); - - ConstIterator(const LeafNode *leaf, slot_type slot, bool locked = false) - : cur_(leaf), slot_(slot), locked_(locked) {} - - explicit ConstIterator(const ConstIterator &); - - ConstIterator &operator=(const ConstIterator &lhs); - - explicit ConstIterator(ConstIterator &&) noexcept; - - ConstIterator &operator=(ConstIterator &&lhs); - - pointer operator->() const { return cur_->data_[cur_->slot_dir_[slot_]].get(); } - - reference operator*() const { return *(cur_->data_[cur_->slot_dir_[slot_]].get()); } - - const key_type &key() const { return cur_->keys_[cur_->slot_dir_[slot_]]; } - - value_type &value() const { return *(cur_->data_[cur_->slot_dir_[slot_]].get()); } - - // Prefix++ - ConstIterator &operator++(); - - // Postfix++ - ConstIterator operator++(int); - - // Prefix-- - ConstIterator &operator--(); - - // Postfix-- - ConstIterator operator--(int); - - bool operator==(const ConstIterator &x) const { return (x.cur_ == cur_) && (x.slot_ == slot_); } - bool operator!=(const ConstIterator &x) const { return (x.cur_ != cur_) || (x.slot_ != slot_); } - - void LockShared() { - cur_->rw_lock_.LockShared(); - locked_ = true; - } - - void LockExclusive() { - cur_->rw_lock_.LockExclusive(); - locked_ = true; - } - - void Unlock() { - cur_->rw_lock_.Unlock(); - locked_ = false; - } - - private: - const typename BPlusTree::LeafNode *cur_; - slot_type slot_; - bool locked_; - }; - - Iterator begin(); - Iterator end(); - - ConstIterator begin() const; - ConstIterator end() const; - - ConstIterator cbegin() const; - ConstIterator cend() const; - - // Locate the entry with key - std::pair Search(const key_type &key) const; - std::pair Search(const key_type &key); - - value_type operator[](key_type key); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INDEX_H_ - -#include "mindspore-lite/minddata/dataset/util/btree_impl.tpp" -#include "mindspore-lite/minddata/dataset/util/btree_iterator.tpp" diff --git a/mindspore-lite/minddata/dataset/util/btree_impl.tpp b/mindspore-lite/minddata/dataset/util/btree_impl.tpp deleted file mode 100644 index ec1d03095..000000000 --- a/mindspore-lite/minddata/dataset/util/btree_impl.tpp +++ /dev/null @@ -1,533 +0,0 @@ -/* Copyright 2019 Huawei Technologies Co., Ltd.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. - */ -#ifndef DATASET_UTIL_BTREE_H_ -#define DATASET_UTIL_BTREE_H_ - -#include "mindspore-lite/minddata/dataset/util/btree.h" - -namespace mindspore { -namespace dataset { -template -typename BPlusTree::IndexRc BPlusTree::InnerNode::Sort() { - // Build an inverse map. Basically it means keys[i] should be relocated to keys[inverse[i]]; - slot_allocator alloc(this->alloc_); - try { - // We use a unique_ptr will custom deleter to ensure the memory will be released when this - // function returns. - std::unique_ptr> memGuard( - alloc.allocate(traits::kInnerSlots), [&alloc](slot_type *p) { alloc.deallocate(p, traits::kInnerSlots); }); - slot_type *inverse = memGuard.get(); - for (slot_type i = 0; i < slotuse_; i++) { - inverse[slot_dir_[i]] = i; - } - for (slot_type i = 0; i < slotuse_; i++) { - while (inverse[i] != i) { - slot_type j = inverse[i]; - slot_type k = inverse[j]; - // Swap the key - std::swap(keys_[j], keys_[i]); - // Swap the pointers. - if ((j + 1) >= traits::kInnerSlots + 1 || (i + 1) >= traits::kInnerSlots + 1) { - return IndexRc::kUnexpectedError; - } - std::swap(data_[j + 1], data_[i + 1]); - // one key in order. - inverse[j] = j; - // continue to move - inverse[i] = k; - } - slot_dir_[i] = i; - } - return IndexRc::kOk; - } catch (std::bad_alloc &e) { - return IndexRc::kOutOfMemory; - } catch (std::exception &e) { - return IndexRc::kUnexpectedError; - } -} - -template -typename BPlusTree::IndexRc BPlusTree::InnerNode::Split( - BPlusTree::InnerNode *to, key_type *split_key) { - MS_ASSERT(to); - MS_ASSERT(to->slotuse_ == 0); - // It is simpler to sort first, then split. Other alternative is to move key by key to the - // new node. Also we need to deal with the 'holes' after a key is moved. - RETURN_IF_BAD_RC(this->Sort()); - slot_type mid = slotuse_ >> 1; - slot_type num_keys_to_move = slotuse_ - (mid + 1); - *split_key = keys_[mid]; - errno_t err = memmove_s(to->keys_, sizeof(to->keys_), keys_ + mid + 1, num_keys_to_move * sizeof(key_type)); - if (err != EOK) { - return IndexRc::kUnexpectedError; - } - err = memcpy_s(to->data_, sizeof(to->data_), data_ + mid + 1, (num_keys_to_move + 1) * sizeof(BaseNode *)); - if (err != EOK) { - return IndexRc::kUnexpectedError; - } - for (slot_type i = 0; i < num_keys_to_move; i++) { - to->slot_dir_[i] = i; - } - slotuse_ -= (num_keys_to_move + 1); // the split key is moved up. So one less - to->slotuse_ += num_keys_to_move; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::InnerNode::InsertIntoSlot( - slot_type slot, const key_type &key, BPlusTree::BaseNode *ptr) { - if (is_full()) { - return IndexRc::kSlotFull; - } - // Shift the slot entries to the right and make room for the new comer. - // We don't sort the key and/or the data array until node split - auto num_keys_to_move = slotuse_ - slot; - if (num_keys_to_move > 0) { - auto *src = &slot_dir_[slot]; - auto *dest = &slot_dir_[slot + 1]; - auto destMax = sizeof(slot_dir_) - sizeof(slot_type) * (slot + 1); - auto amt = sizeof(slot_type) * num_keys_to_move; - errno_t err = memmove_s(dest, destMax, src, amt); - if (err != EOK) { - return IndexRc::kUnexpectedError; - } - } - slot_dir_[slot] = slotuse_; - keys_[slotuse_] = key; - data_[slotuse_ + 1] = ptr; - ++slotuse_; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::LeafNode::Sort() { - // Build an inverse map. Basically it means keys[i] should be relocated to keys[inverse[i]]; - slot_allocator alloc(this->alloc_); - try { - // We use a unique_ptr will custom deleter to ensure the memory will be released when this - // function returns. - std::unique_ptr> memGuard( - alloc.allocate(traits::kLeafSlots), [&alloc](slot_type *p) { alloc.deallocate(p, traits::kLeafSlots); }); - slot_type *inverse = memGuard.get(); - for (slot_type i = 0; i < slotuse_; i++) { - inverse[slot_dir_[i]] = i; - } - for (slot_type i = 0; i < slotuse_; i++) { - while (inverse[i] != i) { - slot_type j = inverse[i]; - slot_type k = inverse[j]; - // Swap the key - if (j >= traits::kLeafSlots || i >= traits::kLeafSlots) { - return IndexRc::kUnexpectedError; - } - std::swap(keys_[j], keys_[i]); - // Swap the shared pointers - std::swap(data_[j], data_[i]); - // one key in order. - inverse[j] = j; - // continue to move - inverse[i] = k; - } - slot_dir_[i] = i; - } - return IndexRc::kOk; - } catch (std::bad_alloc &e) { - return IndexRc::kOutOfMemory; - } catch (std::exception &e) { - return IndexRc::kUnexpectedError; - } -} - -template -typename BPlusTree::IndexRc BPlusTree::LeafNode::Split( - BPlusTree::LeafNode *to) { - MS_ASSERT(to); - MS_ASSERT(to->slotuse_ == 0); - // It is simpler to sort first, then split. Other alternative is to move key by key to the - // new node. Also we need to deal with the 'holes' after a key is moved. - RETURN_IF_BAD_RC(this->Sort()); - slot_type mid = slotuse_ >> 1; - slot_type num_keys_to_move = slotuse_ - mid; - errno_t err = memmove_s(to->keys_, sizeof(to->keys_), keys_ + mid, num_keys_to_move * sizeof(key_type)); - if (err != EOK) { - return IndexRc::kUnexpectedError; - } - for (slot_type i = 0; i < num_keys_to_move; i++) { - to->data_[i] = std::move(data_[i + mid]); - to->slot_dir_[i] = i; - } - slotuse_ -= num_keys_to_move; - to->slotuse_ += num_keys_to_move; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::LeafNode::InsertIntoSlot( - BPlusTree::LockPathCB *insCB, slot_type slot, const key_type &key, - std::unique_ptr &&value) { - if (is_full()) { - // If we need to do node split, we need to ensure all the intermediate nodes are locked exclusive. - // Otherwise we need to do a retry. - if (insCB == nullptr || !insCB->latch_shared_) { - return IndexRc::kSlotFull; - } else { - return IndexRc::kRetry; - } - } - // We can now let go all the locks of the parent. Nothing we do from now on will change the - // structure of the tree. - if (insCB) { - insCB->UnlockMyParents(this); - } - // Shift the slot entries to the right and make room for the new comer. - // We don't sort the key and/or the data array until node split - auto num_keys_to_move = slotuse_ - slot; - if (num_keys_to_move > 0) { - auto *src = &slot_dir_[slot]; - auto *dest = &slot_dir_[slot + 1]; - auto destMax = sizeof(slot_dir_) - sizeof(slot_type) * (slot + 1); - auto amt = sizeof(slot_type) * num_keys_to_move; - errno_t err = memmove_s(dest, destMax, src, amt); - if (err != EOK) { - return IndexRc::kUnexpectedError; - } - } - slot_dir_[slot] = slotuse_; - keys_[slotuse_] = key; - data_[slotuse_] = std::move(value); - ++slotuse_; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::AllocateInner( - BPlusTree::InnerNode **p) { - if (p == nullptr) { - return IndexRc::kNullPointer; - } - typename InnerNode::alloc_type alloc(alloc_); - InnerNode *ptr = nullptr; - try { - ptr = alloc.allocate(1); - } catch (std::bad_alloc &e) { - return IndexRc::kOutOfMemory; - } catch (std::exception &e) { - return IndexRc::kUnexpectedError; - } - *p = new (ptr) InnerNode(alloc_); - all_.Prepend(ptr); - stats_.inner_nodes_++; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::AllocateLeaf( - BPlusTree::LeafNode **p) { - if (p == nullptr) { - return IndexRc::kNullPointer; - } - typename LeafNode::alloc_type alloc(this->alloc_); - LeafNode *ptr = nullptr; - try { - ptr = alloc.allocate(1); - } catch (std::bad_alloc &e) { - return IndexRc::kOutOfMemory; - } catch (std::exception &e) { - return IndexRc::kUnexpectedError; - } - *p = new (ptr) LeafNode(alloc_); - all_.Prepend(ptr); - stats_.leaves_++; - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::LeafInsertKeyValue( - BPlusTree::LockPathCB *ins_cb, BPlusTree::LeafNode *node, const key_type &key, - std::unique_ptr &&value, key_type *split_key, BPlusTree::LeafNode **split_node) { - bool duplicate; - slot_type slot = FindSlot(node, key, &duplicate); - if (duplicate) { - return IndexRc::kDuplicateKey; - } - IndexRc rc = node->InsertIntoSlot(ins_cb, slot, key, std::move(value)); - if (rc == IndexRc::kSlotFull) { - LeafNode *new_leaf = nullptr; - rc = AllocateLeaf(&new_leaf); - RETURN_IF_BAD_RC(rc); - leaf_nodes_.InsertAfter(node, new_leaf); - *split_node = new_leaf; - // 50/50 split - rc = node->Split(new_leaf); - RETURN_IF_BAD_RC(rc); - *split_key = new_leaf->keys_[0]; - if (LessThan(key, *split_key)) { - rc = node->InsertIntoSlot(nullptr, slot, key, std::move(value)); - RETURN_IF_BAD_RC(rc); - } else { - slot -= node->slotuse_; - rc = new_leaf->InsertIntoSlot(nullptr, slot, key, std::move(value)); - RETURN_IF_BAD_RC(rc); - } - } - return rc; -} - -template -typename BPlusTree::IndexRc BPlusTree::InnerInsertKeyChild( - BPlusTree::InnerNode *node, const key_type &key, BPlusTree::BaseNode *ptr, - key_type *split_key, BPlusTree::InnerNode **split_node) { - bool duplicate; - slot_type slot = FindSlot(node, key, &duplicate); - if (duplicate) { - return IndexRc::kDuplicateKey; - } - IndexRc rc = node->InsertIntoSlot(slot, key, ptr); - if (rc == IndexRc::kSlotFull) { - InnerNode *new_inner = nullptr; - rc = AllocateInner(&new_inner); - RETURN_IF_BAD_RC(rc); - *split_node = new_inner; - rc = node->Split(new_inner, split_key); - RETURN_IF_BAD_RC(rc); - if (LessThan(key, *split_key)) { - // Need to readjust the slot position since the split key is no longer in the two children. - slot = FindSlot(node, key); - rc = node->InsertIntoSlot(slot, key, ptr); - RETURN_IF_BAD_RC(rc); - } else { - // Same reasoning as above - slot = FindSlot(new_inner, key); - rc = new_inner->InsertIntoSlot(slot, key, ptr); - RETURN_IF_BAD_RC(rc); - } - } - return rc; -} - -template -typename BPlusTree::IndexRc BPlusTree::InsertKeyValue( - BPlusTree::LockPathCB *ins_cb, BPlusTree::BaseNode *n, const key_type &key, - std::unique_ptr &&value, key_type *split_key, BPlusTree::BaseNode **split_node) { - if (split_key == nullptr || split_node == nullptr) { - return IndexRc::kUnexpectedError; - } - if (n->is_leafnode()) { - if (ins_cb) { - // Always lock the leaf in X. - ins_cb->LockNode(n, LockPathCB::LockMode::kExclusive); - } - auto *leaf = static_cast(n); - LeafNode *new_leaf = nullptr; - RETURN_IF_BAD_RC(LeafInsertKeyValue(ins_cb, leaf, key, std::move(value), split_key, &new_leaf)); - if (new_leaf) { - *split_node = new_leaf; - } - } else { - if (ins_cb) { - // For internal node, lock in S unless we are doing retry. - if (ins_cb->latch_shared_) { - ins_cb->LockNode(n, LockPathCB::LockMode::kShared); - } else { - ins_cb->LockNode(n, LockPathCB::LockMode::kExclusive); - } - } - auto *inner = static_cast(n); - slot_type slot = FindSlot(inner, key); - BaseNode *new_child = nullptr; - key_type new_key = key_type(); - RETURN_IF_BAD_RC(InsertKeyValue(ins_cb, FindBranch(inner, slot), key, std::move(value), &new_key, &new_child)); - if (new_child) { - InnerNode *new_inner = nullptr; - RETURN_IF_BAD_RC(InnerInsertKeyChild(inner, new_key, new_child, split_key, &new_inner)); - if (new_inner) { - *split_node = new_inner; - } - } - } - return IndexRc::kOk; -} - -template -typename BPlusTree::IndexRc BPlusTree::Locate(RWLock *parent_lock, bool forUpdate, - BPlusTree::BaseNode *top, - const key_type &key, - BPlusTree::LeafNode **ln, - slot_type *s) const { - if (ln == nullptr || s == nullptr) { - return IndexRc::kNullPointer; - } - if (top == nullptr) { - return IndexRc::kKeyNotFound; - } - RWLock *myLock = nullptr; - if (parent_lock != nullptr) { - // Crabbing. Lock this node first, then unlock the parent. - myLock = &top->rw_lock_; - if (top->is_leafnode()) { - if (forUpdate) { - // We are holding the parent lock in S and try to lock this node with X. It is not possible to run - // into deadlock because no one will hold the child in X and trying to lock the parent in that order. - myLock->LockExclusive(); - } else { - myLock->LockShared(); - } - } else { - myLock->LockShared(); - } - parent_lock->Unlock(); - } - if (top->is_leafnode()) { - bool duplicate; - auto *leaf = static_cast(top); - slot_type slot = FindSlot(leaf, key, &duplicate); - // Need exact match. - if (duplicate) { - *ln = leaf; - *s = slot; - } else { - if (myLock != nullptr) { - myLock->Unlock(); - } - return IndexRc::kKeyNotFound; - } - } else { - auto *inner = static_cast(top); - slot_type slot = FindSlot(inner, key); - return Locate(myLock, forUpdate, FindBranch(inner, slot), key, ln, s); - } - // We still have a S lock on the leaf node. Leave it there. The iterator will unlock it for us. - return IndexRc::kOk; -} - -template -BPlusTree::BPlusTree() - : leaf_nodes_(&LeafNode::link_), all_(&BaseNode::lru_), root_(nullptr), acquire_lock_(true) { - Init(); -} - -template -BPlusTree::BPlusTree(const Allocator &alloc) - : alloc_(alloc), leaf_nodes_(&LeafNode::link_), all_(&BaseNode::lru_), root_(nullptr), acquire_lock_(true) { - Init(); -} - -template -BPlusTree::~BPlusTree() noexcept { - // We have a list of all the nodes allocated. Traverse them and free all the memory - BaseNode *n = all_.head; - BaseNode *t = nullptr; - while (n) { - t = n->lru_.next; - all_.Remove(n); - if (n->is_leafnode()) { - auto *leaf = static_cast(n); - typename LeafNode::alloc_type alloc(alloc_); - leaf->~LeafNode(); - alloc.deallocate(leaf, 1); - } else { - auto *in = static_cast(n); - typename InnerNode::alloc_type alloc(alloc_); - in->~InnerNode(); - alloc.deallocate(in, 1); - } - n = t; - } - root_ = nullptr; -} - -template -Status BPlusTree::DoInsert(const key_type &key, std::unique_ptr &&value) { - IndexRc rc; - bool retry = false; - do { - // Track all the paths to the target and lock each internal node in S. - LockPathCB InsCB(this, retry); - // Initially we lock path in S unless we need to do node split. - retry = false; - BaseNode *new_child = nullptr; - key_type new_key = key_type(); - rc = InsertKeyValue(acquire_lock_ ? &InsCB : nullptr, root_, key, std::move(value), &new_key, &new_child); - if (rc == IndexRc::kRetry) { - retry = true; - } else if (rc != IndexRc::kOk) { - return IndexRc2Status(rc); - } else if (new_child != nullptr) { - // root is full - InnerNode *new_root = nullptr; - rc = AllocateInner(&new_root); - if (rc == IndexRc::kOk) { - rc = new_root->InsertIntoSlot(0, new_key, new_child); - if (rc != IndexRc::kOk) { - return IndexRc2Status(rc); - } - new_root->data_[0] = root_; - root_ = new_root; - stats_.level_++; - } else { - return IndexRc2Status(rc); - } - } - } while (retry); - (void)stats_.size_++; - return Status::OK(); -} - -template -Status BPlusTree::DoInsert(const key_type &key, const value_type &value) { - // We don't store the value directly into the leaf node as it is expensive to move it during node split. - // Rather we store a pointer instead. - return DoInsert(key, std::make_unique(value)); -} - -template -std::unique_ptr BPlusTree::DoUpdate(const key_type &key, const value_type &new_value) { - return DoUpdate(key, std::make_unique(new_value)); -} - -template -std::unique_ptr BPlusTree::DoUpdate(const key_type &key, std::unique_ptr &&new_value) { - if (root_ != nullptr) { - LeafNode *leaf = nullptr; - slot_type slot; - RWLock *myLock = nullptr; - if (acquire_lock_) { - myLock = &this->rw_lock_; - // Lock the tree in S, pass the lock to Locate which will unlock it for us underneath. - myLock->LockShared(); - } - IndexRc rc = Locate(myLock, true, root_, key, &leaf, &slot); - if (rc == IndexRc::kOk) { - // All locks from the tree to the parent of leaf are all gone. We still have a X lock - // on the leaf. - // Swap out the old value and replace it with new value. - std::unique_ptr old = std::move(leaf->data_[leaf->slot_dir_[slot]]); - leaf->data_[leaf->slot_dir_[slot]] = std::move(new_value); - if (acquire_lock_) { - leaf->rw_lock_.Unlock(); - } - return old; - } else { - MS_LOG(DEBUG) << "Key not found. rc = " << static_cast(rc) << "."; - return nullptr; - } - } else { - return nullptr; - } -} - -} // namespace dataset -} // namespace mindspore -#endif diff --git a/mindspore-lite/minddata/dataset/util/btree_iterator.tpp b/mindspore-lite/minddata/dataset/util/btree_iterator.tpp deleted file mode 100644 index b15d0bef7..000000000 --- a/mindspore-lite/minddata/dataset/util/btree_iterator.tpp +++ /dev/null @@ -1,364 +0,0 @@ -/* COPYRIGHT 2019 Huawei Technologies Co., Ltd.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. - */ -#ifndef DATASET_UTIL_BTREE_ITERATOR_H_ -#define DATASET_UTIL_BTREE_ITERATOR_H_ - -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/btree.h" - -namespace mindspore { -namespace dataset { -template -BPlusTree::Iterator::~Iterator() { - if (locked_) { - cur_->rw_lock_.Unlock(); - locked_ = false; - } -} - -template -typename BPlusTree::Iterator &BPlusTree::Iterator::operator++() { - if (slot_ + 1u < cur_->slotuse_) { - ++slot_; - } else if (cur_->link_.next) { - if (locked_) { - cur_->link_.next->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.next; - slot_ = 0; - } else { - slot_ = cur_->slotuse_; - } - return *this; -} - -template -typename BPlusTree::Iterator BPlusTree::Iterator::operator++(int) { - Iterator tmp = *this; - if (slot_ + 1u < cur_->slotuse_) { - ++slot_; - } else if (cur_->link_.next) { - if (locked_) { - cur_->link_.next->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.next; - slot_ = 0; - } else { - slot_ = cur_->slotuse_; - } - return tmp; -} - -template -typename BPlusTree::Iterator &BPlusTree::Iterator::operator--() { - if (slot_ > 0) { - --slot_; - } else if (cur_->link_.prev) { - if (locked_) { - cur_->link_.prev->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.prev; - slot_ = cur_->slotuse_ - 1; - } else { - slot_ = 0; - } - return *this; -} - -template -typename BPlusTree::Iterator BPlusTree::Iterator::operator--(int) { - Iterator tmp = *this; - if (slot_ > 0) { - --slot_; - } else if (cur_->link_.prev) { - if (locked_) { - cur_->link_.prev->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.prev; - slot_ = cur_->slotuse_ - 1; - } else { - slot_ = 0; - } - return tmp; -} - -template -BPlusTree::Iterator::Iterator(const BPlusTree::Iterator &lhs) { - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - if (this->locked_) { - this->cur_->rw_lock_.LockShared(); - } -} - -template -BPlusTree::Iterator::Iterator(BPlusTree::Iterator &&lhs) noexcept { - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - lhs.locked_ = false; - lhs.slot_ = 0; - lhs.cur_ = nullptr; -} - -template -typename BPlusTree::Iterator &BPlusTree::Iterator::operator=( - const BPlusTree::Iterator &lhs) { - if (*this != lhs) { - if (this->locked_) { - this->cur_->rw_lock_.Unlock(); - } - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - if (this->locked_) { - this->cur_->rw_lock_.LockShared(); - } - } - return *this; -} - -template -typename BPlusTree::Iterator &BPlusTree::Iterator::operator=( - BPlusTree::Iterator &&lhs) { - if (*this != lhs) { - if (this->locked_) { - this->cur_->rw_lock_.Unlock(); - } - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - lhs.locked_ = false; - lhs.slot_ = 0; - lhs.cur_ = nullptr; - } - return *this; -} - -template -BPlusTree::ConstIterator::~ConstIterator() { - if (locked_) { - cur_->rw_lock_.Unlock(); - locked_ = false; - } -} - -template -typename BPlusTree::ConstIterator &BPlusTree::ConstIterator::operator++() { - if (slot_ + 1u < cur_->slotuse_) { - ++slot_; - } else if (cur_->link_.next) { - if (locked_) { - cur_->link_.next->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.next; - slot_ = 0; - } else { - slot_ = cur_->slotuse_; - } - return *this; -} - -template -typename BPlusTree::ConstIterator BPlusTree::ConstIterator::operator++(int) { - Iterator tmp = *this; - if (slot_ + 1u < cur_->slotuse_) { - ++slot_; - } else if (cur_->link_.next) { - if (locked_) { - cur_->link_.next->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.next; - slot_ = 0; - } else { - slot_ = cur_->slotuse_; - } - return tmp; -} - -template -typename BPlusTree::ConstIterator &BPlusTree::ConstIterator::operator--() { - if (slot_ > 0) { - --slot_; - } else if (cur_->link_.prev) { - if (locked_) { - cur_->link_.prev->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.prev; - slot_ = cur_->slotuse_ - 1; - } else { - slot_ = 0; - } - return *this; -} - -template -typename BPlusTree::ConstIterator BPlusTree::ConstIterator::operator--(int) { - Iterator tmp = *this; - if (slot_ > 0) { - --slot_; - } else if (cur_->link_.prev) { - if (locked_) { - cur_->link_.prev->rw_lock_.LockShared(); - cur_->rw_lock_.Unlock(); - } - cur_ = cur_->link_.prev; - slot_ = cur_->slotuse_ - 1; - } else { - slot_ = 0; - } - return tmp; -} - -template -BPlusTree::ConstIterator::ConstIterator(const BPlusTree::ConstIterator &lhs) { - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - if (this->locked_) { - this->cur_->rw_lock_.LockShared(); - } -} - -template -BPlusTree::ConstIterator::ConstIterator(BPlusTree::ConstIterator &&lhs) noexcept { - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - lhs.locked_ = false; - lhs.slot_ = 0; - lhs.cur_ = nullptr; -} - -template -typename BPlusTree::ConstIterator &BPlusTree::ConstIterator::operator=( - const BPlusTree::ConstIterator &lhs) { - if (*this != lhs) { - if (this->locked_) { - this->cur_->rw_lock_.Unlock(); - } - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - if (this->locked_) { - this->cur_->rw_lock_.LockShared(); - } - } - return *this; -} - -template -typename BPlusTree::ConstIterator &BPlusTree::ConstIterator::operator=( - BPlusTree::ConstIterator &&lhs) { - if (*this != lhs) { - if (this->locked_) { - this->cur_->rw_lock_.Unlock(); - } - this->cur_ = lhs.cur_; - this->slot_ = lhs.slot_; - this->locked_ = lhs.locked_; - lhs.locked_ = false; - lhs.slot_ = 0; - lhs.cur_ = nullptr; - } - return *this; -} - -template -std::pair::ConstIterator, bool> BPlusTree::Search( - const key_type &key) const { - if (root_ != nullptr) { - LeafNode *leaf = nullptr; - slot_type slot; - RWLock *myLock = nullptr; - if (acquire_lock_) { - myLock = &this->rw_lock_; - // Lock the tree in S, pass the lock to Locate which will unlock it for us underneath. - myLock->LockShared(); - } - IndexRc rc = Locate(myLock, false, root_, key, &leaf, &slot); - bool find = (rc == IndexRc::kOk); - return std::make_pair(ConstIterator(leaf, slot, find), find); - } else { - return std::make_pair(cend(), false); - } -} - -template -std::pair::Iterator, bool> BPlusTree::Search(const key_type &key) { - if (root_ != nullptr) { - LeafNode *leaf = nullptr; - slot_type slot; - RWLock *myLock = nullptr; - if (acquire_lock_) { - myLock = &this->rw_lock_; - // Lock the tree in S, pass the lock to Locate which will unlock it for us underneath. - myLock->LockShared(); - } - IndexRc rc = Locate(myLock, false, root_, key, &leaf, &slot); - bool find = (rc == IndexRc::kOk); - return std::make_pair(Iterator(leaf, slot, find), find); - } else { - return std::make_pair(end(), false); - } -} - -template -typename BPlusTree::value_type BPlusTree::operator[](key_type key) { - auto r = Search(key); - return r.first.value(); -} - -template -typename BPlusTree::Iterator BPlusTree::begin() { - return Iterator(this); -} - -template -typename BPlusTree::Iterator BPlusTree::end() { - return Iterator(this->leaf_nodes_.tail, this->leaf_nodes_.tail ? this->leaf_nodes_.tail->slotuse_ : 0); -} - -template -typename BPlusTree::ConstIterator BPlusTree::begin() const { - return ConstIterator(this); -} - -template -typename BPlusTree::ConstIterator BPlusTree::end() const { - return ConstIterator(this->leaf_nodes_.tail, this->leaf_nodes_.tail ? this->leaf_nodes_.tail->slotuse_ : 0); -} - -template -typename BPlusTree::ConstIterator BPlusTree::cbegin() const { - return ConstIterator(this); -} - -template -typename BPlusTree::ConstIterator BPlusTree::cend() const { - return ConstIterator(this->leaf_nodes_.tail, this->leaf_nodes_.tail ? this->leaf_nodes_.tail->slotuse_ : 0); -} -} // namespace dataset -} // namespace mindspore -#endif diff --git a/mindspore-lite/minddata/dataset/util/buddy.cc b/mindspore-lite/minddata/dataset/util/buddy.cc deleted file mode 100644 index a993a4145..000000000 --- a/mindspore-lite/minddata/dataset/util/buddy.cc +++ /dev/null @@ -1,415 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/buddy.h" -#include -#include - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -inline uint64_t BitLeftShift(uint64_t v, uint64_t n) { return (v << n); } - -inline uint64_t BitRightShift(uint64_t v, uint64_t n) { return (v >> n); } - -inline uint64_t BitOr(uint64_t rhs, uint64_t lhs) { return rhs | lhs; } - -inline uint64_t BitEx(uint64_t rhs, uint64_t lhs) { return rhs ^ lhs; } - -inline uint64_t BitAnd(uint64_t rhs, uint64_t lhs) { return rhs & lhs; } - -namespace mindspore { -namespace dataset { -Status BuddySpace::Init() { - const uint64_t kBitOffset = 3; - const int kLvlMin = 3; - const int kLvlMax = 18; - if (log_min_ < 0) { - RETURN_STATUS_UNEXPECTED("log_min must be positive, but got: " + std::to_string(log_min_)); - } - if (num_lvl_ < kLvlMin || num_lvl_ > kLvlMax) { - RETURN_STATUS_UNEXPECTED("num_lvl must be between 3 and 18, but got: " + std::to_string(num_lvl_)); - } - min_ = BitLeftShift(1, log_min_); - max_ = BitLeftShift(1, log_min_ + num_lvl_ - 1); - size_t offset_1 = sizeof(rel_addr_t) * num_lvl_; - size_t offset_2 = sizeof(int) * num_lvl_ + offset_1; - size_t offset_3 = sizeof(char) * BitLeftShift(1, static_cast(num_lvl_ - kBitOffset)) + offset_2; - try { - mem_ = std::make_unique(offset_3); - } catch (const std::bad_alloc &e) { - RETURN_STATUS_OOM("Out of memory."); - } - CHECK_FAIL_RETURN_UNEXPECTED(memset_s(mem_.get(), offset_3, 0, offset_3) == EOK, "Failed to init memory to zero."); - auto ptr = mem_.get(); - hint_ = reinterpret_cast(ptr); - count_ = reinterpret_cast((reinterpret_cast(ptr) + offset_1)); - map_ = reinterpret_cast(ptr) + offset_2; - count_[num_lvl_ - 1] = 1; - map_[0] = BitOr(MORE_BIT, static_cast(num_lvl_ - kBitOffset)); - return Status::OK(); -} - -Status BuddySpace::Alloc(const uint64_t sz, BSpaceDescriptor *desc, addr_t *p) noexcept { - RETURN_UNEXPECTED_IF_NULL(desc); - RETURN_UNEXPECTED_IF_NULL(p); - std::lock_guard lock(mutex_); - addr_t addr = AllocNoLock(sz, desc); - if (addr != NOSPACE) { - *p = addr; - return Status::OK(); - } else { - RETURN_STATUS_ERROR(StatusCode::kMDBuddySpaceFull, "BuddySpace full. Not an error. Please ignore."); - } -} - -addr_t BuddySpace::AllocNoLock(const uint64_t sz, BSpaceDescriptor *desc) noexcept { - MS_ASSERT(sz <= max_); - uint32_t reqSize = SizeToBlock(sz); - rel_addr_t rel_addr = AllocBuddySeg(reqSize); - if (rel_addr != static_cast(NOSPACE)) { - if (memset_s(desc, sizeof(BSpaceDescriptor), 0, sizeof(BSpaceDescriptor)) != EOK) { - MS_LOG(ERROR) << "Failed to init memory to zero."; - return NOSPACE; - } - desc->sig = static_cast(0xDEADBEEF); - desc->addr = rel_addr; - desc->req_size = reqSize; - desc->blk_size = NextPowerOf2(reqSize); - return static_cast(rel_addr * min_); - } else { - return NOSPACE; - } -} - -void BuddySpace::FreeNoLock(const BSpaceDescriptor *desc) { - MS_ASSERT(desc->sig == 0XDEADBEEF); - rel_addr_t rel_addr = desc->addr; - size_t blk_size = desc->blk_size; - size_t req_size = desc->req_size; - FreeBuddySeg(rel_addr, blk_size, req_size); -} - -void BuddySpace::Free(const BSpaceDescriptor *desc) { - if (desc == nullptr) { - MS_LOG(ERROR) << "The pointer[desc] is null."; - return; - } - std::lock_guard lock(mutex_); - return FreeNoLock(desc); -} - -std::ostream &operator<<(std::ostream &os, const BuddySpace &s) { - const int32_t kLvlOffset = 4; - os << "1 unit = " << s.GetMinSize() << "\n" - << "Size of buddy space = " << s.GetMaxSize() << "\n" - << "Number of levels = " << s.num_lvl_ << "\n\n" - << "Percent free = " << s.PercentFree() << "\n" - << "Dumping count array : " - << "\n"; - for (int i = 0; i < s.num_lvl_; i++) { - os << "[" << i << "] = " << s.count_[i] << " "; - if (((i + 1) % kLvlOffset) == 0) { - os << "\n"; - } - } - os << "\n"; - os << "Dumping allocation info:" - << "\n"; - auto max_addr = static_cast(BitLeftShift(1, s.num_lvl_ - 1)); - rel_addr_t addr = 0; - while (addr < max_addr) { - size_t sz = 0; - BuddySpace::STATE st; - s.GetBuddySegState(addr, &sz, &st); - os << "Address : " << std::left << std::setw(8) << addr << " Size : " << std::setw(8) << sz << " State : " - << ((st == BuddySpace::STATE::kAlloc) ? "ALLOC" : ((st == BuddySpace::STATE::kFree) ? "FREE" : "Unknown")) - << "\n"; - addr += sz; - } - return os; -} - -uint32_t BuddySpace::SizeToBlock(const uint64_t sz) const { - if (min_ == 0) { - MS_LOG(ERROR) << "min_ can not be zero."; - return 0; - } - uint32_t reqSize = (sz / min_); - if (sz % min_) { - reqSize++; - } - return reqSize; -} - -void BuddySpace::GetBuddySegState(const rel_addr_t rel_addr, size_t *rel_sz, STATE *st) const { - const int32_t kAddrOffset = 4; - const int32_t kShiftOffset = 2; - const uint64_t kBitLeftShift = 2; - const uint64_t kPosOffset = 2; - char byte; - int pos; - int offset; - uint64_t val = 0; - int shift; - pos = BitRightShift(rel_addr, kPosOffset); - offset = rel_addr % kAddrOffset; - shift = offset * kShiftOffset; - byte = map_[pos]; - switch (offset) { - case 0: - val = byte; - break; - case 1: - case 2: - val = BitLeftShift(BitAnd(byte, 0x0F), shift); - break; - case 3: - if (offset == 1) { - val = BitLeftShift(BitAnd(byte, 0x30), shift); - } else { - val = BitLeftShift(BitAnd(byte, 0x03), shift); - } - break; - } - if (BitAnd(val, ONE_BIT)) { - *rel_sz = 1; - } else if (BitAnd(val, TWO_BIT)) { - *rel_sz = 2; - } else if (BitAnd(val, MORE_BIT)) { - log_t lg = BitAnd(val, 0x0F); - *rel_sz = BitLeftShift(1, static_cast(lg + kBitLeftShift)); - } else { - *st = STATE::kEmpty; - return; - } - *st = BitAnd(val, ALLOC_BIT) ? STATE::kAlloc : STATE::kFree; -} - -void BuddySpace::SetBuddySegState(rel_addr_t rel_addr, size_t rel_sz, STATE st) { - const int32_t kAddrOffset = 4; - const int32_t kShiftOffset = 2; - const uint64_t kBitOffset = 2; - const uint64_t kPosOffset = 2; - int clr; - int mask; - int pos; - int offset; - int val = 0; - int shift; - auto log_sz = static_cast(Log2(rel_sz)); - pos = BitRightShift(rel_addr, kPosOffset); - offset = rel_addr % kAddrOffset; - shift = offset * kShiftOffset; - if (rel_sz == 1) { - val = ONE_BIT; - mask = 0xC0; - } else if (rel_sz == 2) { - val = TWO_BIT; - mask = 0xF0; - } else { - val = BitOr(static_cast(log_sz - kBitOffset), MORE_BIT); - mask = 0xFF; - } - if (st == STATE::kAlloc) { - val = BitOr(val, ALLOC_BIT); - } else if (st == STATE::kFree) { - val = BitAnd(val, ~(static_cast(ALLOC_BIT))); - } else if (st == STATE::kEmpty) { - val = 0; - } - clr = static_cast(~(BitRightShift(mask, shift))); - map_[pos] = static_cast(BitAnd(map_[pos], clr)); - map_[pos] = static_cast(BitOr(map_[pos], BitRightShift(val, shift))); - if (st == STATE::kAlloc) { - count_[log_sz]--; - } else if (st == STATE::kFree) { - count_[log_sz]++; - if (rel_addr < hint_[log_sz]) { - hint_[log_sz] = rel_addr; - } - } -} - -void BuddySpace::JoinBuddySeg(rel_addr_t addr, size_t blk_sz) { - const int32_t kLogszStep = 2; - while (blk_sz < BitLeftShift(1, num_lvl_)) { - rel_addr_t buddy = BitEx(addr, blk_sz); - size_t sz = 0; - STATE st; - GetBuddySegState(buddy, &sz, &st); - if (st == STATE::kFree && sz == blk_sz) { - auto log_sz = static_cast(Log2(blk_sz)); - rel_addr_t left = (buddy < addr) ? buddy : addr; - rel_addr_t right = left + blk_sz; - MS_ASSERT(count_[log_sz] >= 2); - count_[log_sz] -= kLogszStep; - SetBuddySegState(right, blk_sz, STATE::kEmpty); - SetBuddySegState(left, BitLeftShift(blk_sz, 1), STATE::kFree); - for (int i = 0; i < log_sz; i++) { - if (hint_[i] == right) { - hint_[i] = left; - } - } - addr = left; - blk_sz <<= 1u; - } else { - break; - } - } -} - -void BuddySpace::TrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz) { - MS_ASSERT(ask_sz < blk_sz); - uint32_t inx = Log2(blk_sz); - size_t remaining_sz = ask_sz; - for (int i = inx; i > 0; i--) { - size_t b_size = BitLeftShift(1, i); - size_t half_sz = BitRightShift(b_size, 1); - count_[i]--; - SetBuddySegState(addr, half_sz, STATE::kFree); - SetBuddySegState(addr + half_sz, half_sz, STATE::kFree); - if (remaining_sz >= half_sz) { - SetBuddySegState(addr, half_sz, STATE::kAlloc); - remaining_sz -= half_sz; - if (remaining_sz == 0) { - break; - } - addr += half_sz; - } - } -} - -void BuddySpace::UnTrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz) { - MS_ASSERT(ask_sz < blk_sz); - uint32_t inx = Log2(blk_sz); - size_t remaining_sz = ask_sz; - for (int i = inx; i > 0; i--) { - size_t b_size = BitLeftShift(1, i); - size_t half_sz = BitRightShift(b_size, 1); - if (remaining_sz >= half_sz) { -#ifdef DEBUG - { - size_t sz = 0; - STATE st; - GetBuddySegState(addr, &sz, &st); - MS_ASSERT(sz == half_sz && st == STATE::kAlloc); - } -#endif - SetBuddySegState(addr, half_sz, STATE::kFree); - remaining_sz -= half_sz; - if (remaining_sz == 0) { - JoinBuddySeg(addr, half_sz); - break; - } - addr += half_sz; - } - } -} - -rel_addr_t BuddySpace::AllocBuddySeg(uint32_t req_size) noexcept { - uint32_t blk_size = NextPowerOf2(req_size); - int start_inx = static_cast(Log2(blk_size)); - bool found = false; - rel_addr_t ask_addr = 0; - auto max_addr = static_cast(BitLeftShift(1, num_lvl_ - 1)); - STATE st; - size_t sz = 0; - for (int i = start_inx; !found && i < num_lvl_; i++) { - MS_ASSERT(count_[i] >= 0); - if (count_[i] == 0) { - continue; - } - auto blk_sz = static_cast(BitLeftShift(1, i)); - ask_addr = hint_[i]; - while (ask_addr < max_addr && !found) { - GetBuddySegState(ask_addr, &sz, &st); - if (st == STATE::kFree && sz == blk_sz) { - found = true; - } else { - MS_ASSERT(st != STATE::kEmpty); - ask_addr += ((sz > blk_sz) ? sz : blk_sz); - } - } - } - if (found) { - if (sz > req_size) { - TrimBuddySeg(ask_addr, sz, req_size); - } else { - SetBuddySegState(ask_addr, sz, STATE::kAlloc); - hint_[start_inx] = ask_addr; - } - return ask_addr; - } else { - return static_cast(NOSPACE); - } -} - -void BuddySpace::FreeBuddySeg(rel_addr_t addr, size_t blk_size, size_t req_size) { - if (req_size == blk_size) { -#ifdef DEBUG - { - size_t sz = 0; - STATE st; - GetBuddySegState(addr, &sz, &st); - } -#endif - SetBuddySegState(addr, blk_size, STATE::kFree); - JoinBuddySeg(addr, blk_size); - } else { - UnTrimBuddySeg(addr, blk_size, req_size); - } -} - -int BuddySpace::PercentFree() const { - const int32_t kFloatToPercent = 100; - uint64_t total_free_sz = 0; - uint64_t max_sz_in_unit = BitLeftShift(1, num_lvl_ - 1); - // Go through the count array without lock - for (int i = 0; i < num_lvl_; i++) { - int cnt = count_[i]; - if (cnt == 0) { - continue; - } - uint64_t blk_sz = BitLeftShift(1, i); - total_free_sz += (blk_sz * cnt); - } - return static_cast(static_cast(total_free_sz) / static_cast(max_sz_in_unit) * kFloatToPercent); -} - -BuddySpace::BuddySpace(int log_min, int num_lvl) - : hint_(nullptr), count_(nullptr), map_(nullptr), log_min_(log_min), num_lvl_(num_lvl), min_(0), max_(0) {} - -BuddySpace::~BuddySpace() { - hint_ = nullptr; - count_ = nullptr; - map_ = nullptr; -} - -Status BuddySpace::CreateBuddySpace(std::unique_ptr *out_bs, int log_min, int num_lvl) { - Status rc; - auto bs = new (std::nothrow) BuddySpace(log_min, num_lvl); - if (bs == nullptr) { - RETURN_STATUS_OOM("Out of memory."); - } - rc = bs->Init(); - if (rc.IsOk()) { - (*out_bs).reset(bs); - } else { - delete bs; - } - return rc; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/buddy.h b/mindspore-lite/minddata/dataset/util/buddy.h deleted file mode 100644 index 9f4ecea43..000000000 --- a/mindspore-lite/minddata/dataset/util/buddy.h +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BUDDY_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BUDDY_H_ - -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/status.h" - -using addr_t = int64_t; -using rel_addr_t = int32_t; -using log_t = int; -#define ALLOC_BIT 0x80 -#define ONE_BIT 0x40 -#define TWO_BIT 0x20 -#define MORE_BIT 0x10 -#define NOSPACE ((addr_t)(-1)) -namespace mindspore { -namespace dataset { -struct BSpaceDescriptor { - int32_t sig; - rel_addr_t addr; - size_t req_size; - size_t blk_size; -}; - -class BuddySpace { - public: - // C++11 feature. Change STATE into a type safe class with - // the keyword. Don't take out the keyword 'class' - enum class STATE { kFree, kAlloc, kEmpty }; - - BuddySpace(const BuddySpace &) = delete; - - BuddySpace &operator=(const BuddySpace &) = delete; - - virtual ~BuddySpace(); - - Status Alloc(uint64_t sz, BSpaceDescriptor *desc, addr_t *) noexcept; - - void Free(const BSpaceDescriptor *desc); - - uint64_t GetMinSize() const { return min_; } - - uint64_t GetMaxSize() const { return max_; } - - int PercentFree() const; - - friend std::ostream &operator<<(std::ostream &os, const BuddySpace &s); - - static uint64_t NextPowerOf2(uint64_t n) { - if (n <= 1) { - return 1; - } - n = n - 1; - while (n & (n - 1)) { - n = n & (n - 1); - } - return n << 1; - } - - static uint32_t Log2(uint64_t n) { - uint32_t cnt = 0; - while (n >>= 1) { - cnt++; - } - return cnt; - } - - static Status CreateBuddySpace(std::unique_ptr *out_bs, int log_min = 15, int num_lvl = 18); - - private: - rel_addr_t *hint_; - int *count_; - char *map_; - int log_min_; - int num_lvl_; - uint64_t min_; - uint64_t max_; - std::unique_ptr mem_; - std::mutex mutex_; - - explicit BuddySpace(int log_min = 15, int num_lvl = 18); - - Status Init(); - - addr_t AllocNoLock(const uint64_t sz, BSpaceDescriptor *desc) noexcept; - - void FreeNoLock(const BSpaceDescriptor *desc); - - uint32_t SizeToBlock(const uint64_t sz) const; - - void GetBuddySegState(const rel_addr_t rel_addr, size_t *rel_sz, STATE *st) const; - - void SetBuddySegState(rel_addr_t rel_addr, size_t rel_sz, STATE st); - - void JoinBuddySeg(rel_addr_t addr, size_t blk_sz); - - void TrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz); - - void UnTrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz); - - rel_addr_t AllocBuddySeg(uint32_t req_size) noexcept; - - void FreeBuddySeg(rel_addr_t addr, size_t blk_size, size_t req_size); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_BUDDY_H_ diff --git a/mindspore-lite/minddata/dataset/util/circular_pool.cc b/mindspore-lite/minddata/dataset/util/circular_pool.cc deleted file mode 100644 index 0a193bc43..000000000 --- a/mindspore-lite/minddata/dataset/util/circular_pool.cc +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/circular_pool.h" - -#include -#include - -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -Status CircularPool::AddOneArena() { - Status rc; - std::shared_ptr b; - RETURN_IF_NOT_OK(Arena::CreateArena(&b, arena_size_, is_cuda_malloc_)); - tail_ = b.get(); - cur_size_in_mb_ += arena_size_; - mem_segments_.push_back(std::move(b)); - return Status::OK(); -} - -ListOfArenas::iterator CircularPool::CircularIterator::Next() { - ListOfArenas::iterator it = dp_->mem_segments_.begin(); - uint32_t size = dp_->mem_segments_.size(); - // This is what we return - it += cur_; - // Prepare for the next round - cur_++; - if (cur_ == size) { - if (start_ == 0) { - has_next_ = false; - } else { - wrap_ = true; - cur_ = 0; - } - } else if (cur_ == start_) { - has_next_ = false; - } - return it; -} - -bool CircularPool::CircularIterator::has_next() const { return has_next_; } - -void CircularPool::CircularIterator::Reset() { - wrap_ = false; - has_next_ = false; - if (!dp_->mem_segments_.empty()) { - // Find the buddy arena that corresponds to the tail. - cur_tail_ = dp_->tail_; - auto list_end = dp_->mem_segments_.end(); - auto it = std::find_if(dp_->mem_segments_.begin(), list_end, - [this](const std::shared_ptr &b) { return b.get() == cur_tail_; }); - MS_ASSERT(it != list_end); - start_ = std::distance(dp_->mem_segments_.begin(), it); - cur_ = start_; - has_next_ = true; - } -} - -CircularPool::CircularIterator::CircularIterator(CircularPool *dp) : dp_(dp) { Reset(); } - -Status CircularPool::Allocate(size_t n, void **p) { - if (p == nullptr) { - RETURN_STATUS_UNEXPECTED("p is null"); - } - Status rc; - void *ptr = nullptr; - do { - SharedLock lock_s(&rw_lock_); - int prevSzInMB = cur_size_in_mb_; - bool move_tail = false; - CircularIterator cirIt(this); - while (cirIt.has_next()) { - auto it = cirIt.Next(); - Arena *ba = it->get(); - RETURN_UNEXPECTED_IF_NULL(ba); - if (ba->get_max_size() < n) { - RETURN_STATUS_OOM("Out of memory."); - } - // If we are asked to move forward the tail - if (move_tail) { - Arena *expected = cirIt.cur_tail_; - (void)atomic_compare_exchange_weak(&tail_, &expected, ba); - move_tail = false; - } - rc = ba->Allocate(n, &ptr); - if (rc.IsOk()) { - *p = ptr; - break; - } else if (rc == StatusCode::kMDOutOfMemory) { - // Make the next arena a new tail and continue. - move_tail = true; - } else { - return rc; - } - } - - // Handle the case we have done one round robin search. - if (ptr == nullptr) { - // If we have room to expand. - if (unlimited_ || cur_size_in_mb_ < max_size_in_mb_) { - // lock in exclusively mode. - lock_s.Upgrade(); - // Check again if someone has already expanded. - if (cur_size_in_mb_ == prevSzInMB) { - RETURN_IF_NOT_OK(AddOneArena()); - } - // Re-acquire the shared lock and try again - lock_s.Downgrade(); - } else { - RETURN_STATUS_OOM("Out of memory."); - } - } - } while (ptr == nullptr); - return rc; -} - -void CircularPool::Deallocate(void *p) { - // Lock in the chain in shared mode and find out which - // segment it comes from - SharedLock lock(&rw_lock_); - auto it = std::find_if(mem_segments_.begin(), mem_segments_.end(), [this, p](std::shared_ptr &b) -> bool { - char *q = reinterpret_cast(p); - auto *base = reinterpret_cast(b->get_base_addr()); - return (q > base && q < base + arena_size_ * 1048576L); - }); - lock.Unlock(); - MS_ASSERT(it != mem_segments_.end()); - it->get()->Deallocate(p); -} - -Status CircularPool::Reallocate(void **pp, size_t old_sz, size_t new_sz) { - // Lock in the chain in shared mode and find out which - // segment it comes from - if (pp == nullptr) { - RETURN_STATUS_UNEXPECTED("pp is null"); - } - void *p = *pp; - SharedLock lock(&rw_lock_); - auto it = std::find_if(mem_segments_.begin(), mem_segments_.end(), [this, p](std::shared_ptr &b) -> bool { - char *q = reinterpret_cast(p); - auto *base = reinterpret_cast(b->get_base_addr()); - return (q > base && q < base + arena_size_ * 1048576L); - }); - lock.Unlock(); - MS_ASSERT(it != mem_segments_.end()); - Arena *ba = it->get(); - Status rc = ba->Reallocate(pp, old_sz, new_sz); - if (rc == StatusCode::kMDOutOfMemory) { - // The current arena has no room for the bigger size. - // Allocate free space from another arena and copy - // the content over. - void *q = nullptr; - rc = this->Allocate(new_sz, &q); - RETURN_IF_NOT_OK(rc); - errno_t err = memcpy_s(q, new_sz, p, old_sz); - if (err) { - this->Deallocate(q); - RETURN_STATUS_UNEXPECTED(std::to_string(err)); - } - *pp = q; - ba->Deallocate(p); - } - return Status::OK(); -} - -uint64_t CircularPool::get_max_size() const { return mem_segments_.front()->get_max_size(); } - -int CircularPool::PercentFree() const { - int percent_free = 0; - int num_arena = 0; - for (auto const &p : mem_segments_) { - percent_free += p->PercentFree(); - num_arena++; - } - if (num_arena) { - return percent_free / num_arena; - } else { - return 100; - } -} - -CircularPool::CircularPool(int max_size_in_gb, int arena_size, bool is_cuda_malloc) - : unlimited_(max_size_in_gb <= 0), - max_size_in_mb_(unlimited_ ? std::numeric_limits::max() : max_size_in_gb * 1024), - arena_size_(arena_size), - is_cuda_malloc_(is_cuda_malloc), - cur_size_in_mb_(0) {} - -Status CircularPool::CreateCircularPool(std::shared_ptr *out_pool, int max_size_in_gb, int arena_size, - bool createOneArena, bool is_cuda_malloc) { - Status rc; - if (out_pool == nullptr) { - RETURN_STATUS_UNEXPECTED("pPool is null"); - } - auto pool = new (std::nothrow) CircularPool(max_size_in_gb, arena_size, is_cuda_malloc); - if (pool == nullptr) { - RETURN_STATUS_OOM("Out of memory."); - } - if (createOneArena) { - rc = pool->AddOneArena(); - } - if (rc.IsOk()) { - (*out_pool).reset(pool); - } else { - delete pool; - } - return rc; -} - -CircularPool::~CircularPool() = default; -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/circular_pool.h b/mindspore-lite/minddata/dataset/util/circular_pool.h deleted file mode 100644 index be34b636d..000000000 --- a/mindspore-lite/minddata/dataset/util/circular_pool.h +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_CIRCULAR_POOL_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_CIRCULAR_POOL_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" -#include "mindspore-lite/minddata/dataset/util/arena.h" -#include "mindspore-lite/minddata/dataset/util/lock.h" - -namespace mindspore { -namespace dataset { -using ListOfArenas = std::vector>; - -// This is a dynamic memory pool built on top of memory -// segment each of which is 4G in size. Initially we start -// with one segment, and gradually add segments (not -// guaranteed contiguous) until we reach 32G in size. There -// is an assumption about this kind of memory pool. Allocated -// memory is not held for the whole duration of the pool and -// will be released soon. Based on this assumption, memory is -// obtained from the tail while allocated memory is returned -// to the head of the pool. -class CircularPool : public MemoryPool { - public: - class CircularIterator { - friend class CircularPool; - - public: - explicit CircularIterator(CircularPool *dp); - - ~CircularIterator() = default; - - bool has_next() const; - - ListOfArenas::iterator Next(); - - void Reset(); - - private: - CircularPool *dp_; - Arena *cur_tail_{}; - uint32_t start_{}; - uint32_t cur_{}; - bool wrap_{}; - bool has_next_{}; - }; - - CircularPool(const CircularPool &) = delete; - - CircularPool &operator=(const CircularPool &) = delete; - - ~CircularPool() override; - - Status Allocate(size_t n, void **) override; - - Status Reallocate(void **, size_t old_size, size_t new_size) override; - - void Deallocate(void *) override; - - uint64_t get_max_size() const override; - - int PercentFree() const override; - - friend std::ostream &operator<<(std::ostream &os, const CircularPool &s) { - int i = 0; - for (auto it = s.mem_segments_.begin(); it != s.mem_segments_.end(); ++it, ++i) { - os << "Dumping segment " << i << "\n" << *(it->get()); - } - return os; - } - - static Status CreateCircularPool(std::shared_ptr *out_pool, int max_size_in_gb = -1, - int arena_size = 4096, bool create_one_arena = false, bool is_cuda_malloc = false); - - private: - ListOfArenas mem_segments_; - std::atomic tail_{}; - bool unlimited_; - int max_size_in_mb_; - int arena_size_; - int cur_size_in_mb_; - RWLock rw_lock_; - bool is_cuda_malloc_; - - // We can take negative or 0 as input which means unlimited. - CircularPool(int max_size_in_gb, int arena_size, bool is_cuda_malloc); - - Status AddOneArena(); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_CIRCULAR_POOL_H_ diff --git a/mindspore-lite/minddata/dataset/util/cond_var.cc b/mindspore-lite/minddata/dataset/util/cond_var.cc deleted file mode 100644 index 77e0aa7c5..000000000 --- a/mindspore-lite/minddata/dataset/util/cond_var.cc +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/cond_var.h" - -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -CondVar::CondVar() : svc_(nullptr), my_name_(Services::GetUniqueID()) {} - -Status CondVar::Wait(std::unique_lock *lck, const std::function &pred) { - try { - if (svc_ != nullptr) { - // If this cv registers with a global resource tracking, then wait unconditionally. - auto f = [this, &pred]() -> bool { - std::unique_lock interrupt_lock(interrupt_mux_); - return (pred() || this->Interrupted()); - }; - cv_.wait(*lck, f); - // If we are interrupted, override the return value if this is the master thread. - // Master thread is being interrupted mostly because of some thread is reporting error. - RETURN_IF_NOT_OK(Task::OverrideInterruptRc(this->GetInterruptStatus())); - } else { - // Otherwise we wake up once a while to check for interrupt (for this thread). - auto f = [&pred]() -> bool { return (pred() || this_thread::is_interrupted()); }; - while (!f()) { - (void)cv_.wait_for(*lck, std::chrono::milliseconds(1)); - } - RETURN_IF_INTERRUPTED(); - } - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED(e.what()); - } - return Status::OK(); -} - -Status CondVar::WaitFor(std::unique_lock *lck, int64_t duration) { - try { - if (svc_ != nullptr) { - // If this cv registers with a global resource tracking, then wait unconditionally. - auto f = [this]() -> bool { - std::unique_lock interrupt_lock(interrupt_mux_); - return this->Interrupted(); - }; - (void)cv_.wait_for(*lck, std::chrono::milliseconds(duration), f); - // If we are interrupted, override the return value if this is the master thread. - // Master thread is being interrupted mostly because of some thread is reporting error. - RETURN_IF_NOT_OK(Task::OverrideInterruptRc(this->GetInterruptStatus())); - } else { - // Otherwise we wake up once a while to check for interrupt (for this thread). - auto f = []() -> bool { return this_thread::is_interrupted(); }; - int64_t ctr = 0; - while (!f()) { - if (ctr < duration) { - ctr++; - (void)cv_.wait_for(*lck, std::chrono::milliseconds(1), f); - } else { - ctr++; - break; - } - } - RETURN_IF_INTERRUPTED(); - } - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED(e.what()); - } - return Status::OK(); -} - -CondVar::~CondVar() noexcept { - if (svc_ != nullptr) { - (void)svc_->Deregister(my_name_); - svc_ = nullptr; - } -} - -void CondVar::NotifyOne() noexcept { cv_.notify_one(); } - -void CondVar::NotifyAll() noexcept { cv_.notify_all(); } - -Status CondVar::Register(std::shared_ptr svc) { - Status rc = svc->Register(&my_name_, this); - if (rc.IsOk()) { - svc_ = svc; - } - return rc; -} - -void CondVar::Interrupt() { - IntrpResource::Interrupt(); - // If we call notify while the thread is executing the pred function in Wait/WaitFor method, it won't respond, - // and the interrupt status the thread gets may not have been updated yet, so it won't exit the wait state, - // resulting in the thread never being woken up. So we use a lock here to ensure that no thread is executing - // the pred function at the time of notify. - std::unique_lock interrupt_lock(interrupt_mux_); - cv_.notify_all(); -} - -std::string CondVar::my_name() const { return my_name_; } - -Status CondVar::Deregister() { - if (svc_) { - Status rc = svc_->Deregister(my_name_); - svc_ = nullptr; - return rc; - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/cond_var.h b/mindspore-lite/minddata/dataset/util/cond_var.h deleted file mode 100644 index 6b83a88f1..000000000 --- a/mindspore-lite/minddata/dataset/util/cond_var.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_COND_VAR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_COND_VAR_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/intrp_resource.h" -#include "mindspore-lite/minddata/dataset/util/intrp_service.h" - -namespace mindspore { -namespace dataset { -class CondVar : public IntrpResource { - public: - CondVar(); - - ~CondVar() noexcept override; - - Status Wait(std::unique_lock *lck, const std::function &pred); - - /// Timed sleep. - /// \param lck lock - /// \param duration time to sleep in ms - /// \return Status code - Status WaitFor(std::unique_lock *lck, int64_t duration); - - void Interrupt() override; - - void NotifyOne() noexcept; - - void NotifyAll() noexcept; - - Status Register(std::shared_ptr svc); - - std::string my_name() const; - - Status Deregister(); - - protected: - std::condition_variable cv_; - std::shared_ptr svc_; - - private: - std::string my_name_; - std::mutex interrupt_mux_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_COND_VAR_H_ diff --git a/mindspore-lite/minddata/dataset/util/ftok_key.cc b/mindspore-lite/minddata/dataset/util/ftok_key.cc deleted file mode 100644 index 07be24697..000000000 --- a/mindspore-lite/minddata/dataset/util/ftok_key.cc +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/ftok_key.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -#include -#include -#include -#include -#include -#include -#endif - -#include "mindspore-lite/minddata/dataset/callback/callback_param.h" -#include "mindspore-lite/minddata/dataset/include/dataset/constants.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/core/message_queue.h" -#include "mindspore-lite/minddata/dataset/core/tensor_row.h" -#include "mindspore-lite/minddata/dataset/engine/ir/datasetops/map_node.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include "mindspore-lite/minddata/dataset/kernels/image/image_utils.h" - -namespace mindspore { -namespace dataset { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -std::atomic inc_id(0); - -Status GetKey(key_t *key) { - RETURN_UNEXPECTED_IF_NULL(key); - // ipc file name - auto pid = getpid(); - auto tid = std::this_thread::get_id(); - std::ostringstream oss; - oss << tid; - std::string ipc_key_file = - "/tmp/dataset_ipc_" + std::to_string(pid) + "_" + oss.str() + "_" + std::to_string(inc_id++); - MS_LOG(INFO) << "ipc file: " << ipc_key_file; - - // create the ipc file - std::fstream fs1; - fs1.open(ipc_key_file, std::fstream::out); - fs1.close(); - - platform::ChangeFileMode(ipc_key_file, S_IRUSR | S_IWUSR); - - // get key by ftok - *key = ftok(ipc_key_file.c_str(), 0x600); - if (*key < 0) { - RETURN_STATUS_UNEXPECTED("ftok a new key error, ipc file: " + ipc_key_file); - } - - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/ftok_key.h b/mindspore-lite/minddata/dataset/util/ftok_key.h deleted file mode 100644 index 615d268ba..000000000 --- a/mindspore-lite/minddata/dataset/util/ftok_key.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_FTOK_KEY_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_FTOK_KEY_H_ - -#include -#include -#include -#include -#include -#include -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -#include -#include -#endif - -#include "mindspore-lite/minddata/dataset/api/python/python_mp.h" -#include "mindspore-lite/minddata/dataset/callback/ds_callback.h" -#include "mindspore-lite/minddata/dataset/core/shared_memory_queue.h" -#include "mindspore-lite/minddata/dataset/engine/dataset_iterator.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/map_op/map_job.h" -#include "mindspore-lite/minddata/dataset/engine/datasetops/parallel_op.h" -#include "mindspore-lite/minddata/dataset/kernels/tensor_op.h" -#include "mindspore-lite/minddata/dataset/util/queue.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -namespace platform = mindspore; -#else -#include "mindspore-lite/src/common/file_utils.h" -namespace platform = mindspore::lite; -#endif - -namespace mindspore { -namespace dataset { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -extern std::atomic inc_id; -Status GetKey(key_t *key); -#endif -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_FTOK_KEY_H_ diff --git a/mindspore-lite/minddata/dataset/util/gil_scoped.h b/mindspore-lite/minddata/dataset/util/gil_scoped.h deleted file mode 100644 index 15def6cf1..000000000 --- a/mindspore-lite/minddata/dataset/util/gil_scoped.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_GIL_SCOPED_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_GIL_SCOPED_H_ - -#include - -#include "pybind11/pybind11.h" - -namespace py = pybind11; - -namespace mindspore { -namespace dataset { - -class GilAcquireWithCheck { - public: - GilAcquireWithCheck() { - if (Py_IsInitialized() != 0 && PyGILState_Check() == 0) { - MS_LOG(INFO) << "Begin acquire gil."; - acquire_ = std::make_unique(); // acquire the gil - MS_LOG(INFO) << "End acquire gil."; - } else { - MS_LOG(INFO) << "Py_IsInitialized is 1, PyGILState_Check is 1, no need to acquire gil."; - acquire_ = nullptr; - } - if (PyGILState_Check() == 0) { - MS_LOG(EXCEPTION) << "PyGILState_Check(): 0, except 1. Acquire gil failed in current thread."; - } - } - - ~GilAcquireWithCheck() { - if (PyGILState_Check() == 1 && acquire_ != nullptr) { - MS_LOG(INFO) << "Begin release gil."; - acquire_ = nullptr; - MS_LOG(INFO) << "End release gil."; - } else { - MS_LOG(INFO) << "No need to release gil."; - } - } - - private: - std::unique_ptr acquire_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_GIL_SCOPED_H_ diff --git a/mindspore-lite/minddata/dataset/util/intrp_resource.h b/mindspore-lite/minddata/dataset/util/intrp_resource.h deleted file mode 100644 index d5f13ccad..000000000 --- a/mindspore-lite/minddata/dataset/util/intrp_resource.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_RESOURCE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_RESOURCE_H_ - -#include -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class IntrpResource { - public: - enum class State : int { kRunning, kInterrupted }; - - IntrpResource() : st_(State::kRunning) {} - - virtual ~IntrpResource() = default; - - virtual void Interrupt() { st_ = State::kInterrupted; } - - virtual void ResetIntrpState() { st_ = State::kRunning; } - - State CurState() const { return st_; } - - bool Interrupted() const { return CurState() == State::kInterrupted; } - - virtual Status GetInterruptStatus() const { - if (Interrupted()) { - return Status(StatusCode::kMDInterrupted); - } - return Status::OK(); - } - - protected: - std::atomic st_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_RESOURCE_H_ diff --git a/mindspore-lite/minddata/dataset/util/intrp_service.cc b/mindspore-lite/minddata/dataset/util/intrp_service.cc deleted file mode 100644 index 32c72b3d0..000000000 --- a/mindspore-lite/minddata/dataset/util/intrp_service.cc +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/intrp_service.h" -#include -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -const int64_t kServiceRetryGetUniqueIdInterVal = 10; -IntrpService::IntrpService() try : high_water_mark_(0) { (void)ServiceStart(); } catch (const std::exception &e) { - MS_LOG(ERROR) << "Interrupt service failed: " << e.what() << "."; - std::terminate(); -} - -IntrpService::~IntrpService() noexcept { - MS_LOG(DEBUG) << "Number of registered resources is " << high_water_mark_ << "."; - if (!all_intrp_resources_.empty()) { - try { - InterruptAll(); - } catch (const std::exception &e) { - // Ignore all error as we can't throw in the destructor. - } - } - (void)ServiceStop(); -} - -Status IntrpService::Register(std::string *name, IntrpResource *res) { - SharedLock stateLck(&state_lock_); - // Now double check the state - if (ServiceState() != STATE::kRunning) { - RETURN_STATUS_ERROR(StatusCode::kMDInterrupted, "Interrupt service is shutting down"); - } else { - std::lock_guard lck(mutex_); - try { - std::ostringstream ss; - std::string uuid = std::string(""); - ss << this_thread::get_id(); - MS_LOG(DEBUG) << "Register resource with name " << *name << ". Thread ID " << ss.str() << "."; - auto it = all_intrp_resources_.emplace(*name, res); - while (it.second == false) { - uuid = Services::GetUniqueID(); - it = all_intrp_resources_.emplace(uuid, res); - MS_LOG(INFO) << "The name(" << *name << ") of register resource is duplicate, get new uuid: " << uuid; - std::this_thread::sleep_for(std::chrono::milliseconds(kServiceRetryGetUniqueIdInterVal)); - } - if (!uuid.empty()) { - *name = uuid; - } - high_water_mark_++; - } catch (std::exception &e) { - RETURN_STATUS_UNEXPECTED(e.what()); - } - } - return Status::OK(); -} - -Status IntrpService::Deregister(const std::string &name) noexcept { - std::lock_guard lck(mutex_); - try { - std::ostringstream ss; - ss << this_thread::get_id(); - MS_LOG(DEBUG) << "De-register resource with name " << name << ". Thread ID is " << ss.str() << "."; - auto n = all_intrp_resources_.erase(name); - if (n == 0) { - MS_LOG(INFO) << "Key " << name << " not found."; - } - } catch (std::exception &e) { - RETURN_STATUS_UNEXPECTED(e.what()); - } - return Status::OK(); -} - -void IntrpService::InterruptAll() noexcept { - std::lock_guard lck(mutex_); - for (auto const &it : all_intrp_resources_) { - std::string kName = it.first; - try { - it.second->Interrupt(); - } catch (const std::exception &e) { - // continue the clean up. - } - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/intrp_service.h b/mindspore-lite/minddata/dataset/util/intrp_service.h deleted file mode 100644 index d47e7ccfa..000000000 --- a/mindspore-lite/minddata/dataset/util/intrp_service.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_SERVICE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_SERVICE_H_ - -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/intrp_resource.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/service.h" - -namespace mindspore { -namespace dataset { -using SvcAllocator = Allocator>; - -class IntrpService : public Service { - public: - IntrpService(); - - ~IntrpService() noexcept override; - - IntrpService(const IntrpService &) = delete; - - IntrpService &operator=(const IntrpService &) = delete; - - Status Register(std::string *name, IntrpResource *res); - - Status Deregister(const std::string &name) noexcept; - - void InterruptAll() noexcept; - - Status DoServiceStart() override { return Status::OK(); } - - Status DoServiceStop() override { return Status::OK(); } - - private: - int high_water_mark_; - std::mutex mutex_; - std::map all_intrp_resources_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_INTRP_SERVICE_H_ diff --git a/mindspore-lite/minddata/dataset/util/json_helper.cc b/mindspore-lite/minddata/dataset/util/json_helper.cc deleted file mode 100644 index 63d587486..000000000 --- a/mindspore-lite/minddata/dataset/util/json_helper.cc +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright 2020-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/json_helper.h" - -#include - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Create a numbered json file from image folder -Status JsonHelper::CreateAlbum(const std::string &in_dir, const std::string &out_dir) { - // in check - Path base_dir = Path(in_dir); - RETURN_IF_NOT_OK(RealPath(in_dir)); - if (!base_dir.IsDirectory() || !base_dir.Exists()) { - RETURN_STATUS_UNEXPECTED("Input dir is not a directory or doesn't exist"); - } - // check if output_dir exists and create it if it does not exist - Path target_dir = Path(out_dir); - RETURN_IF_NOT_OK(target_dir.CreateDirectory()); - - // iterate over in dir and create json for all images - uint64_t index = 0; - auto dir_it = Path::DirIterator::OpenDirectory(&base_dir); - RETURN_UNEXPECTED_IF_NULL(dir_it); - while (dir_it->HasNext()) { - Path v = dir_it->Next(); - // check if found file fits image extension - - // create json file in output dir with the path - std::string out_file = out_dir + "/" + std::to_string(index) + ".json"; - RETURN_IF_NOT_OK(UpdateValue(out_file, "image", v.ToString(), out_file)); - index++; - } - return Status::OK(); -} - -Status JsonHelper::RealPath(const std::string &path) { - std::string real_path; - RETURN_IF_NOT_OK(Path::RealPath(path, real_path)); - return Status::OK(); -} - -// A print method typically used for debugging -void JsonHelper::Print(std::ostream &out) const { - out << " Data Helper" - << "\n"; -} - -Status JsonHelper::UpdateArray(const std::string &in_file, const std::string &key, - const std::vector &value, const std::string &out_file) { - try { - Path in = Path(in_file); - nlohmann::json js; - if (in.Exists()) { - RETURN_IF_NOT_OK(RealPath(in_file)); - std::ifstream in_stream(in_file, std::ios::in); - try { - MS_LOG(INFO) << "Filename: " << in_file << "."; - in_stream >> js; - } catch (const std::exception &err) { - in_stream.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open json file: " + in_file + - ", please delete it and try again!"); - } - in_stream.close(); - } - js[key] = value; - if (out_file == "") { - std::ofstream o(in_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(in_file, S_IRUSR | S_IWUSR); - } else { - std::ofstream o(out_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(out_file, S_IRUSR | S_IWUSR); - } - } - // Catch any exception and convert to Status return code - catch (nlohmann::json::exception &e) { - std::string err_msg = "Parse json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } catch (const std::exception &e) { - std::string err_msg = "Update json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -Status JsonHelper::RemoveKey(const std::string &in_file, const std::string &key, const std::string &out_file) { - try { - Path in = Path(in_file); - nlohmann::json js; - if (in.Exists()) { - RETURN_IF_NOT_OK(RealPath(in_file)); - std::ifstream in_stream(in_file, std::ios::in); - try { - MS_LOG(INFO) << "Filename: " << in_file << "."; - in_stream >> js; - } catch (const std::exception &err) { - in_stream.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open json file: " + in_file + - ", please delete it and try again!"); - } - in_stream.close(); - } - (void)js.erase(key); - MS_LOG(INFO) << "Write outfile is: " << js << "."; - if (out_file == "") { - std::ofstream o(in_file, std::ios::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(in_file, S_IRUSR | S_IWUSR); - } else { - std::ofstream o(out_file, std::ios::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(out_file, S_IRUSR | S_IWUSR); - } - } - // Catch any exception and convert to Status return code - catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Update json failed "); - } - return Status::OK(); -} - -size_t JsonHelper::DumpData(const unsigned char *tensor_addr, const size_t &tensor_size, void *addr, - const size_t &buffer_size) { - // write to address, input order is: destination, source - errno_t ret = memcpy_s(addr, buffer_size, tensor_addr, tensor_size); - if (ret != 0) { - // memcpy failed - MS_LOG(ERROR) << "memcpy tensor memory failed" - << "."; - return 0; // amount of data copied is 0, error - } - return tensor_size; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/json_helper.h b/mindspore-lite/minddata/dataset/util/json_helper.h deleted file mode 100644 index 6ba72bbb3..000000000 --- a/mindspore-lite/minddata/dataset/util/json_helper.h +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright 2019-2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_JSON_DATA_HELPER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_JSON_DATA_HELPER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/path.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -namespace platform = mindspore; -#else -#include "mindspore-lite/src/common/file_utils.h" -namespace platform = mindspore::lite; -#endif - -namespace mindspore { -namespace dataset { - -/// \brief Simple class to do data manipulation, contains helper function to update json files in dataset -class JsonHelper { - public: - /// \brief constructor - JsonHelper() {} - - /// \brief Destructor - ~JsonHelper() = default; - - /// \brief Create an Album dataset while taking in a path to a image folder - /// Creates the output directory if doesn't exist - /// \param[in] in_dir Image folder directory that takes in images - /// \param[in] out_dir Directory containing output json files - Status CreateAlbum(const std::string &in_dir, const std::string &out_dir); - - /// \brief Update a json file field with a vector of integers - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional input for output file path, will write to input file if not specified - /// \return Status The status code returned - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = ""); - - /// \brief Update a json file field with a vector of type T values - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value array to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - template - Status UpdateArray(const std::string &in_file, const std::string &key, const std::vector &value, - const std::string &out_file = "") { - try { - Path in = Path(in_file); - nlohmann::json js; - if (in.Exists()) { - RETURN_IF_NOT_OK(RealPath(in_file)); - try { - std::ifstream in_stream(in_file, std::ios::in); - MS_LOG(INFO) << "Filename: " << in_file << "."; - in_stream >> js; - in_stream.close(); - } catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open json file: " + in_file + - ", please delete it and try again!"); - } - } - js[key] = value; - - if (out_file == "") { - std::ofstream o(in_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(in_file, S_IRUSR | S_IWUSR); - } else { - std::ofstream o(out_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(out_file, S_IRUSR | S_IWUSR); - } - } - // Catch any exception and convert to Status return code - catch (nlohmann::json::exception &e) { - std::string err_msg = "Parse json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } catch (const std::exception &e) { - std::string err_msg = "Update json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); - } - - /// \brief Update a json file field with a single value of of type T - /// \param in_file The input file name to read in - /// \param key Key of field to write to - /// \param value Value to write to file - /// \param out_file Optional parameter for output file path, will write to input file if not specified - /// \return Status The status code returned - template - Status UpdateValue(const std::string &in_file, const std::string &key, const T &value, - const std::string &out_file = "") { - try { - Path in = Path(in_file); - nlohmann::json js; - if (in.Exists()) { - RETURN_IF_NOT_OK(RealPath(in_file)); - try { - std::ifstream in_stream(in_file, std::ios::in); - MS_LOG(INFO) << "Filename: " << in_file << "."; - in_stream >> js; - in_stream.close(); - } catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open json file: " + in_file + - ", please delete it and try again!"); - } - } - js[key] = value; - if (out_file == "") { - std::ofstream o(in_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(in_file, S_IRUSR | S_IWUSR); - } else { - std::ofstream o(out_file, std::ofstream::out | std::ofstream::trunc); - o << js; - o.close(); - platform::ChangeFileMode(out_file, S_IRUSR | S_IWUSR); - } - } - // Catch any exception and convert to Status return code - catch (nlohmann::json::exception &e) { - std::string err_msg = "Parse json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } catch (const std::exception &e) { - std::string err_msg = "Update json failed. Error info: "; - err_msg += e.what(); - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); - } - - /// \brief Template function to write tensor to file - /// \param[in] in_file File to write to - /// \param[in] data Array of type T values - /// \return Status The status code returned - template - Status WriteBinFile(const std::string &in_file, const std::vector &data) { - try { - std::string real_in_file; - RETURN_IF_NOT_OK(Path::RealPath(in_file, real_in_file)); - std::ofstream o(real_in_file, std::ios::binary | std::ios::out); - if (!o.is_open()) { - RETURN_STATUS_UNEXPECTED("Error opening Bin file to write"); - } - size_t length = data.size(); - o.write(reinterpret_cast(&data[0]), std::streamsize(length * sizeof(T))); - o.close(); - - platform::ChangeFileMode(real_in_file, S_IRUSR | S_IWUSR); - } - // Catch any exception and convert to Status return code - catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Write bin file failed "); - } - return Status::OK(); - } - - /// \brief Write pointer to bin, use pointer to avoid memcpy - /// \param[in] in_file File name to write to - /// \param[in] data Pointer to data - /// \param[in] length Length of values to write from pointer - /// \return Status The status code returned - template - Status WriteBinFile(const std::string &in_file, T *data, size_t length) { - try { - std::string real_in_file; - RETURN_IF_NOT_OK(Path::RealPath(in_file, real_in_file)); - std::ofstream o(real_in_file, std::ios::binary | std::ios::out); - if (!o.is_open()) { - RETURN_STATUS_UNEXPECTED("Error opening Bin file to write"); - } - o.write(reinterpret_cast(data), std::streamsize(length * sizeof(T))); - o.close(); - - platform::ChangeFileMode(real_in_file, S_IRUSR | S_IWUSR); - } - // Catch any exception and convert to Status return code - catch (const std::exception &err) { - RETURN_STATUS_UNEXPECTED("Write bin file failed "); - } - return Status::OK(); - } - - /// \brief Helper function to copy content of a tensor to buffer - /// \note This function iterates over the tensor in bytes, since - /// \param[in] tensor_addr The memory held by a tensor, e.g. tensor->GetBuffer() - /// \param[in] tensor_size The amount of data in bytes in tensor_addr, e.g. tensor->SizeInBytes() - /// \param[out] addr The address to copy tensor data to - /// \param[in] buffer_size The buffer size of addr - /// \return The size of the tensor (bytes copied - size_t DumpData(const unsigned char *tensor_addr, const size_t &tensor_size, void *addr, const size_t &buffer_size); - - /// \brief Helper function to delete key in json file - /// \note This function will return okay even if key not found - /// \param[in] in_file Json file to remove key from - /// \param[in] key The key to remove - /// \return Status The status code returned - Status RemoveKey(const std::string &in_file, const std::string &key, const std::string &out_file = ""); - - /// \brief A print method typically used for debugging - /// \param out - The output stream to write output to - void Print(std::ostream &out) const; - - /// \brief Helper function to check real path - /// \note This function will return okay even if key not found - /// \param[in] path Path to Json file - /// \return Status The status code returned - Status RealPath(const std::string &path); - - /// \brief << Stream output operator overload - /// \note This allows you to write the debug print info using stream operators - /// \param out Reference to the output stream being overloaded - /// \param dh Reference to the DataSchema to display - /// \return The output stream must be returned - friend std::ostream &operator<<(std::ostream &out, const JsonHelper &dh) { - dh.Print(out); - return out; - } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_JSON_HELPER_H_ diff --git a/mindspore-lite/minddata/dataset/util/list.h b/mindspore-lite/minddata/dataset/util/list.h deleted file mode 100644 index 776827aa7..000000000 --- a/mindspore-lite/minddata/dataset/util/list.h +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LIST_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LIST_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -template -struct Node { - using value_type = T; - using pointer = T *; - pointer prev; - pointer next; - - Node() { - prev = nullptr; - next = nullptr; - } -}; - -template -struct List { - using value_type = T; - using pointer = T *; - using const_pointer = const T *; - using reference = T &; - using const_reference = const T &; - int count; - pointer head; - pointer tail; - Node T::*node; - - // Constructor - explicit List(Node T::*m) : count(0), head(nullptr), tail(nullptr), node(m) {} - - // Destructor - virtual ~List() { - head = nullptr; - tail = nullptr; - } - - // Prepend - virtual void Prepend(pointer elem) { - Node &elem_node = elem->*node; - elem_node.prev = nullptr; - elem_node.next = head; - if (head != nullptr) { - Node &base_node = head->*node; - base_node.prev = elem; - } - head = elem; - if (tail == nullptr) { - tail = elem; - } - ++count; - } - - // Append - virtual void Append(pointer elem) { - Node &elem_node = elem->*node; - elem_node.next = nullptr; - elem_node.prev = tail; - if (tail != nullptr) { - Node &base_node = tail->*node; - base_node.next = elem; - } - tail = elem; - if (head == nullptr) { - head = elem; - } - ++count; - } - - // Insert elem2 after elem1 in the list. - virtual void InsertAfter(pointer elem1, pointer elem2) { - MS_ASSERT(elem1 != elem2); - Node &elem1_node = elem1->*node; - Node &elem2_node = elem2->*node; - elem2_node.prev = elem1; - elem2_node.next = elem1_node.next; - if (elem1_node.next != nullptr) { - Node &next_node = elem1_node.next->*node; - next_node.prev = elem2; - } - elem1_node.next = elem2; - if (tail == elem1) { - tail = elem2; - } - ++count; - } - - // Insert elem2 before elem1 in the list. - virtual void InsertBefore(pointer elem1, pointer elem2) { - MS_ASSERT(elem1 != elem2); - Node &elem1_node = elem1->*node; - Node &elem2_node = elem2->*node; - elem2_node.next = elem1; - elem2_node.prev = elem1_node.prev; - if (elem1_node.prev != nullptr) { - Node &prev_node = elem1_node.prev->*node; - prev_node.next = elem2; - } - elem1_node.prev = elem2; - if (head == elem1) { - head = elem2; - } - ++count; - } - - // Remove an element in the list - virtual void Remove(pointer elem) noexcept { - Node &elem_node = elem->*node; - if (elem_node.next != nullptr) { - Node &next_node = elem_node.next->*node; - next_node.prev = elem_node.prev; - } else { - tail = elem_node.prev; - } - if (elem_node.prev != nullptr) { - Node &prev_node = elem_node.prev->*node; - prev_node.next = elem_node.next; - } else { - head = elem_node.next; - } - elem_node.prev = nullptr; - elem_node.next = nullptr; - --count; - } - - // Iterator - class Iterator : public std::iterator { - public: - pointer elem_; - - explicit Iterator(const List &v, pointer p = nullptr) : elem_(p), li_(v) {} - - ~Iterator() = default; - - reference operator*() { return *elem_; } - - pointer operator->() { return elem_; } - - const_reference operator*() const { return *elem_; } - - const_pointer operator->() const { return elem_; } - - bool operator==(const Iterator &rhs) const { return elem_ == rhs.elem_; } - - bool operator!=(const Iterator &rhs) const { return elem_ != rhs.elem_; } - - // Prefix increment - Iterator &operator++() { - Node &elem_node = elem_->*(li_.node); - elem_ = elem_node.next; - return *this; - } - - // Postfix increment - Iterator operator++(int junk) { - Iterator tmp(*this); - Node &elem_node = elem_->*(li_.node); - elem_ = elem_node.next; - return tmp; - } - - // Prefix decrement - Iterator &operator--() { - Node &elem_node = elem_->*(li_.node); - elem_ = elem_node.prev; - return *this; - } - - // Postfix decrement - Iterator operator--(int junk) { - Iterator tmp(*this); - Node &elem_node = elem_->*(li_.node); - elem_ = elem_node.prev; - return tmp; - } - - private: - const List &li_; - }; - - Iterator begin() { - Iterator it(*this, head); - return it; - } - - Iterator end() { - Iterator it(*this); - return it; - } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LIST_H_ diff --git a/mindspore-lite/minddata/dataset/util/lock.cc b/mindspore-lite/minddata/dataset/util/lock.cc deleted file mode 100644 index 490834382..000000000 --- a/mindspore-lite/minddata/dataset/util/lock.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/lock.h" - -namespace mindspore { -namespace dataset { -void SpinLock::Lock() { - while (true) { - int expected = kUnlocked; - if (val_.compare_exchange_weak(expected, kLocked)) { - break; - } - } -} - -bool SpinLock::TryLock() { - int expected = kUnlocked; - return val_.compare_exchange_strong(expected, kLocked); -} - -void SpinLock::Unlock() noexcept { val_.store(kUnlocked); } - -void RWLock::LockShared() { - std::unique_lock lck(mtx_); - waiting_readers_ += 1; - read_cv_.wait(lck, [this]() { return (waiting_writers_ == 0 && status_ >= 0); }); - waiting_readers_ -= 1; - status_ += 1; -} - -void RWLock::Unlock() noexcept { - std::unique_lock lck(mtx_); - if (status_ == -1) { - // I am the writer. By definition, no other writer nor reader. - status_ = 0; - } else if (status_ > 0) { - // One less reader - status_ -= 1; - } - // Wake up writer only if there is no reader. - if (waiting_writers_ > 0) { - if (status_ == 0) { - write_cv_.notify_one(); - } - } else { - read_cv_.notify_all(); - } -} - -void RWLock::Upgrade() { - std::unique_lock lck(mtx_); - if (status_ == -1) { - // I am a writer already. - return; - } else if (status_ == 1) { - // If I am the only reader. Just change the status. - status_ = -1; - return; - } else { - // In all other cases, let of the shared lock and relock in exclusive. - lck.unlock(); - this->Unlock(); - this->LockExclusive(); - } -} - -void RWLock::Downgrade() { - std::unique_lock lck(mtx_); - if (status_ == -1) { - // If there are no other writers waiting, just change the status - if (waiting_writers_ == 0) { - status_ = 1; - } else { - // Otherwise just unlock and relock in shared - lck.unlock(); - this->Unlock(); - this->LockShared(); - } - } else if (status_ > 0) { - return; - } -} - -SharedLock::SharedLock(RWLock *rw) : rw_(rw), ownlock_(false) { - rw_->LockShared(); - ownlock_ = true; -} - -SharedLock::~SharedLock() { - if (ownlock_) { - rw_->Unlock(); - ownlock_ = false; - } - rw_ = nullptr; -} - -void SharedLock::Unlock() { - rw_->Unlock(); - ownlock_ = false; -} - -void SharedLock::Lock() { - rw_->LockShared(); - ownlock_ = true; -} - -void SharedLock::Upgrade() { rw_->Upgrade(); } - -void SharedLock::Downgrade() { rw_->Downgrade(); } - -UniqueLock::UniqueLock(RWLock *rw) : rw_(rw), ownlock_(false) { - rw_->LockExclusive(); - ownlock_ = true; -} - -UniqueLock::~UniqueLock() { - if (ownlock_) { - rw_->Unlock(); - ownlock_ = false; - } - rw_ = nullptr; -} - -void UniqueLock::Unlock() { - rw_->Unlock(); - ownlock_ = false; -} - -void UniqueLock::Lock() { - rw_->LockExclusive(); - ownlock_ = true; -} - -LockGuard::LockGuard(SpinLock *lock) : lck_(lock), own_lock_(false) { - lck_->Lock(); - own_lock_ = true; -} - -LockGuard::~LockGuard() { - if (own_lock_) { - lck_->Unlock(); - own_lock_ = false; - } - lck_ = nullptr; -} - -void LockGuard::Unlock() { - lck_->Unlock(); - own_lock_ = false; -} - -void LockGuard::Lock() { - lck_->Lock(); - own_lock_ = true; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/lock.h b/mindspore-lite/minddata/dataset/util/lock.h deleted file mode 100644 index 35f15ef93..000000000 --- a/mindspore-lite/minddata/dataset/util/lock.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOCK_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOCK_H_ - -#include -#include -#include - -namespace mindspore { -namespace dataset { -class SpinLock { - public: - void Lock(); - - bool TryLock(); - - void Unlock() noexcept; - - SpinLock() : val_(kUnlocked) {} - - SpinLock(const SpinLock &) = delete; - - SpinLock(SpinLock &&) = delete; - - ~SpinLock() = default; - - SpinLock &operator=(const SpinLock &) = delete; - - SpinLock &operator=(SpinLock &&) = delete; - - private: - static constexpr int kUnlocked = 0; - static constexpr int kLocked = 1; - std::atomic val_; -}; - -// C++11 has no shared mutex. The following class is an alternative. It favors writer and is suitable for the case -// where writer is rare. -class RWLock { - public: - RWLock() : status_(0), waiting_readers_(0), waiting_writers_(0) {} - - RWLock(const RWLock &) = delete; - - RWLock(RWLock &&) = delete; - - ~RWLock() = default; - - RWLock &operator=(const RWLock &) = delete; - - RWLock &operator=(RWLock &&) = delete; - - void LockShared(); - - void LockExclusive() { - std::unique_lock lck(mtx_); - waiting_writers_ += 1; - write_cv_.wait(lck, [this]() { return status_ == 0; }); - waiting_writers_ -= 1; - status_ = -1; - } - - void Unlock() noexcept; - - // Upgrade a shared lock to exclusive lock - void Upgrade(); - - // Downgrade an exclusive lock to shared lock - void Downgrade(); - - private: - // -1 : one writer - // 0 : no reader and no writer - // n > 0 : n reader - int32_t status_; - int32_t waiting_readers_; - int32_t waiting_writers_; - std::mutex mtx_; - std::condition_variable read_cv_; - std::condition_variable write_cv_; -}; - -// A Wrapper for RWLock. The destructor will release the lock if we own it. -class SharedLock { - public: - explicit SharedLock(RWLock *rw); - - ~SharedLock(); - - SharedLock(const SharedLock &) = delete; - - SharedLock(SharedLock &&) = delete; - - SharedLock &operator=(const SharedLock &) = delete; - - SharedLock &operator=(SharedLock &&) = delete; - - void Unlock(); - - void Lock(); - - void Upgrade(); - - void Downgrade(); - - private: - RWLock *rw_; - bool ownlock_; -}; - -class UniqueLock { - public: - explicit UniqueLock(RWLock *rw); - - ~UniqueLock(); - - UniqueLock(const UniqueLock &) = delete; - - UniqueLock(UniqueLock &&) = delete; - - UniqueLock &operator=(const UniqueLock &) = delete; - - UniqueLock &operator=(UniqueLock &&) = delete; - - void Unlock(); - - void Lock(); - - private: - RWLock *rw_; - bool ownlock_; -}; - -class LockGuard { - public: - explicit LockGuard(SpinLock *lock); - - ~LockGuard(); - - LockGuard(const LockGuard &) = delete; - - LockGuard(LockGuard &&) = delete; - - LockGuard &operator=(const LockGuard &) = delete; - - LockGuard &operator=(LockGuard &&) = delete; - - void Unlock(); - - void Lock(); - - private: - SpinLock *lck_; - bool own_lock_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOCK_H_ diff --git a/mindspore-lite/minddata/dataset/util/log_adapter.h b/mindspore-lite/minddata/dataset/util/log_adapter.h deleted file mode 100644 index c842c000a..000000000 --- a/mindspore-lite/minddata/dataset/util/log_adapter.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOG_ADAPTER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOG_ADAPTER_H_ - -#if !defined(ENABLE_ANDROID) -#include "src/common/log_adapter.h" -#define DATASET_SRC_FILE_NAME FILE_NAME -#else -#include "mindspore-lite/src/common/log_adapter.h" -#define DATASET_SRC_FILE_NAME LITE_FILE_NAME -#endif - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_LOG_ADAPTER_H_ diff --git a/mindspore-lite/minddata/dataset/util/md_log_adapter.cc b/mindspore-lite/minddata/dataset/util/md_log_adapter.cc deleted file mode 100644 index 0a1dd83a1..000000000 --- a/mindspore-lite/minddata/dataset/util/md_log_adapter.cc +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "mindspore-lite/minddata/dataset/util/md_log_adapter.h" - -#include - -namespace mindspore { -namespace dataset { -Status MDLogAdapter::Apply(Status *rc) { - std::string status_msg = ConstructMsg(rc->StatusCode(), rc->CodeAsString(rc->StatusCode()), "", rc->GetLineOfCode(), - rc->GetFileName(), rc->GetErrDescription()); - rc->SetStatusMsg(status_msg); - return *rc; -} - -std::string MDLogAdapter::ConstructMsg(const enum StatusCode &status_code, const std::string &code_as_string, - const std::string &status_msg, const int line_of_code, - const std::string &file_name, const std::string &err_description) { - std::ostringstream ss; - std::string kSplitLine = std::string(66, '-') + "\n"; - std::string err_ori = err_description; - - /// Python Runtime Error - ss << code_as_string << ". \n\n"; - - /// Internal Error - if (err_description.find("[Internal ERROR]") != std::string::npos) { - ss << kSplitLine << "- Framework Unexpected Exception Raised: \n" << kSplitLine; - ss << "This exception is caused by framework's unexpected error. Please create an issue at " - << "https://gitee.com/mindspore/mindspore/issues to get help.\n\n"; - } - - /// Python Stack - std::string user_err; - std::string user_stack; - if (status_code == StatusCode::kMDPyFuncException) { - std::string at_stack = "\n\nAt:\n"; - if (err_ori.find(at_stack) != std::string::npos) { - user_stack = err_ori.substr(0, err_ori.find(at_stack)); - user_err = "Execute user Python code failed, check 'Python Call Stack' above."; - ss << kSplitLine << "- Python Call Stack: \n" << kSplitLine; - ss << user_stack << "\n\n"; - } else { - user_err = err_ori; - } - } - - /// Summary Message - ss << kSplitLine << "- Dataset Pipeline Error Message: \n" << kSplitLine; - if (!user_err.empty()) { - ss << "[ERROR] " + user_err + "\n\n"; - } else { - user_err = err_description; - if (*user_err.rbegin() != '.') { - user_err += '.'; - } - ss << "[ERROR] " + user_err + "\n\n"; - } - - /// C++ Stack - if (!file_name.empty()) { - ss << kSplitLine << "- C++ Call Stack: (For framework developers) \n" << kSplitLine; - std::string cpp_trace = std::string(file_name) + "(" + std::to_string(line_of_code) + ").\n"; - ss << cpp_trace << "\n\n"; - } - - return ss.str(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/md_log_adapter.h b/mindspore-lite/minddata/dataset/util/md_log_adapter.h deleted file mode 100644 index d93ecd782..000000000 --- a/mindspore-lite/minddata/dataset/util/md_log_adapter.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_MD_LOG_ADAPTER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_MD_LOG_ADAPTER_H_ - -#include -#include -#include - -#include "include/api/status.h" - -namespace mindspore { -namespace dataset { -class MDLogAdapter { - public: - MDLogAdapter() = default; - - ~MDLogAdapter() = default; - - static Status Apply(Status *rc); - - static std::string ConstructMsg(const enum StatusCode &status_code, const std::string &code_as_string, - const std::string &status_msg, const int line_of_code, const std::string &file_name, - const std::string &err_description); -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_MD_LOG_ADAPTER_H diff --git a/mindspore-lite/minddata/dataset/util/memory_pool.cc b/mindspore-lite/minddata/dataset/util/memory_pool.cc deleted file mode 100644 index 7f9b0dcaf..000000000 --- a/mindspore-lite/minddata/dataset/util/memory_pool.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" -#include "include/securec.h" - -namespace mindspore { -namespace dataset { -Status DeMalloc(std::size_t s, void **p, bool init_to_zero = false) { - if (p == nullptr) { - RETURN_STATUS_UNEXPECTED("p is null"); - } - void *q = ::malloc(s); - if (q == nullptr) { - RETURN_STATUS_OOM("Out of memory."); - } else { - *p = q; - if (init_to_zero) { - CHECK_FAIL_RETURN_UNEXPECTED(memset_s(q, s, 0, s) == EOK, "Failed to init memory to zero."); - } - return Status::OK(); - } -} -} // namespace dataset -} // namespace mindspore - -void *operator new(std::size_t s, mindspore::Status *rc, std::shared_ptr b) { - void *ptr = nullptr; - *rc = b->Allocate(s, &ptr); - return ptr; -} - -void *operator new[](std::size_t s, mindspore::Status *rc, std::shared_ptr b) { - void *ptr = nullptr; - *rc = b->Allocate(s, &ptr); - return ptr; -} - -void operator delete(void *p, std::shared_ptr b) { - if (p != nullptr) { - b->Deallocate(p); - } -} - -void operator delete[](void *p, std::shared_ptr b) { - if (p != nullptr) { - b->Deallocate(p); - } -} diff --git a/mindspore-lite/minddata/dataset/util/memory_pool.h b/mindspore-lite/minddata/dataset/util/memory_pool.h deleted file mode 100644 index 0da6a45dd..000000000 --- a/mindspore-lite/minddata/dataset/util/memory_pool.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MEMORY_POOL_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MEMORY_POOL_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Abstract class of a memory pool -class MemoryPool { - public: - // Allocate a block of size n - virtual Status Allocate(size_t, void **) = 0; - - // Enlarge or shrink a block from oldSz to newSz - virtual Status Reallocate(void **, size_t old_sz, size_t new_sz) = 0; - - // Free a pointer - virtual void Deallocate(void *) = 0; - - // What is the maximum size I can allocate ? - virtual uint64_t get_max_size() const = 0; - - virtual int PercentFree() const = 0; - - // Destructor - virtual ~MemoryPool() {} -}; - -Status DeMalloc(std::size_t s, void **p, bool); -} // namespace dataset -} // namespace mindspore - -void *operator new(std::size_t, mindspore::Status *, std::shared_ptr); - -void *operator new[](std::size_t, mindspore::Status *, std::shared_ptr); - -void operator delete(void *, std::shared_ptr); - -void operator delete[](void *, std::shared_ptr); - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MEMORY_POOL_H_ diff --git a/mindspore-lite/minddata/dataset/util/monitor.cc b/mindspore-lite/minddata/dataset/util/monitor.cc deleted file mode 100644 index 35edd98db..000000000 --- a/mindspore-lite/minddata/dataset/util/monitor.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/monitor.h" - -#include - -namespace mindspore { -namespace dataset { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -Status MonitorSubprocess(int pid) { - CHECK_FAIL_RETURN_UNEXPECTED(pid != -1, "[Internal ERROR] The subprocess id is -1."); - // get the state changes in a child of the calling process - int status = 0; - auto ret = waitpid(pid, &status, WNOHANG | WUNTRACED | WCONTINUED); - if (ret != 0) { // the status of subprocess is changed - uint32_t uint_status = static_cast(status); - if (WIFEXITED(uint_status)) { // the child is exit normal - std::string err_msg = "[Monitor] The sub-process: " + std::to_string(pid) + - " exits. Status: " + std::to_string(WEXITSTATUS(uint_status)) + - ". Errno: " + std::to_string(errno); - RETURN_STATUS_UNEXPECTED(err_msg); - } else if (WIFSIGNALED(uint_status)) { // if the child process was terminated by a signal - std::string err_msg = "[Monitor] The sub-process: " + std::to_string(pid) + - " is terminated by a signal abnormally. Signal: " + std::to_string(WTERMSIG(uint_status)) + - ". Errno: " + std::to_string(errno); - RETURN_STATUS_UNEXPECTED(err_msg); - } else if (WIFSTOPPED(uint_status)) { // if the child process was stopped by delivery of a signal - std::string err_msg = - "[Monitor] The sub-process: " + std::to_string(pid) + - " is stopped by delivery of a signal abnormally. Signal: " + std::to_string(WSTOPSIG(uint_status)) + - ". Errno: " + std::to_string(errno); - RETURN_STATUS_UNEXPECTED(err_msg); - } else if (WIFCONTINUED(uint_status)) { // returns true if the child process was resumed by delivery of SIGCONT - MS_LOG(INFO) << "[Monitor] The sub-process: " + std::to_string(pid) + " is resumed."; - } else { - MS_LOG(INFO) << "[Monitor] The sub-process: " + std::to_string(pid) + - " has generated a new status: " + std::to_string(uint_status); - } - } else { - MS_LOG(INFO) << "[Monitor] The sub-process: " << std::to_string(pid) - << " is still running. The state is not changed."; - } - return Status::OK(); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/monitor.h b/mindspore-lite/minddata/dataset/util/monitor.h deleted file mode 100644 index 2cbeee82e..000000000 --- a/mindspore-lite/minddata/dataset/util/monitor.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MONITOR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MONITOR_H_ - -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -#include -#include -#include -#endif - -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) -// Monitor the Subprocess status to confirm it's still alive -Status MonitorSubprocess(int pid); -#endif -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_MONITOR_H_ diff --git a/mindspore-lite/minddata/dataset/util/path.cc b/mindspore-lite/minddata/dataset/util/path.cc deleted file mode 100644 index 7ebc7265f..000000000 --- a/mindspore-lite/minddata/dataset/util/path.cc +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/path.h" - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#include // for _mkdir -#define stat _stat64 // for file size exceeds (1<<31)-1 bytes -#endif - -#include "include/securec.h" -#ifndef BUILD_LITE -#include "utils/file_utils.h" -#else -#include "mindspore-lite/src/common/file_utils.h" -#endif -#include "utils/ms_utils.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -#if defined(_WIN32) || defined(_WIN64) -char Path::separator_ = '\\'; -#else -char Path::separator_ = '/'; -#endif - -Path::Path(const std::string &s) { -#if defined(_WIN32) || defined(_WIN64) - path_ = FileUtils::UTF_8ToGB2312(s.data()); -#else - path_ = s; -#endif -} - -Path::Path(const char *p) { -#if defined(_WIN32) || defined(_WIN64) - path_ = FileUtils::UTF_8ToGB2312(p); -#else - path_ = p; -#endif -} - -Path::Path(const Path &p) = default; - -Path &Path::operator=(const Path &p) { - if (&p != this) { - this->path_ = p.path_; - } - return *this; -} - -Path &Path::operator=(Path &&p) noexcept { - if (&p != this) { - this->path_ = std::move(p.path_); - } - return *this; -} - -Path::Path(Path &&p) noexcept { this->path_ = std::move(p.path_); } - -Path Path::operator+(const Path &p) { - std::string q = path_ + p.ToString(); - return Path(q); -} - -Path Path::operator+(const std::string &p) { - std::string q = path_ + p; - return Path(q); -} - -Path Path::operator+(const char *p) { - std::string q = path_ + p; - return Path(q); -} - -Path &Path::operator+=(const Path &rhs) { - path_ += rhs.ToString(); - return *this; -} - -Path &Path::operator+=(const std::string &p) { -#if defined(_WIN32) || defined(_WIN64) - path_ += FileUtils::UTF_8ToGB2312(p.data()); -#else - path_ += p; -#endif - return *this; -} - -Path &Path::operator+=(const char *p) { -#if defined(_WIN32) || defined(_WIN64) - path_ += FileUtils::UTF_8ToGB2312(p); -#else - path_ += p; -#endif - return *this; -} - -Path Path::operator/(const Path &p) { - std::string q = path_ + separator_ + p.ToString(); - return Path(q); -} - -Path Path::operator/(const std::string &p) { - std::string q = path_ + separator_ + p; - return Path(q); -} - -Path Path::operator/(const char *p) { - std::string q = path_ + separator_ + p; - return Path(q); -} - -std::string Path::Extension() const { - std::size_t found = path_.find_last_of('.'); - if (found != std::string::npos) { - return path_.substr(found); - } else { - return std::string(""); - } -} - -bool Path::Exists() { - struct stat sb {}; - int rc = stat(common::SafeCStr(path_), &sb); - if (rc == -1 && errno != ENOENT) { - MS_LOG(WARNING) << "Unable to query the status of " << path_ << ". Errno = " << errno << "."; - } - return (rc == 0); -} - -bool Path::IsDirectory() { - struct stat sb {}; - int rc = stat(common::SafeCStr(path_), &sb); - if (rc == 0) { - return S_ISDIR(sb.st_mode); - } else { - return false; - } -} - -bool Path::IsFile() { - struct stat sb {}; - int rc = stat(common::SafeCStr(path_), &sb); - if (rc == 0) { - return S_ISREG(sb.st_mode); - } else { - return false; - } -} - -Status Path::CreateDirectory(bool is_common_dir) { - if (!Exists()) { -#if defined(_WIN32) || defined(_WIN64) -#ifndef _MSC_VER - int rc = mkdir(common::SafeCStr(path_)); -#else - int rc = _mkdir(common::SafeCStr(path_)); -#endif -#else - int rc = mkdir(common::SafeCStr(path_), S_IRUSR | S_IWUSR | S_IXUSR); -#endif - if (rc) { - std::ostringstream oss; - oss << "Unable to create directory " << path_ << ". Errno = " << errno; - RETURN_STATUS_UNEXPECTED(oss.str()); - } - return Status::OK(); - } else { - if (IsDirectory()) { - return Status::OK(); - } else { - std::ostringstream oss; - oss << "Unable to create directory " << path_ << ". It exists but is not a directory"; - RETURN_STATUS_UNEXPECTED(oss.str()); - } - } -} - -std::string Path::ParentPath() { - std::string r; - std::size_t found = path_.find_last_of(separator_); - if (found != std::string::npos) { - if (found == 0) { - r += separator_; - } else { - r = std::string(path_.substr(0, found)); - } - } - return r; -} - -Status Path::CreateDirectories(bool is_common_dir) { - if (IsDirectory()) { - MS_LOG(DEBUG) << "Directory " << ToString() << " already exists."; - return Status::OK(); - } else { - MS_LOG(DEBUG) << "Creating directory " << ToString() << "."; - std::string parent = ParentPath(); - if (!parent.empty()) { - if (Path(parent).CreateDirectories(is_common_dir)) { - return CreateDirectory(is_common_dir); - } - } else { - return CreateDirectory(is_common_dir); - } - } - return Status::OK(); -} - -Status Path::CreateCommonDirectories() { return CreateDirectories(true); } - -Status Path::Remove() { - if (Exists()) { - if (IsDirectory()) { -#ifndef _MSC_VER - errno_t err = rmdir(common::SafeCStr(path_)); -#else - errno_t err = _rmdir(common::SafeCStr(path_)); -#endif - if (err == -1) { - std::ostringstream oss; - oss << "Unable to delete directory " << path_ << ". Errno = " << errno; - RETURN_STATUS_UNEXPECTED(oss.str()); - } - } else { - errno_t err = unlink(common::SafeCStr(path_)); - if (err == -1) { - std::ostringstream oss; - oss << "Unable to delete file " << path_ << ". Errno = " << errno; - RETURN_STATUS_UNEXPECTED(oss.str()); - } - } - } - return Status::OK(); -} - -Status Path::CreateFile(int *file_descriptor) { return OpenFile(file_descriptor, true); } - -Status Path::OpenFile(int *file_descriptor, bool create) { - int fd; - if (file_descriptor == nullptr) { - RETURN_STATUS_UNEXPECTED("null pointer"); - } - if (IsDirectory()) { - std::ostringstream oss; - oss << "Unable to create file " << path_ << " which is a directory."; - RETURN_STATUS_UNEXPECTED(oss.str()); - } - // Convert to canonical form. - if (strlen(common::SafeCStr(path_)) >= PATH_MAX) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - char canonical_path[PATH_MAX] = {0x00}; -#if defined(_WIN32) || defined(_WIN64) - auto err = _fullpath(canonical_path, common::SafeCStr(path_), PATH_MAX); -#else - auto err = realpath(common::SafeCStr(path_), canonical_path); -#endif - if (err == nullptr) { - if (errno == ENOENT && create) { - // File doesn't exist and we are to create it. Let's break it down. - auto file_part = Basename(); - auto parent_part = ParentPath(); -#if defined(_WIN32) || defined(_WIN64) - auto parent_err = _fullpath(canonical_path, common::SafeCStr(parent_part), PATH_MAX); -#else - auto parent_err = realpath(common::SafeCStr(parent_part), canonical_path); -#endif - if (parent_err == nullptr) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - auto cur_inx = strlen(canonical_path); - if (cur_inx + file_part.length() >= PATH_MAX) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - canonical_path[cur_inx++] = separator_; - if (strncpy_s(canonical_path + cur_inx, PATH_MAX - cur_inx, common::SafeCStr(file_part), file_part.length()) != - EOK) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - } else { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - } - if (create) { - fd = open(canonical_path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); - } else { - fd = open(canonical_path, O_RDWR); - } - if (fd == -1) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - *file_descriptor = fd; - return Status::OK(); -} - -Status Path::CloseFile(int fd) const { - if (close(fd) < 0) { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } - return Status::OK(); -} - -Status Path::TruncateFile(int fd) const { -#ifdef _MSC_VER - int rc = _chsize(fd, 0); -#else - int rc = ftruncate(fd, 0); -#endif - if (rc == 0) { - return Status::OK(); - } else { - RETURN_STATUS_UNEXPECTED(strerror(errno)); - } -} - -std::string Path::Basename() { - std::size_t found = path_.find_last_of(separator_); - if (found != std::string::npos) { - return path_.substr(found + 1); - } else { - return path_; - } -} - -std::shared_ptr Path::DirIterator::OpenDirectory(Path *f) { - auto it = new (std::nothrow) DirIterator(f); - - if (it == nullptr) { - return nullptr; - } - - if (it->dp_) { - return std::shared_ptr(it); - } else { - delete it; - return nullptr; - } -} - -Path::DirIterator::~DirIterator() { - if (dp_) { - (void)closedir(dp_); - } - dp_ = nullptr; - dir_ = nullptr; - entry_ = nullptr; -} - -Path::DirIterator::DirIterator(Path *f) : dir_(f), dp_(nullptr), entry_(nullptr) { - MS_LOG(DEBUG) << "Open directory " << f->ToString() << "."; - dp_ = opendir(f->ToString().c_str()); -} - -bool Path::DirIterator::HasNext() { - do { - entry_ = readdir(dp_); - if (entry_) { - if (strcmp(entry_->d_name, ".") == 0 || strcmp(entry_->d_name, "..") == 0) { - continue; - } - } - break; - } while (true); - return (entry_ != nullptr); -} - -Path Path::DirIterator::Next() { return (*(this->dir_) / Path(entry_->d_name)); } - -Status Path::RealPath(const std::string &path, std::string &realpath_str) { - char real_path[PATH_MAX] = {0}; - // input_path is only file_name -#if defined(_WIN32) || defined(_WIN64) - CHECK_FAIL_RETURN_UNEXPECTED(path.length() < PATH_MAX, - "The length of path: " + path + " exceeds limit: " + std::to_string(PATH_MAX)); - auto ret = _fullpath(real_path, common::SafeCStr(path), PATH_MAX); - CHECK_FAIL_RETURN_UNEXPECTED(ret != nullptr, "The file " + path + " does not exist."); -#else - CHECK_FAIL_RETURN_UNEXPECTED(path.length() < NAME_MAX, - "The length of path: " + path + " exceeds limit: " + std::to_string(NAME_MAX)); - auto ret = realpath(common::SafeCStr(path), real_path); - CHECK_FAIL_RETURN_UNEXPECTED(ret != nullptr, "The file " + path + " does not exist."); -#endif - realpath_str = std::string(real_path); - return Status::OK(); -} - -std::ostream &operator<<(std::ostream &os, const Path &s) { - os << s.path_; - return os; -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/path.h b/mindspore-lite/minddata/dataset/util/path.h deleted file mode 100644 index ae0c690db..000000000 --- a/mindspore-lite/minddata/dataset/util/path.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_PATH_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_PATH_H_ - -#include -#include -#include "utils/os.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class Path { - public: - class DirIterator { - public: - static std::shared_ptr OpenDirectory(Path *f); - - ~DirIterator(); - - bool HasNext(); - - Path Next(); - - private: - explicit DirIterator(Path *f); - - Path *dir_ = nullptr; - DIR *dp_ = nullptr; - struct dirent *entry_ = nullptr; - }; - - explicit Path(const std::string &); - - explicit Path(const char *); - - ~Path() = default; - - Path(const Path &); - - Path &operator=(const Path &); - - Path(Path &&) noexcept; - - Path &operator=(Path &&) noexcept; - - std::string ToString() const { return path_; } - - Path operator+(const Path &); - - Path operator+(const std::string &); - - Path operator+(const char *); - - Path &operator+=(const Path &rhs); - - Path &operator+=(const std::string &); - - Path &operator+=(const char *); - - Path operator/(const Path &); - - Path operator/(const std::string &); - - Path operator/(const char *); - - bool operator==(const Path &rhs) const { return (path_ == rhs.path_); } - - bool operator!=(const Path &rhs) const { return (path_ != rhs.path_); } - - bool operator<(const Path &rhs) const { return (path_ < rhs.path_); } - - bool operator>(const Path &rhs) const { return (path_ > rhs.path_); } - - bool operator<=(const Path &rhs) const { return (path_ <= rhs.path_); } - - bool operator>=(const Path &rhs) const { return (path_ >= rhs.path_); } - - bool Exists(); - - bool IsDirectory(); - - bool IsFile(); - - Status CreateDirectory(bool is_common_dir = false); - - Status CreateDirectories(bool is_common_dir = false); - - Status CreateCommonDirectories(); - - std::string Extension() const; - - std::string ParentPath(); - - Status Remove(); - - Status CreateFile(int *fd); - - Status OpenFile(int *fd, bool create = false); - - Status CloseFile(int fd) const; - - Status TruncateFile(int fd) const; - - std::string Basename(); - - static Status RealPath(const std::string &path, std::string &realpath_str); // NOLINT - - friend std::ostream &operator<<(std::ostream &os, const Path &s); - - private: - static char separator_; - std::string path_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_PATH_H_ diff --git a/mindspore-lite/minddata/dataset/util/queue.h b/mindspore-lite/minddata/dataset/util/queue.h deleted file mode 100644 index 20dd0c255..000000000 --- a/mindspore-lite/minddata/dataset/util/queue.h +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_H_ - -#include -#include -#include -#include -#include - -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/cond_var.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -// A simple thread safe queue using a fixed size array -template -class Queue { - public: - using value_type = T; - using pointer = T *; - using const_pointer = const T *; - using reference = T &; - using const_reference = const T &; - - explicit Queue(int sz) - : sz_(sz), arr_(Services::GetAllocator()), head_(0), tail_(0), my_name_(Services::GetUniqueID()) { - Status rc = arr_.allocate(sz); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to create a queue."; - std::terminate(); - } else { - MS_LOG(DEBUG) << "Create Q with uuid " << my_name_ << " of size " << sz_ << "."; - } - } - - virtual ~Queue() { ResetQue(); } - - size_t size() const { - std::unique_lock _lock(mux_); - size_t v = 0; - if (tail_ >= head_) { - v = tail_ - head_; - } - return v; - } - - size_t capacity() const { - std::unique_lock _lock(mux_); - return sz_; - } - - bool empty() const { - std::unique_lock _lock(mux_); - return head_ == tail_; - } - - void Reset() { - std::unique_lock _lock(mux_); - ResetQue(); - extra_arr_.clear(); - } - - // Producer - Status Add(const_reference ele) noexcept { - std::unique_lock _lock(mux_); - // Block when full - Status rc = - full_cv_.Wait(&_lock, [this]() -> bool { return (SizeWhileHoldingLock() != CapacityWhileHoldingLock()); }); - if (rc.IsOk()) { - this->AddWhileHoldingLock(ele); - empty_cv_.NotifyAll(); - _lock.unlock(); - } else { - empty_cv_.Interrupt(); - } - return rc; - } - - Status Add(T &&ele) noexcept { - std::unique_lock _lock(mux_); - // Block when full - Status rc = - full_cv_.Wait(&_lock, [this]() -> bool { return (SizeWhileHoldingLock() != CapacityWhileHoldingLock()); }); - if (rc.IsOk()) { - this->AddWhileHoldingLock(std::forward(ele)); - empty_cv_.NotifyAll(); - _lock.unlock(); - } else { - empty_cv_.Interrupt(); - } - return rc; - } - - template - Status EmplaceBack(Ts &&... args) noexcept { - std::unique_lock _lock(mux_); - // Block when full - Status rc = - full_cv_.Wait(&_lock, [this]() -> bool { return (SizeWhileHoldingLock() != CapacityWhileHoldingLock()); }); - if (rc.IsOk()) { - auto k = tail_++ % sz_; - new (arr_[k]) T(std::forward(args)...); - empty_cv_.NotifyAll(); - _lock.unlock(); - } else { - empty_cv_.Interrupt(); - } - return rc; - } - - // Consumer - virtual Status PopFront(pointer p) { - std::unique_lock _lock(mux_); - // Block when empty - Status rc = empty_cv_.Wait(&_lock, [this]() -> bool { return !EmptyWhileHoldingLock(); }); - if (rc.IsOk()) { - this->PopFrontWhileHoldingLock(p, true); - full_cv_.NotifyAll(); - _lock.unlock(); - } else { - full_cv_.Interrupt(); - } - return rc; - } - - Status Register(TaskGroup *vg) { - Status rc1 = empty_cv_.Register(vg->GetIntrpService()); - Status rc2 = full_cv_.Register(vg->GetIntrpService()); - if (rc1.IsOk()) { - return rc2; - } else { - return rc1; - } - } - - Status Resize(int32_t new_capacity) { - std::unique_lock _lock(mux_); - CHECK_FAIL_RETURN_UNEXPECTED(new_capacity > 0, - "New capacity: " + std::to_string(new_capacity) + ", should be larger than 0"); - RETURN_OK_IF_TRUE(new_capacity == static_cast(CapacityWhileHoldingLock())); - std::vector queue; - // pop from the original queue until the new_capacity is full - for (int32_t i = 0; i < new_capacity; ++i) { - if (head_ < tail_) { - // if there are elements left in queue, pop out - T temp; - this->PopFrontWhileHoldingLock(&temp, true); - queue.push_back(temp); - } else { - // if there is nothing left in queue, check extra_arr_ - if (!extra_arr_.empty()) { - // if extra_arr_ is not empty, push to fill the new_capacity - queue.push_back(extra_arr_[0]); - extra_arr_.erase(extra_arr_.begin()); - } else { - // if everything in the queue and extra_arr_ is popped out, break the loop - break; - } - } - } - // if there are extra elements in queue, put them to extra_arr_ - while (head_ < tail_) { - T temp; - this->PopFrontWhileHoldingLock(&temp, false); - extra_arr_.push_back(temp); - } - this->ResetQue(); - RETURN_IF_NOT_OK(arr_.allocate(new_capacity)); - sz_ = new_capacity; - for (int32_t i = 0; i < static_cast(queue.size()); ++i) { - this->AddWhileHoldingLock(queue[i]); - } - queue.clear(); - _lock.unlock(); - return Status::OK(); - } - - private: - size_t sz_; - MemGuard> arr_; - std::vector extra_arr_; // used to store extra elements after reducing capacity, will not be changed by Add, - // will pop when there is a space in queue (by PopFront or Resize) - size_t head_; - size_t tail_; - std::string my_name_; - mutable std::mutex mux_; - CondVar empty_cv_; - CondVar full_cv_; - - // Helper function for Add, must be called when holding a lock - void AddWhileHoldingLock(const_reference ele) { - auto k = tail_++ % sz_; - *(arr_[k]) = ele; - } - - // Helper function for Add, must be called when holding a lock - void AddWhileHoldingLock(T &&ele) { - auto k = tail_++ % sz_; - *(arr_[k]) = std::forward(ele); - } - - // Helper function for PopFront, must be called when holding a lock - void PopFrontWhileHoldingLock(pointer p, bool clean_extra) { - auto k = head_++ % sz_; - *p = std::move(*(arr_[k])); - if (!extra_arr_.empty() && clean_extra) { - this->AddWhileHoldingLock(std::forward(extra_arr_[0])); - extra_arr_.erase(extra_arr_.begin()); - } - } - - void ResetQue() noexcept { - while (head_ < tail_) { - T val; - this->PopFrontWhileHoldingLock(&val, false); - MS_LOG(DEBUG) << "Address of val: " << &val; - } - empty_cv_.ResetIntrpState(); - full_cv_.ResetIntrpState(); - head_ = 0; - tail_ = 0; - } - - size_t SizeWhileHoldingLock() const { - size_t v = 0; - if (tail_ >= head_) { - v = tail_ - head_; - } - return v; - } - - size_t CapacityWhileHoldingLock() const { return sz_; } - - bool EmptyWhileHoldingLock() const { return head_ == tail_; } -}; - -// A container of queues with [] operator accessors. Basically this is a wrapper over of a vector of queues -// to help abstract/simplify code that is maintaining multiple queues. -template -class QueueList { - public: - QueueList() {} - - void Init(int num_queues, int capacity) { - (void)queue_list_.reserve(num_queues); - for (int i = 0; i < num_queues; i++) { - (void)queue_list_.emplace_back(std::make_unique>(capacity)); - } - } - - Status Register(TaskGroup *vg) { - if (vg == nullptr) { - RETURN_STATUS_UNEXPECTED("Null task group during QueueList registration."); - } - for (int i = 0; i < queue_list_.size(); ++i) { - RETURN_IF_NOT_OK(queue_list_[i]->Register(vg)); - } - return Status::OK(); - } - - auto size() const { - std::unique_lock _lock(mux_); - return queue_list_.size(); - } - - std::unique_ptr> &operator[](const int index) { - std::unique_lock _lock(mux_); - return queue_list_[index]; - } - - const std::unique_ptr> &operator[](const int index) const { - std::unique_lock _lock(mux_); - return queue_list_[index]; - } - - ~QueueList() = default; - - Status AddQueue(TaskGroup *vg) { - std::unique_lock _lock(mux_); - (void)queue_list_.emplace_back(std::make_unique>(queue_list_[0]->capacity())); - return queue_list_[queue_list_.size() - 1]->Register(vg); - } - Status RemoveLastQueue() { - std::unique_lock _lock(mux_); - CHECK_FAIL_RETURN_UNEXPECTED(queue_list_.size() > 1, "Cannot remove more than the current queues."); - (void)queue_list_.pop_back(); - return Status::OK(); - } - - private: - // Queue contains non-copyable objects, so it cannot be added to a vector due to the vector - // requirement that objects must have copy semantics. To resolve this, we use a vector of unique - // pointers. This allows us to provide dynamic creation of queues in a container. - std::vector>> queue_list_; - - mutable std::mutex mux_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_H_ diff --git a/mindspore-lite/minddata/dataset/util/queue_map.h b/mindspore-lite/minddata/dataset/util/queue_map.h deleted file mode 100644 index 52bd3795c..000000000 --- a/mindspore-lite/minddata/dataset/util/queue_map.h +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_MAP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_MAP_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/system_pool.h" -#include "mindspore-lite/minddata/dataset/util/semaphore.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -namespace mindspore { -namespace dataset { -template -/// \brief QueueMap is like a Queue but instead of there is a map of deque. -/// Consumer will block if the corresponding deque is empty. -/// Producer can add an element of type T with key of type K to the map and -/// wake up any waiting consumer. -/// \tparam K key type -/// \tparam T payload of the map -class QueueMap { - public: - using key_type = K; - using value_type = T; - - QueueMap() : num_rows_(0) {} - virtual ~QueueMap() = default; - - /// Add an element to the map and wake up any consumer that is waiting - /// \param key - /// \param payload - /// \return Status object - virtual Status Add(key_type key, T &&payload) { - RequestQueue *rq = nullptr; - RETURN_IF_NOT_OK(GetRq(key, &rq)); - RETURN_IF_NOT_OK(rq->WakeUpAny(std::move(payload))); - ++num_rows_; - return Status::OK(); - } - - /// Pop the front of the deque with key. Block if the deque is empty. - virtual Status PopFront(key_type key, T *out) { - RequestQueue *rq = nullptr; - RETURN_IF_NOT_OK(GetRq(key, &rq)); - RETURN_IF_NOT_OK(rq->Wait(out)); - --num_rows_; - return Status::OK(); - } - - /// Get the number of elements in the container - /// \return The number of elements in the container - int64_t size() const { return num_rows_; } - - /// \return if the container is empty - bool empty() const { return num_rows_ == 0; } - - /// Print out some useful information about the container - friend std::ostream &operator<<(std::ostream &out, const QueueMap &qm) { - std::unique_lock lck(qm.mux_); - out << "Number of elements: " << qm.num_rows_ << "\n"; - out << "Dumping internal info:\n"; - int64_t k = 0; - constexpr int64_t line_breaks_number = 6; - for (auto &it : qm.all_) { - auto key = it.first; - const RequestQueue *rq = it.second.GetPointer(); - out << "(k:" << key << "," << *rq << ") "; - ++k; - if (k % line_breaks_number == 0) { - out << "\n"; - } - } - return out; - } - - protected: - /// This is a handshake structure between producer and consumer - class RequestQueue { - public: - RequestQueue() : use_count_(0) {} - ~RequestQueue() = default; - - Status Wait(T *out) { - RETURN_UNEXPECTED_IF_NULL(out); - // Block until the missing row is in the pool. - RETURN_IF_NOT_OK(use_count_.P()); - std::unique_lock lck(dq_mux_); - CHECK_FAIL_RETURN_UNEXPECTED(!row_.empty(), "Programming error"); - *out = std::move(row_.front()); - row_.pop_front(); - return Status::OK(); - } - - Status WakeUpAny(T &&row) { - std::unique_lock lck(dq_mux_); - row_.push_back(std::move(row)); - // Bump up the use count by 1. This wake up any parallel worker which is waiting - // for this row. - use_count_.V(); - return Status::OK(); - } - - friend std::ostream &operator<<(std::ostream &out, const RequestQueue &rq) { - out << "sz:" << rq.row_.size() << ",uc:" << rq.use_count_.Peek(); - return out; - } - - private: - mutable std::mutex dq_mux_; - Semaphore use_count_; - std::deque row_; - }; - - /// Create or locate an element with matching key - /// \param key - /// \param out - /// \return Status object - Status GetRq(key_type key, RequestQueue **out) { - RETURN_UNEXPECTED_IF_NULL(out); - std::unique_lock lck(mux_); - auto it = all_.find(key); - if (it != all_.end()) { - *out = it->second.GetMutablePointer(); - } else { - // We will create a new one. - auto alloc = SystemPool::GetAllocator(); - auto r = all_.emplace(key, MemGuard>(alloc)); - if (r.second) { - auto &mem = r.first->second; - RETURN_IF_NOT_OK(mem.allocate(1)); - *out = mem.GetMutablePointer(); - } else { - RETURN_STATUS_UNEXPECTED("Map insert fail."); - } - } - return Status::OK(); - } - - private: - mutable std::mutex mux_; - std::map>> all_; - std::atomic num_rows_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_QUEUE_MAP_H_ diff --git a/mindspore-lite/minddata/dataset/util/random.h b/mindspore-lite/minddata/dataset/util/random.h deleted file mode 100644 index 54604fa11..000000000 --- a/mindspore-lite/minddata/dataset/util/random.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RANDOM_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RANDOM_H_ - -#if defined(_WIN32) || defined(_WIN64) -#ifndef _CRT_RAND_S -#define _CRT_RAND_S -#endif -#include -#endif -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/core/config_manager.h" -#include "mindspore-lite/minddata/dataset/core/global_context.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -inline std::mt19937 GetRandomDevice() { -#if defined(_WIN32) || defined(_WIN64) - unsigned int number; - rand_s(&number); - std::mt19937 random_device{static_cast(number)}; -#else - int i = 0; - constexpr int64_t retry_times = 5; - while (i < retry_times) { - try { - std::mt19937 random_device{std::random_device("/dev/urandom")()}; - return random_device; - } catch (const std::exception &e) { - MS_LOG(WARNING) << "Get std::random_device failed, retry: " << i << ", error: " << e.what(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - i++; - } - } - std::mt19937 random_device{std::random_device("/dev/urandom")()}; -#endif - return random_device; -} - -inline uint32_t GetNewSeed() { - std::mt19937 random_device = GetRandomDevice(); - std::uniform_int_distribution distribution(0, std::numeric_limits::max()); - return distribution(random_device); -} - -inline uint32_t GetSeed() { - uint32_t seed = GlobalContext::config_manager()->seed(); - if (seed == std::mt19937::default_seed) { - seed = GetNewSeed(); - } - return seed; -} - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RANDOM_H_ diff --git a/mindspore-lite/minddata/dataset/util/rdr.cc b/mindspore-lite/minddata/dataset/util/rdr.cc deleted file mode 100644 index aa3750769..000000000 --- a/mindspore-lite/minddata/dataset/util/rdr.cc +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/rdr.h" - -#include - -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -const int32_t kMdRdrRecordLimit = 10; - -std::string MDChannelInfo::ToString() { - std::ostringstream ss; - ss << "have_sent: " << preprocess_batch_ << "; "; - ss << "host_queue: "; - for (uint32_t i = 0; i < batch_queue_.size(); i++) { - ss << batch_queue_.at(i); - if (i < batch_queue_.size() - 1) { - ss << ", "; - } - } - - ss << "; push_start_time: "; - for (uint32_t i = 0; i < push_start_time_.size(); i++) { - ss << push_start_time_.at(i); - if (i < push_start_time_.size() - 1) { - ss << ", "; - } - } - - ss << "; push_end_time: "; - for (uint32_t i = 0; i < push_end_time_.size(); i++) { - ss << push_end_time_.at(i); - if (i < push_end_time_.size() - 1) { - ss << ", "; - } - } - ss << "."; - return ss.str(); -} - -std::string MDChannelInfo::ToFormatString() { - std::ostringstream ss; - // the output like below: - // channel_name: 29475464-f51b-11ee-b72b-8feb6783b0c3 - // have_sent: 282; - // host_queue: 64, 64, 64, 63, 64, 64, 64, 63, 64, 64; - // device_queue: 99, 99, 99, 99, 98, 99, 97, 99, 98, 99; - // push_first_start_time -> push_first_end_time - // 2022-05-09-14:29:12.110.276 -> 2022-05-09-14:29:12.439.621 - // push_start_time -> push_end_time - // -> 2022-05-09-14:31:00.603.866 - // 2022-05-09-14:31:00.621.146 -> 2022-05-09-14:31:01.018.964 - // 2022-05-09-14:31:01.043.705 -> 2022-05-09-14:31:01.396.650 - // 2022-05-09-14:31:01.421.501 -> 2022-05-09-14:31:01.807.671 - // 2022-05-09-14:31:01.828.931 -> 2022-05-09-14:31:02.179.945 - // 2022-05-09-14:31:02.201.960 -> 2022-05-09-14:31:02.555.941 - // 2022-05-09-14:31:02.584.413 -> 2022-05-09-14:31:02.943.839 - // 2022-05-09-14:31:02.969.583 -> 2022-05-09-14:31:03.309.299 - // 2022-05-09-14:31:03.337.607 -> 2022-05-09-14:31:03.684.034 - // 2022-05-09-14:31:03.717.230 -> 2022-05-09-14:31:04.038.521 - // 2022-05-09-14:31:04.064.571 -> - ss << "\n"; - ss << "channel_name: " << channel_name_ << ";\n"; - ss << "have_sent: " << preprocess_batch_ << ";\n"; - ss << "host_queue: "; - for (uint32_t i = 0; i < batch_queue_.size(); i++) { - ss << batch_queue_.at(i); - if (i < batch_queue_.size() - 1) { - ss << ", "; - } - } - ss << ";\n"; - ss << "device_queue: "; - for (uint32_t i = 0; i < device_queue_.size(); i++) { - ss << device_queue_.at(i); - if (i < device_queue_.size() - 1) { - ss << ", "; - } - } - ss << ";\n"; - ss << " push_first_start_time -> push_first_end_time\n"; - ss << push_first_start_time_ << " -> " << push_first_end_time_ << "\n"; - ss << " push_start_time -> push_end_time\n"; - if (!push_start_time_.empty()) { - if (!push_end_time_.empty()) { - // start_time[0] bigger than end_time[0] - uint32_t end_time_index = 0; - if (push_start_time_.at(0) > push_end_time_.at(0)) { - ss << " -> " << push_end_time_.at(end_time_index) << "\n"; - end_time_index = 1; - } - for (uint32_t i = 0; i < push_start_time_.size(); i++, end_time_index++) { - ss << push_start_time_.at(i) << " -> "; - if (end_time_index < push_end_time_.size()) { - ss << push_end_time_.at(end_time_index); - } - ss << "\n"; - } - } else { - ss << push_start_time_.at(0) << " -> \n"; // only one start time without end time - } - } - ss << "For more details, please refer to the FAQ at " - << "https://www.mindspore.cn/docs/en/master/faq/data_processing.html."; - return ss.str(); -} - -Status MDChannelInfo::RecordBatchQueue(int64_t batch_queue_size) { - if (batch_queue_.size() == kMdRdrRecordLimit) { - batch_queue_.pop_front(); - } - batch_queue_.push_back(batch_queue_size); - return Status::OK(); -} - -Status MDChannelInfo::RecordDeviceQueue(int64_t device_queue_size) { - if (device_queue_.size() == kMdRdrRecordLimit) { - device_queue_.pop_front(); - } - device_queue_.push_back(device_queue_size); - return Status::OK(); -} - -Status MDChannelInfo::RecordPreprocessBatch(int64_t preprocess_batch) { - preprocess_batch_ = preprocess_batch; - return Status::OK(); -} - -Status MDChannelInfo::RecordPushFirstStartTime() { - push_first_start_time_ = GetTimeString(); - return Status::OK(); -} - -Status MDChannelInfo::RecordPushFirstEndTime() { - push_first_end_time_ = GetTimeString(); - return Status::OK(); -} - -Status MDChannelInfo::RecordPushStartTime() { - if (push_start_time_.size() == kMdRdrRecordLimit) { - push_start_time_.pop_front(); - } - push_start_time_.push_back(GetTimeString()); - return Status::OK(); -} - -Status MDChannelInfo::RecordPushEndTime() { - if (push_end_time_.size() == kMdRdrRecordLimit) { - push_end_time_.pop_front(); - } - push_end_time_.push_back(GetTimeString()); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/rdr.h b/mindspore-lite/minddata/dataset/util/rdr.h deleted file mode 100644 index c8f3861ec..000000000 --- a/mindspore-lite/minddata/dataset/util/rdr.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RDR_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RDR_H_ - -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class MDChannelInfo { - public: - explicit MDChannelInfo(std::string channel_name) - : channel_name_(channel_name), preprocess_batch_(0), push_first_start_time_("-1"), push_first_end_time_("-1") {} - - ~MDChannelInfo() = default; - - std::string ToString(); - - std::string ToFormatString(); - - Status RecordBatchQueue(int64_t batch_queue_size); - - Status RecordDeviceQueue(int64_t device_queue_size); - - Status RecordPreprocessBatch(int64_t preprocess_batch); - - Status RecordPushFirstStartTime(); - - Status RecordPushFirstEndTime(); - - Status RecordPushStartTime(); - - Status RecordPushEndTime(); - - private: - std::string channel_name_; - std::deque batch_queue_; - std::deque device_queue_; - int64_t preprocess_batch_; - std::string push_first_start_time_; // record the push start time for first batch - std::string push_first_end_time_; // record the push end time for first batch - std::deque push_start_time_; - std::deque push_end_time_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_RDR_H_ diff --git a/mindspore-lite/minddata/dataset/util/semaphore.cc b/mindspore-lite/minddata/dataset/util/semaphore.cc deleted file mode 100644 index 40a5a4bf3..000000000 --- a/mindspore-lite/minddata/dataset/util/semaphore.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/semaphore.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -Status Semaphore::P() { - std::unique_lock lck(mutex_); - RETURN_IF_NOT_OK(wait_cond_.Wait(&lck, [this]() { return value_ > 0; })); - --value_; - return Status::OK(); -} -void Semaphore::V() { - std::unique_lock lck(mutex_); - ++value_; - wait_cond_.NotifyOne(); -} -int Semaphore::Peek() const { return value_; } -Status Semaphore::Register(TaskGroup *vg) { return wait_cond_.Register(vg->GetIntrpService()); } -Status Semaphore::Deregister() { return (wait_cond_.Deregister()); } -void Semaphore::ResetIntrpState() { wait_cond_.ResetIntrpState(); } - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/semaphore.h b/mindspore-lite/minddata/dataset/util/semaphore.h deleted file mode 100644 index f3bf736f1..000000000 --- a/mindspore-lite/minddata/dataset/util/semaphore.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SEMAPHORE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SEMAPHORE_H_ - -#include "mindspore-lite/minddata/dataset/util/cond_var.h" - -namespace mindspore { -namespace dataset { -class TaskGroup; - -/// \brief A counting semaphore. There are two external functions P and V. P decrements the internal count and will be -/// blocked if the count is 0 (zero). V increments the internal count and wake up one of the waiters. -class Semaphore { - public: - /// \brief Constructor - /// \param init Initial value of the internal counter. - explicit Semaphore(int init) : value_(init) {} - - virtual ~Semaphore() {} - /// \brief Decrement the internal counter. Will be blocked if the value is 0. - /// \return Error code. Can get interrupt. - Status P(); - /// \brief Increment the internal counter. Wake up on of the waiters if any. - void V(); - /// \brief Peek the internal value - /// \return The internal value - int Peek() const; - Status Register(TaskGroup *vg); - Status Deregister(); - void ResetIntrpState(); - - private: - int value_; - - std::mutex mutex_; - CondVar wait_cond_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SEMAPHORE_H_ diff --git a/mindspore-lite/minddata/dataset/util/service.cc b/mindspore-lite/minddata/dataset/util/service.cc deleted file mode 100644 index a3ca1db73..000000000 --- a/mindspore-lite/minddata/dataset/util/service.cc +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/service.h" -#include - -namespace mindspore { -namespace dataset { -Status Service::ServiceStart() { - do { - UniqueLock lck(&state_lock_); - // No-op if it is already up or some other thread is - // in the process of bring it up. - if (state_ == STATE::kRunning || state_ == STATE::kStartInProg) { - return Status::OK(); - } - // If a stop is in progress, we line up after it - // is done. - if (state_ == STATE::kStopInProg) { - std::this_thread::yield(); - } else { - state_ = STATE::kStartInProg; - // At this point, we will let go of the lock. This allow others to proceed. - lck.Unlock(); - // Call the real implementation from the derived class. - Status rc = DoServiceStart(); - // If we hit any error, change the state back into the initial state. - // It is possible that the user may want to drive a clean up by calling - // ServiceStop but if it will end up in a loop because of the state is still - // kStartInProg. - if (rc.IsError()) { - lck.Lock(); - state_ = STATE::kStopped; - lck.Unlock(); - return rc; - } - // Lock again to change state. - lck.Lock(); - state_ = STATE::kRunning; - return Status::OK(); - } - } while (true); -} - -Status Service::ServiceStop() noexcept { - do { - UniqueLock lck(&state_lock_); - // No-op if it is already stopped or some other thread is - // in the process of shutting it down - if (state_ == STATE::kStopped || state_ == STATE::kStopInProg) { - return Status::OK(); - } - // If a start is in progress, we line up after it - // is done. - if (state_ == STATE::kStartInProg) { - std::this_thread::yield(); - } else { - state_ = STATE::kStopInProg; - // At this point, we will let go of the lock. This allows others to proceed. - lck.Unlock(); - RETURN_IF_NOT_OK(DoServiceStop()); - // Lock again to change state. - lck.Lock(); - state_ = STATE::kStopped; - return Status::OK(); - } - } while (true); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/service.h b/mindspore-lite/minddata/dataset/util/service.h deleted file mode 100644 index a97be7544..000000000 --- a/mindspore-lite/minddata/dataset/util/service.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICE_H_ - -#include -#include "mindspore-lite/minddata/dataset/util/lock.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -class Service { - public: - enum class STATE : int { kStartInProg = 1, kRunning, kStopInProg, kStopped }; - - Service() : state_(STATE::kStopped) {} - - Service(const Service &) = delete; - - Service &operator=(const Service &) = delete; - - virtual ~Service() {} - - STATE ServiceState() const { return state_; } - - virtual Status DoServiceStart() = 0; - - virtual Status DoServiceStop() = 0; - - Status ServiceStart(); - - Status ServiceStop() noexcept; - - protected: - STATE state_; - RWLock state_lock_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICE_H_ diff --git a/mindspore-lite/minddata/dataset/util/services.cc b/mindspore-lite/minddata/dataset/util/services.cc deleted file mode 100644 index fc4f214c4..000000000 --- a/mindspore-lite/minddata/dataset/util/services.cc +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/services.h" - -#include -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -#include -#else -#include -#endif -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/circular_pool.h" -#include "mindspore-lite/minddata/dataset/util/random.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -#if defined(__APPLE__) -#define LOGIN_NAME_MAX 256 -#endif - -namespace mindspore { -namespace dataset { -std::unique_ptr Services::instance_ = nullptr; -std::once_flag Services::init_instance_flag_; -std::map Services::unique_id_list_ = {}; -uint64_t Services::unique_id_count_ = 0; -std::mutex Services::unique_id_mutex_; - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -std::string Services::GetUserName() { - char user[LOGIN_NAME_MAX]; - (void)getlogin_r(user, sizeof(user)); - return std::string(user); -} - -std::string Services::GetHostName() { - char host[LOGIN_NAME_MAX]; - (void)gethostname(host, sizeof(host)); - return std::string(host); -} - -int Services::GetLWP() { return syscall(SYS_gettid); } -#endif - -std::string Services::GetUniqueID() { - const std::string kStr = "abcdefghijklmnopqrstuvwxyz0123456789"; - std::mt19937 gen = GetRandomDevice(); - std::uniform_int_distribution dist(0, kStr.size() - 1); - char buffer[UNIQUEID_LEN]; - { - std::unique_lock lock(unique_id_mutex_); - while (true) { - auto ret = memset_s(buffer, UNIQUEID_LEN, 0, UNIQUEID_LEN); - if (ret != EOK) { - MS_LOG(ERROR) << "memset_s error, errorno(" << ret << ")"; - return std::string(""); - } - for (int i = 0; i < UNIQUEID_LEN; i++) { - buffer[i] = kStr[dist(gen)]; - } - if (unique_id_list_.find(std::string(buffer, UNIQUEID_LEN)) != unique_id_list_.end()) { - continue; - } - unique_id_list_[std::string(buffer, UNIQUEID_LEN)] = unique_id_count_; - unique_id_count_++; - // Temporary solution to solve a long stability memory increasing problem that - // we limit the size of unique_id_list_ not to greater than UNIQUEID_LIST_LIMITS(1024). - if (unique_id_list_.size() >= UNIQUEID_LIST_LIMITS) { - for (auto iter = unique_id_list_.begin(); iter != unique_id_list_.end();) { - if (iter->second < UNIQUEID_HALF_INDEX) { - iter = unique_id_list_.erase(iter); - unique_id_count_--; - } else { - iter->second -= UNIQUEID_HALF_INDEX; - iter++; - } - } - } - MS_LOG(DEBUG) << "unique_id_list_ size is " << unique_id_list_.size() << ", count is " << unique_id_count_; - break; - } - } - return std::string(buffer, UNIQUEID_LEN); -} - -Status Services::CreateAllInstances() { - // First one is always the TaskManager - RETURN_IF_NOT_OK(TaskManager::CreateInstance()); - TaskManager &tm = TaskManager::GetInstance(); - RETURN_IF_NOT_OK(tm.ServiceStart()); - return Status::OK(); -} - -Services::Services() : pool_(nullptr) { - Status rc = CircularPool::CreateCircularPool(&pool_, -1, 16, true); // each arena 16M - if (rc.IsError()) { - std::terminate(); - } -} - -Services::~Services() noexcept { - // Shutdown in reverse order. - auto n = hook_.size(); - while (n > 0) { - hook_.pop_back(); - n = hook_.size(); - } -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/services.h b/mindspore-lite/minddata/dataset/util/services.h deleted file mode 100644 index f209668d3..000000000 --- a/mindspore-lite/minddata/dataset/util/services.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICES_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICES_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/service.h" - -#define UNIQUEID_LEN 36 -#define UNIQUEID_LIST_LIMITS 1024 -#define UNIQUEID_HALF_INDEX ((UNIQUEID_LIST_LIMITS) / 2) -namespace mindspore { -namespace dataset { -class TaskManager; - -class Services { - public: - static Status CreateInstance() { - std::call_once(init_instance_flag_, [&]() -> Status { - instance_.reset(new Services()); - return (instance_->CreateAllInstances()); - }); - - if (instance_ == nullptr) { - instance_.reset(new Services()); - return (instance_->CreateAllInstances()); - } - - return Status::OK(); - } - - static Services &GetInstance() { - if (instance_ == nullptr) { - if (!CreateInstance()) { - std::terminate(); - } - } - return *instance_; - } - - Services(const Services &) = delete; - - Services &operator=(const Services &) = delete; - - ~Services() noexcept; - - std::shared_ptr GetServiceMemPool() { return pool_; } - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - static std::string GetUserName(); - - static std::string GetHostName(); - - static int GetLWP(); -#endif - - static std::string GetUniqueID(); - - template - static Allocator GetAllocator() { - return Allocator(Services::GetInstance().GetServiceMemPool()); - } - - /// \brief Add a new service to the start up list. - /// \tparam T Class that implements Service - /// \return Status object and where the service is located in the hook_ list - template - Status AddHook(T **out, Args &&... args) { - RETURN_UNEXPECTED_IF_NULL(out); - try { - (*out) = new T(std::forward(args)...); - std::unique_ptr svc(*out); - hook_.push_back(std::move(svc)); - } catch (const std::bad_alloc &e) { - return Status(StatusCode::kMDOutOfMemory); - } - return Status::OK(); - } - - private: - static std::once_flag init_instance_flag_; - static std::unique_ptr instance_; - static std::map unique_id_list_; - static uint64_t unique_id_count_; - static std::mutex unique_id_mutex_; - // A small pool used for small objects that last until the - // Services Manager shuts down. Used by all sub-services. - std::shared_ptr pool_; - std::vector> hook_; - - Services(); - - Status CreateAllInstances(); -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SERVICES_H_ diff --git a/mindspore-lite/minddata/dataset/util/shared_mem.cc b/mindspore-lite/minddata/dataset/util/shared_mem.cc deleted file mode 100644 index d0bcf8605..000000000 --- a/mindspore-lite/minddata/dataset/util/shared_mem.cc +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/shared_mem.h" - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#include -#include -#endif - -#include -#include -#include - -namespace mindspore::dataset { -#if !defined(_WIN32) && !defined(_WIN64) -std::string GenerateShmName() { - static std::atomic counter{0}; - static std::random_device rd; - std::string handle = "/mindspore_"; - handle += std::to_string(getpid()); - handle += "_"; - handle += std::to_string(rd()); - handle += "_"; - handle += std::to_string(counter.fetch_add(1, std::memory_order_relaxed)); - return handle; -} - -SharedMem::SharedMem(const std::string &name, bool create, int fd, size_t size) - : name_(name), create_(create), fd_(fd), size_(size) { - if (name_.empty()) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: name can not be empty."; - } - - if (!create_ && fd_ < 0) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: fd must be non-negative when create is false, but got: " << fd_; - } - - if (size_ == 0) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: size must be a positive integer, but got: " << size_; - } - - if (create_) { - mode_t mode = 0600; - if ((fd_ = shm_open(name_.c_str(), O_RDWR | O_CREAT | O_EXCL, mode)) == -1) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: shm_open failed with errno: " << errno; - } - } - - struct stat file_stat {}; - if (fstat(fd_, &file_stat) == -1) { - errno_t fstat_errno = errno; - if (create) { - ::close(fd_); - } - MS_EXCEPTION(RuntimeError) << "SharedMemory: fstat failed with errno: " << fstat_errno; - } - - if (size_ > static_cast(file_stat.st_size)) { - if (ftruncate(fd_, static_cast(size_)) == -1) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: ftruncate failed with errno: " << errno; - } - } - - if ((buf_ = mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)) == MAP_FAILED) { - buf_ = nullptr; - MS_EXCEPTION(RuntimeError) << "SharedMemory: mmap failed with errno: " << errno; - } - - if (create_) { - if (shm_unlink(name_.c_str()) == -1) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: shm_unlink failed with errno: " + std::to_string(errno); - } - MS_LOG(INFO) << "Shared memory " << name_ << " has been created, fd: " << fd_ << ", size: " << size_; - } else { - MS_LOG(INFO) << "Attach to shared memory " << name_ << ", fd: " << fd_ << ", size: " << size_; - } -} - -SharedMem::~SharedMem() { Close(); } - -void *SharedMem::Buf() { return buf_; } - -std::string SharedMem::Name() const { return name_; } - -int32_t SharedMem::Fd() const { return fd_; } - -size_t SharedMem::Size() const { return size_; } - -void SharedMem::Close() { - if (buf_ != nullptr) { - if (munmap(buf_, size_) != 0) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: munmap failed with errno: " << errno; - } - buf_ = nullptr; - } - - if (fd_ >= 0) { - if (::close(fd_) != 0) { - MS_EXCEPTION(RuntimeError) << "SharedMemory: close fd failed with errno: " << errno; - } - fd_ = -1; - } - MS_LOG(INFO) << "Shared memory " << name_ << " has been closed."; -} -#endif -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/util/shared_mem.h b/mindspore-lite/minddata/dataset/util/shared_mem.h deleted file mode 100644 index 60aca87e0..000000000 --- a/mindspore-lite/minddata/dataset/util/shared_mem.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SHARED_MEM_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SHARED_MEM_H_ - -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore::dataset { -std::string GenerateShmName(); - -class SharedMem { - public: - SharedMem() = delete; - - explicit SharedMem(const std::string &name, bool create, int fd, size_t size); - - ~SharedMem(); - - void *Buf(); - - std::string Name() const; - - int32_t Fd() const; - - size_t Size() const; - - void Close(); - - private: - std::string name_; - bool create_; - int32_t fd_; - size_t size_; - void *buf_; -}; -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SHARED_MEM_H_ diff --git a/mindspore-lite/minddata/dataset/util/sig_handler.cc b/mindspore-lite/minddata/dataset/util/sig_handler.cc deleted file mode 100644 index 92a268f56..000000000 --- a/mindspore-lite/minddata/dataset/util/sig_handler.cc +++ /dev/null @@ -1,225 +0,0 @@ -/** - * Copyright 2020-2025 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/sig_handler.h" - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#endif - -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore::dataset { -static std::unordered_map> worker_groups = {}; -#if !defined(_WIN32) && !defined(_WIN64) -/// \brief Set handler for the specified signal. -/// \param[in] signal The signal to set handler. -/// \param[in] handler The handler to execute when receive the signal. -/// \param[in] old_action The former handler. -void SetSignalHandler(int signal, void (*handler)(int, siginfo_t *, void *), struct sigaction *old_action) { - struct sigaction action {}; - action.sa_sigaction = handler; - action.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDSTOP | SA_NODEFER; - if (sigemptyset(&action.sa_mask) != 0) { - MS_EXCEPTION(RuntimeError) << "Failed to initialise the signal set, " << strerror(errno); - } - if (sigaction(signal, &action, old_action) != 0) { - MS_EXCEPTION(RuntimeError) << "Failed to set handler for " << strsignal(signal) << ", " << strerror(errno); - } -} - -/// \brief A signal handler for SIGINT to interrupt watchdog. -/// \param[in] signal The signal that was raised. -/// \param[in] info The siginfo structure. -/// \param[in] context The context info. -void SIGINTHandler(int signal, siginfo_t *info, void *context) { - // Wake up the watchdog which is designed as async-signal-safe. - TaskManager::WakeUpWatchDog(); -} - -/// \brief A signal handler for SIGTERM to exit the process. -/// \details When Python exits, it may terminate the children processes before deleting our runtime. -/// Then the watch dog has not been aborted, it will report an error and terminate the main process. -/// So we suppress SIGTERM sent from main process here by _exit(EXIT_SUCCESS). -/// \param[in] signal The signal that was raised. -/// \param[in] info The siginfo structure. -/// \param[in] context The context info. -void SIGTERMHandler(int signal, siginfo_t *info, void *context) { - if (signal != SIGTERM) { - MS_LOG(ERROR) << "SIGTERMHandler expects SIGTERM signal, but got: " << strsignal(signal); - _exit(EXIT_FAILURE); - } - - if (info->si_pid == getppid()) { - MS_LOG(INFO) << "Dataset worker process " << std::to_string(getpid()) - << " was terminated by parent process: " << std::to_string(info->si_pid) - << ", exits with successful status."; - _exit(EXIT_SUCCESS); - } - // reset the handler to the default - struct sigaction term_action {}; - term_action.sa_handler = SIG_DFL; - term_action.sa_flags = 0; - if (sigemptyset(&term_action.sa_mask) != 0) { - MS_LOG(ERROR) << "Failed to initialise the signal set, " << strerror(errno); - _exit(EXIT_FAILURE); - } - if (sigaction(signal, &term_action, nullptr) != 0) { - MS_LOG(ERROR) << "Failed to set handler for " << strsignal(signal) << ", " << strerror(errno); - _exit(EXIT_FAILURE); - } - raise(signal); -} - -/// \brief A signal handler for SIGBUS to retrieve the kill information. -/// \param[in] signal The signal that was raised. -/// \param[in] info The siginfo structure. -/// \param[in] context The context info. -void SIGBUSHandler(int signal, siginfo_t *info, void *context) { - if (signal != SIGBUS) { - MS_LOG(ERROR) << "SIGBUSHandler expects SIGBUS signal, but got: " << strsignal(signal); - _exit(EXIT_FAILURE); - } - - if (info->si_code == BUS_ADRERR) { - MS_LOG(ERROR) << "Unexpected bus error encountered in process: " << std::to_string(getpid()) - << ". Non-existent physical address. This might be caused by insufficient shared memory. " - << "Please check if '/dev/shm' has enough available space via 'df -h'."; - } - - // reset the handler to the default - struct sigaction bus_action {}; - bus_action.sa_handler = SIG_DFL; - bus_action.sa_flags = 0; - if (sigemptyset(&bus_action.sa_mask) != 0) { - MS_LOG(ERROR) << "Failed to initialise the signal set, " << strerror(errno); - _exit(EXIT_FAILURE); - } - if (sigaction(signal, &bus_action, nullptr) != 0) { - MS_LOG(ERROR) << "Failed to set handler for " << strsignal(signal) << ", " << strerror(errno); - _exit(EXIT_FAILURE); - } - raise(signal); -} - -/// \brief A signal handler for SIGCHLD to clean the rest processes. -/// \param[in] signal The signal that was raised. -/// \param[in] info The siginfo structure. -/// \param[in] context The context info. -void SIGCHLDHandler(int signal, siginfo_t *info, void *context) { - if (signal != SIGCHLD) { - MS_LOG(ERROR) << "SIGCHLDHandler expects SIGCHLD signal, but got: " << strsignal(signal); - _exit(EXIT_FAILURE); - } - - for (auto &worker_group : worker_groups) { - auto &pids = worker_group.second; - for (const auto &pid : pids) { - siginfo_t sig_info{}; - sig_info.si_pid = 0; - auto error = waitid(P_PID, pid, &sig_info, WEXITED | WNOHANG | WNOWAIT); - std::string msg; - if (error < 0) { - if (errno == ECHILD) { - msg = "Dataset worker process " + std::to_string(pid) + - " has already exited. Its state may have been retrieved by other threads."; - } else { - MS_LOG(WARNING) << "Failed to wait for dataset worker process " << pid << ", got: " << strerror(errno); - continue; - } - } else { - if (sig_info.si_pid == 0) { - continue; // There were no children in a wait state. - } - if (sig_info.si_code == CLD_EXITED && sig_info.si_status != EXIT_SUCCESS) { // exited unexpected - msg = "Dataset worker process " + std::to_string(sig_info.si_pid) + " exited unexpected with exit code " + - std::to_string(sig_info.si_status) + "."; - } else if (sig_info.si_code == CLD_KILLED) { // killed by signal - msg = "Dataset worker process " + std::to_string(sig_info.si_pid) + - " was killed by signal: " + std::string(strsignal(sig_info.si_status)) + "."; - } else if (sig_info.si_code == CLD_DUMPED) { // core dumped - msg = "Dataset worker process " + std::to_string(sig_info.si_pid) + - " core dumped: " + std::string(strsignal(sig_info.si_status)) + "."; - } else { - MS_LOG(INFO) << "Ignore dataset worker process " << pid << " with signal code " << sig_info.si_code; - continue; - } - } - auto pids_to_kill = pids; - pids.clear(); // Clear the monitoring status of the process group before performing a termination. - for (const auto &pid_to_kill : pids_to_kill) { - if (pid_to_kill != pid) { - MS_LOG(INFO) << "Terminating child process: " << pid_to_kill; - kill(pid_to_kill, SIGTERM); - } - } - MS_LOG(ERROR) << msg << " Main process will be terminated."; - kill(getpid(), SIGTERM); - // In case the signal is not responded, return here - MS_LOG(WARNING) << "Main process may not respond to the SIGTERM signal, please check if it is blocked."; - return; - } - } -} -#endif - -void RegisterHandlers() { -#if !defined(_WIN32) && !defined(_WIN64) - SetSignalHandler(SIGINT, &SIGINTHandler, nullptr); - SetSignalHandler(SIGTERM, &SIGINTHandler, nullptr); -#endif -} - -void RegisterMainHandlers() { -#if !defined(_WIN32) && !defined(_WIN64) - SetSignalHandler(SIGBUS, &SIGBUSHandler, nullptr); - SetSignalHandler(SIGCHLD, &SIGCHLDHandler, nullptr); -#endif -} - -void RegisterWorkerHandlers() { -#if !defined(_WIN32) && !defined(_WIN64) - SetSignalHandler(SIGBUS, &SIGBUSHandler, nullptr); - SetSignalHandler(SIGTERM, &SIGTERMHandler, nullptr); -#endif -} - -std::string GetPIDsString(const std::set &pids) { - std::string pids_string = "["; - for (auto itr = pids.begin(); itr != pids.end(); ++itr) { - if (itr != pids.begin()) { - pids_string += ", "; - } - pids_string += std::to_string(*itr); - } - pids_string += "]"; - return pids_string; -} - -void RegisterWorkerPIDs(int64_t id, const std::set &pids) { - MS_LOG(INFO) << "Watch dog starts monitoring process(es): " << GetPIDsString(pids); - worker_groups[id] = pids; -} - -void DeregisterWorkerPIDs(int64_t id) { - MS_LOG(INFO) << "Watch dog stops monitoring process(es): " << GetPIDsString(worker_groups[id]); - (void)worker_groups.erase(id); -} -} // namespace mindspore::dataset diff --git a/mindspore-lite/minddata/dataset/util/sig_handler.h b/mindspore-lite/minddata/dataset/util/sig_handler.h deleted file mode 100644 index dd7e96c92..000000000 --- a/mindspore-lite/minddata/dataset/util/sig_handler.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SIG_HANDLER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SIG_HANDLER_H_ - -#include -#include - -namespace mindspore::dataset { -/// \brief Register the custom signal handlers. -extern void RegisterHandlers(); - -/// \brief Register signal handlers for main process. -extern void RegisterMainHandlers(); - -/// \brief Register signal handlers for worker process. -extern void RegisterWorkerHandlers(); - -/// \brief Register workers to be monitored by the watch dog. -extern void RegisterWorkerPIDs(int64_t id, const std::set &pids); - -/// \brief Deregister workers to be monitored by the watch dog. -extern void DeregisterWorkerPIDs(int64_t id); -} // namespace mindspore::dataset -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SIG_HANDLER_H_ diff --git a/mindspore-lite/minddata/dataset/util/slice.cc b/mindspore-lite/minddata/dataset/util/slice.cc deleted file mode 100644 index a93e6d8cc..000000000 --- a/mindspore-lite/minddata/dataset/util/slice.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - - * 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. -*/ -#include "mindspore-lite/minddata/dataset/util/slice.h" - -namespace mindspore { -namespace dataset { -WritableSlice::WritableSlice(const WritableSlice &src, off64_t offset, size_t len) : ReadableSlice(src, offset, len) { - mutable_data_ = static_cast(src.mutable_data_) + offset; -} -WritableSlice::WritableSlice(const WritableSlice &src, off64_t offset) - : WritableSlice(src, offset, src.GetSize() - offset) {} -Status WritableSlice::Copy(WritableSlice *dest, const ReadableSlice &src) { - RETURN_UNEXPECTED_IF_NULL(dest); - RETURN_UNEXPECTED_IF_NULL(dest->GetMutablePointer()); - if (dest->GetSize() <= 0) { - RETURN_STATUS_UNEXPECTED("Destination length is non-positive"); - } - auto err = memcpy_s(dest->GetMutablePointer(), dest->GetSize(), src.GetPointer(), src.GetSize()); - if (err) { - RETURN_STATUS_UNEXPECTED(std::to_string(err)); - } - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/slice.h b/mindspore-lite/minddata/dataset/util/slice.h deleted file mode 100644 index 473b26b0a..000000000 --- a/mindspore-lite/minddata/dataset/util/slice.h +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2020-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SLICE_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SLICE_H_ - -#include -#include - -#include "mindspore-lite/minddata/dataset/util/status.h" - -#if defined(__APPLE__) -#define off64_t off_t -#endif - -namespace mindspore { -namespace dataset { -/// \brief A ReadableSlice wraps a const pointer in memory and its size. -/// \see WritableSlice for a non-const version -/// -class ReadableSlice { - public: - ReadableSlice() : ptr_(nullptr), sz_(0) {} - ReadableSlice(const void *ptr, size_t sz) : ptr_(ptr), sz_(sz) {} - - /// \brief Destructor - ~ReadableSlice() = default; - - ReadableSlice(const ReadableSlice &src, off64_t offset, size_t len) { - ptr_ = static_cast(src.GetPointer()) + offset; - sz_ = len; - } - ReadableSlice(const ReadableSlice &src, off64_t offset) : ReadableSlice(src, offset, src.sz_ - offset) {} - ReadableSlice(const ReadableSlice &lhs) { - ptr_ = lhs.ptr_; - sz_ = lhs.sz_; - } - ReadableSlice &operator=(const ReadableSlice &lhs) { - if (this != &lhs) { - ptr_ = lhs.ptr_; - sz_ = lhs.sz_; - } - return *this; - } - ReadableSlice(ReadableSlice &&lhs) noexcept { - if (this != &lhs) { - ptr_ = lhs.ptr_; - sz_ = lhs.sz_; - lhs.ptr_ = nullptr; - lhs.sz_ = 0; - } - } - ReadableSlice &operator=(ReadableSlice &&lhs) noexcept { - if (this != &lhs) { - ptr_ = lhs.ptr_; - sz_ = lhs.sz_; - lhs.ptr_ = nullptr; - lhs.sz_ = 0; - } - return *this; - } - /// \brief Getter function - /// \return Const version of the pointer - const void *GetPointer() const { return ptr_; } - /// \brief Getter function - /// \return Size of the slice - size_t GetSize() const { return sz_; } - bool empty() const { return ptr_ == nullptr; } - - private: - const void *ptr_; - size_t sz_; -}; -/// \brief A WritableSlice inherits from ReadableSlice to allow -/// one to write to the address pointed to by the pointer. -/// -class WritableSlice : public ReadableSlice { - public: - friend class StorageContainer; - friend class CacheService; - friend class CacheServer; - /// \brief Default constructor - WritableSlice() : ReadableSlice(), mutable_data_(nullptr) {} - /// \brief This form of a constructor takes a pointer and its size. - WritableSlice(void *ptr, size_t sz) : ReadableSlice(ptr, sz), mutable_data_(ptr) {} - WritableSlice(const WritableSlice &src, off64_t offset, size_t len); - WritableSlice(const WritableSlice &src, off64_t offset); - WritableSlice(const WritableSlice &lhs) : ReadableSlice(lhs) { mutable_data_ = lhs.mutable_data_; } - /// \brief Destructor - ~WritableSlice() = default; - WritableSlice &operator=(const WritableSlice &lhs) { - if (this != &lhs) { - mutable_data_ = lhs.mutable_data_; - (void)ReadableSlice::operator=(lhs); - } - return *this; - } - WritableSlice(WritableSlice &&lhs) noexcept : ReadableSlice(std::move(lhs)) { - if (this != &lhs) { - mutable_data_ = lhs.mutable_data_; - lhs.mutable_data_ = nullptr; - } - } - WritableSlice &operator=(WritableSlice &&lhs) noexcept { - if (this != &lhs) { - mutable_data_ = lhs.mutable_data_; - lhs.mutable_data_ = nullptr; - (void)ReadableSlice::operator=(std::move(lhs)); - } - return *this; - } - /// \brief Copy the content from one slice onto another. - static Status Copy(WritableSlice *dest, const ReadableSlice &src); - - private: - void *mutable_data_; - void *GetMutablePointer() { return mutable_data_; } -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SLICE_H_ diff --git a/mindspore-lite/minddata/dataset/util/status.cc b/mindspore-lite/minddata/dataset/util/status.cc deleted file mode 100644 index 09bfce3b0..000000000 --- a/mindspore-lite/minddata/dataset/util/status.cc +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/status.h" - -#include -#include -#include -#include "include/securec.h" - -#ifndef ENABLE_ANDROID -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#else -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#endif - -namespace mindspore { -namespace dataset { -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) -float GetMemoryUsage() { - char buf[128] = {0}; - - FILE *fd = fopen("/proc/meminfo", "r"); - if (fd == nullptr) { - MS_LOG(WARNING) << "The meminfo file: /proc/meminfo is opened failed."; - return 0.0; - } - - uint32_t status_count = 0; - uint64_t mem_total = 0L; - uint64_t mem_available = 0L; - while (fgets(buf, sizeof(buf), fd)) { - if (status_count == 2) { // get MemTotal and MemAvailable yet - break; - } - - // get title - std::string line(buf); - std::string::size_type position = line.find(":"); - if (position == std::string::npos) { - MS_LOG(WARNING) << "Parse content of /proc/meminfo failed, delimiter is not found."; - fclose(fd); - return 0.0; - } - std::string title = line.substr(0, position); - - // get the value when MemTotal or MemAvailable - if (title == "MemTotal") { - std::string::size_type pos1 = line.find_last_of(" "); - std::string::size_type pos2 = line.find_last_of(" ", pos1 - 1); - mem_total = atol(line.substr(pos2, pos1 - pos2).c_str()); - status_count++; - } else if (title == "MemAvailable") { - std::string::size_type pos1 = line.find_last_of(" "); - std::string::size_type pos2 = line.find_last_of(" ", pos1 - 1); - mem_available = atol(line.substr(pos2, pos1 - pos2).c_str()); - status_count++; - } - - auto ret = memset_s(buf, sizeof(buf), 0, sizeof(buf)); - if (ret != EOK) { - MS_LOG(WARNING) << "memset_s failed when get memory usage. This might be caused by insufficient memory."; - fclose(fd); - return 0.0; - } - } - fclose(fd); - - if (status_count != 2 || mem_total == 0 || mem_available > mem_total) { - MS_LOG(WARNING) << "Get memory usage failed."; - return 0.0; - } - - return (1.0 - static_cast(static_cast(mem_available) / static_cast(mem_total))); -} -#endif -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/status.h b/mindspore-lite/minddata/dataset/util/status.h deleted file mode 100644 index cb5f9aa52..000000000 --- a/mindspore-lite/minddata/dataset/util/status.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_STATUS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_STATUS_H_ - -#if defined(__GNUC__) || defined(__clang__) -#define DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define DEPRECATED __declspec(deprecated) -#else -#pragma message("WARNING: You need to implement DEPRECATED for this compiler") -#define DEPRECATED -#endif - -#include -#include -#include - -#include "include/api/status.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" - -namespace mindspore { -namespace dataset { -#define RETURN_IF_NOT_OK(_s) \ - do { \ - const mindspore::Status &__rc = (_s); \ - if (__rc.IsError()) { \ - return __rc; \ - } \ - } while (false) - -#define STATUS_ERROR(_error_code, _e) mindspore::Status(_error_code, __LINE__, DATASET_SRC_FILE_NAME, _e) - -#define RETURN_STATUS_ERROR(_error_code, _e) \ - do { \ - return STATUS_ERROR(_error_code, _e); \ - } while (false) - -#define RETURN_STATUS_UNEXPECTED(_e) \ - do { \ - RETURN_STATUS_ERROR(mindspore::StatusCode::kMDUnexpectedError, _e); \ - } while (false) - -#define CHECK_FAIL_RETURN_UNEXPECTED(_condition, _e) \ - do { \ - if (!(_condition)) { \ - RETURN_STATUS_UNEXPECTED(_e); \ - } \ - } while (false) - -#define RETURN_SYNTAX_ERROR(_e) \ - do { \ - RETURN_STATUS_ERROR(mindspore::StatusCode::kMDSyntaxError, _e); \ - } while (false) - -#define CHECK_FAIL_RETURN_SYNTAX_ERROR(_condition, _e) \ - do { \ - if (!(_condition)) { \ - RETURN_SYNTAX_ERROR(_e); \ - } \ - } while (false) - -#define LOG_AND_RETURN_STATUS_SYNTAX_ERROR(_e) \ - do { \ - MS_LOG(ERROR) << _e; \ - RETURN_SYNTAX_ERROR(_e); \ - } while (false) - -#define RETURN_UNEXPECTED_IF_NULL(_ptr) \ - do { \ - if ((_ptr) == nullptr) { \ - std::string err_msg = "The pointer[" + std::string(#_ptr) + "] is null."; \ - RETURN_STATUS_UNEXPECTED(err_msg); \ - } \ - } while (false) - -#define RETURN_OK_IF_TRUE(_condition) \ - do { \ - if (_condition) { \ - return mindspore::Status::OK(); \ - } \ - } while (false) - -#define RETURN_SECOND_IF_ERROR(_s, _r) \ - do { \ - const mindspore::Status &__rc = (_s); \ - if (__rc.IsError()) { \ - MS_LOG(ERROR) << __rc; \ - return _r; \ - } \ - } while (false) - -#define RETURN_STATUS_OOM(_e) \ - do { \ - RETURN_STATUS_ERROR(mindspore::StatusCode::kMDOutOfMemory, _e); \ - } while (false) - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__) -const float MAX_MEMORY_USAGE_THRESHOLD = 0.8; -float GetMemoryUsage(); -#endif - -#ifndef ENABLE_ANDROID -#define VLOG_FLOW(message) \ - do { \ - MS_VLOG(VL_FLOW) << message; \ - } while (false) -#else -#define VLOG_FLOW(message) \ - do { \ - ; \ - } while (false) -#endif - -#ifndef ENABLE_ANDROID -#define VLOG_MD(message) \ - do { \ - MS_VLOG(VL_MD) << message; \ - } while (false) -#else -#define VLOG_MD(message) \ - do { \ - ; \ - } while (false) -#endif -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_STATUS_H_ diff --git a/mindspore-lite/minddata/dataset/util/system_pool.h b/mindspore-lite/minddata/dataset/util/system_pool.h deleted file mode 100644 index e69df666e..000000000 --- a/mindspore-lite/minddata/dataset/util/system_pool.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SYSTEM_POOL_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SYSTEM_POOL_H_ - -#include -#include -#include -#include -#include -#include "include/securec.h" -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/memory_pool.h" - -namespace mindspore { -namespace dataset { -// This class demonstrate how to implement a simple MemoryPool -// for minddata/dataset using malloc/free/realloc. We need to -// implement 4 virtual functions. Other MemoryPool -// implementation, e.g., are BuddyArena and CircularPool. All -// these MemoryPool can be used together with Allocator.h for -// C++ STL containers. -class SystemPool : public MemoryPool { - public: - ~SystemPool() override {} - - Status Allocate(size_t n, void **pp) override { return DeMalloc(n, pp, false); } - - void Deallocate(void *p) override { - if (p != nullptr) { - free(p); - } - } - - Status Reallocate(void **p, size_t old_sz, size_t new_sz) override { - RETURN_UNEXPECTED_IF_NULL(p); - if (old_sz >= new_sz) { - // Do nothing if we shrink. - return Status::OK(); - } else { - void *ptr = *p; - void *q = nullptr; - RETURN_IF_NOT_OK(DeMalloc(new_sz, &q, false)); - errno_t err = memcpy_s(q, new_sz, ptr, old_sz); - if (err) { - free(q); - RETURN_STATUS_UNEXPECTED(std::to_string(err)); - } - free(ptr); - *p = q; - return Status::OK(); - } - } - - uint64_t get_max_size() const override { return std::numeric_limits::max(); } - - int PercentFree() const override { return 100; } - - template - static Allocator GetAllocator() { - return Allocator(std::make_shared()); - } -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_SYSTEM_POOL_H_ diff --git a/mindspore-lite/minddata/dataset/util/task.cc b/mindspore-lite/minddata/dataset/util/task.cc deleted file mode 100644 index 9b0eb5442..000000000 --- a/mindspore-lite/minddata/dataset/util/task.cc +++ /dev/null @@ -1,273 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/task.h" -#include "utils/os.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#if defined(__ANDROID__) || defined(ANDROID) -#include "mindspore-lite/minddata/dataset/util/services.h" -#endif -#ifdef WITH_BACKEND -#include "utils/ms_context.h" -#include "mindspore/ccsrc/include/runtime/hardware_abstract/data_queue/data_queue_mgr.h" -#endif -namespace mindspore { -namespace dataset { -thread_local Task *gMyTask = nullptr; - -void Task::operator()() { -#if !defined(_WIN32) && !defined(_WIN64) - gMyTask = this; -#endif - id_ = this_thread::get_id(); - std::stringstream ss; - ss << id_; -#if defined(__ANDROID__) || defined(ANDROID) || defined(__APPLE__) - // The thread id in Linux may be duplicate - ss << Services::GetUniqueID(); -#endif - MS_LOG(DEBUG) << "Task: " << my_name_ << " Thread ID " << ss.str() << " Started."; - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - native_handle_ = pthread_self(); - thread_id_ = syscall(SYS_gettid); -#endif - - try { - // Previously there is a timing hole where the thread is spawn but hit error immediately before we can set - // the TaskGroup pointer and register. We move the registration logic to here (after we spawn) so we can - // get the thread id. - TaskGroup *vg = MyTaskGroup(); - if (vg == nullptr) { - MS_LOG(ERROR) << "Task Group is nullptr."; - ShutdownGroup(); - return; - } - std::string uuid = ss.str(); - auto intrp_service = vg->GetIntrpService(); - rc_ = intrp_service->Register(&uuid, this); - if (rc_.IsOk()) { - // Now we can run the given task. - rc_ = fnc_obj_(); - } - // Some error codes are ignored, e.g. interrupt. Others we just shutdown the group. - if (rc_.IsError() && rc_ != StatusCode::kMDInterrupted) { - if (rc_.StatusCode() == StatusCode::kMDNetWorkError) { - MS_LOG(WARNING) << rc_; - } else { - MS_LOG(INFO) << "Task: " << my_name_ << " - thread(" << uuid << ") is terminated with err msg: " << rc_; - } - ShutdownGroup(); - } - } catch (const std::bad_alloc &e) { - rc_ = STATUS_ERROR(StatusCode::kMDOutOfMemory, e.what()); - MS_LOG(ERROR) << rc_; - ShutdownGroup(); - } catch (const std::exception &e) { - rc_ = STATUS_ERROR(StatusCode::kMDUnexpectedError, e.what()); - MS_LOG(INFO) << rc_; - ShutdownGroup(); - } - // The given function has finished running. We must change the running status immediately. - // Because std::async may create a new thread with the same thread ID as this thread since it has finished. - // Then there will be two tasks with the same thread ID in our task group, which may cause a mismatch - // in TaskManager::FindMe(). We can identify the exact task based on the running status there. - running_ = false; - MS_LOG(DEBUG) << "Task: " << my_name_ << " Thread ID " << ss.str() << " Finished."; -} - -void Task::ShutdownGroup() { // Wake up watch dog and shutdown the engine. - { - std::lock_guard lk(mux_); - caught_severe_exception_ = true; - } - TaskGroup *vg = MyTaskGroup(); - // If multiple threads hit severe errors in the same group. Keep the first one and - // discard the rest. - std::unique_lock rcLock(vg->rc_mux_); - { - if (vg->rc_.IsOk()) { - // Check again after we get the lock - if (vg->rc_.IsOk()) { - vg->rc_ = rc_; - rcLock.unlock(); - TaskManager::InterruptMaster(rc_); - TaskManager::InterruptGroup(*this); - if (vg->rc_.IsError()) { - // InterruptMaster miss sink pyfunc scenario, thus add print here. - if (vg->has_dataqueue_ && vg->rc_.StatusCode() == mindspore::StatusCode::kMDPyFuncException) { - MS_LOG(ERROR) << "MindSpore dataset is terminated with err msg: " << vg->rc_; - } - } - } - } - } -} - -Status Task::GetTaskErrorIfAny() const { - std::lock_guard lk(mux_); - if (caught_severe_exception_) { - return rc_; - } else { - return Status::OK(); - } -} - -pid_t GetCurrentPID() { -#if defined(_WIN32) || defined(_WIN64) - return GetCurrentProcessId(); -#else - return getpid(); -#endif -} - -Task::Task(const std::string &myName, const std::function &f, int32_t operator_id) - : my_name_(myName), - operator_id_(operator_id), - process_id_(GetCurrentPID()), - thread_id_(-1), - rc_(Status::OK()), - fnc_obj_(f), - task_group_(nullptr), - is_master_(false), - running_(false), - caught_severe_exception_(false), - native_handle_(0) { - IntrpResource::ResetIntrpState(); - wp_.ResetIntrpState(); - wp_.Clear(); -} - -Status Task::Run() { - Status rc; - std::lock_guard lk(mux_); - if (running_ == false) { - try { - running_ = true; - thrd_ = std::async(std::launch::async, std::ref(*this)); - caught_severe_exception_ = false; - } catch (const std::exception &e) { - rc = STATUS_ERROR(StatusCode::kMDUnexpectedError, e.what()); - } - } - return rc; -} - -Status Task::Join(WaitFlag blocking) { -#ifdef WITH_BACKEND - RETURN_UNEXPECTED_IF_NULL(MsContext::GetInstance()); - std::string device_target = MsContext::GetInstance()->get_param(MS_CTX_DEVICE_TARGET); -#endif - // If the current process is a subprocess of map or batch, the process ID will not be equal to process_id_. - // And no need to join WatchDog. - if (running_ && GetCurrentPID() == process_id_ && my_name_.find("WatchDog") == std::string::npos) { - RETURN_UNEXPECTED_IF_NULL(MyTaskGroup()); - auto interrupt_svc = MyTaskGroup()->GetIntrpService(); - try { - if (blocking == WaitFlag::kBlocking) { - // If we are asked to wait, then wait - thrd_.get(); - } else if (blocking == WaitFlag::kNonBlocking) { - // There is a race condition in the global resource tracking such that a thread can miss the - // interrupt and becomes blocked on a conditional variable forever. As a result, calling - // join() will not come back. We need some timeout version of join such that if the thread - // doesn't come back in a reasonable of time, we will send the interrupt again. - uint32_t wait_times = 0; - const uint32_t kLogInterval = 5; - while (thrd_.wait_for(std::chrono::seconds(1)) != std::future_status::ready) { - // We can't tell which conditional_variable this thread is waiting on. So we may need - // to interrupt everything one more time. - std::stringstream ss; - ss << get_id(); - wait_times++; - if (wait_times % kLogInterval == 0) { - MS_LOG(WARNING) << "Task: " << my_name_ << " Thread ID " << ss.str() - << " is not finished and cannot be joined. Try to interrupt again."; - } - interrupt_svc->InterruptAll(); -#ifdef WITH_BACKEND - const int kMaxWaitTimes = 5; - if (device_target == kAscendDevice) { - // Because hostPush hung in DataQueueOp, wait 5 seconds and destroy the tdt - if (wait_times > kMaxWaitTimes && my_name_.find("DataQueueOp") != std::string::npos) { - MS_LOG(WARNING) << "Wait " << wait_times << " seconds, the task: " << my_name_ - << " will be destroyed by TdtHostDestory."; - if (device::DataQueueMgr::DestoryTdtHandle()) { - MS_LOG(INFO) << "Destroy tdt channel success."; - } else { - MS_LOG(WARNING) << "Destroy tdt channel failed."; - } - - // just wait 30 seconds - // case1: cpu usage 100%, DataQueueOp thread may destroy without thread_future - if (wait_times > kWaitInterruptTaskTime) { - MS_LOG(WARNING) << "Task: " << my_name_ << " Thread ID " << ss.str() - << " is not responding. Maybe it has been destroyed. Stop the task."; - break; - } - } - } - - // Because ReceiveBridgeOp maybe hung by MsgRcv from SendBridgeOp - if (wait_times > kMaxWaitTimes && my_name_.find("ReceiveBridgeOp") != std::string::npos) { - MS_LOG(WARNING) << "Wait " << wait_times << " seconds, the task: " << my_name_ << "."; - - // just wait 30 seconds - if (wait_times > kWaitInterruptTaskTime) { - MS_LOG(WARNING) << "Task: " << my_name_ << " Thread ID " << ss.str() - << " is not responding. Break the interrupt."; - break; - } - } -#endif - } - } else { - RETURN_STATUS_UNEXPECTED("Unknown WaitFlag"); - } - std::stringstream ss; - ss << get_id(); - MS_LOG(DEBUG) << "Task: " << my_name_ << " Thread ID " << ss.str() << " Stopped."; - running_ = false; - RETURN_IF_NOT_OK(wp_.Deregister()); - RETURN_IF_NOT_OK(interrupt_svc->Deregister(ss.str())); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED(e.what()); - } - } - return Status::OK(); -} - -TaskGroup *Task::MyTaskGroup() { return task_group_; } - -void Task::set_task_group(TaskGroup *vg) { task_group_ = vg; } - -Task::~Task() { task_group_ = nullptr; } - -Status Task::OverrideInterruptRc(const Status &rc) { - if (rc == StatusCode::kMDInterrupted && this_thread::is_master_thread()) { - // If we are interrupted, override the return value if this is the master thread. - // Master thread is being interrupted mostly because of some thread is reporting error. - return TaskManager::GetMasterThreadRc(); - } - return rc; -} - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -pthread_t Task::GetNativeHandle() const { return native_handle_; } -#endif - -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/task.h b/mindspore-lite/minddata/dataset/util/task.h deleted file mode 100644 index 19b6e30cd..000000000 --- a/mindspore-lite/minddata/dataset/util/task.h +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_H_ - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -#include -#include -#endif -#if defined(_WIN32) || defined(_WIN64) -#include // for GetCurrentProcessId() -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mindspore-lite/minddata/dataset/util/intrp_resource.h" -#include "mindspore-lite/minddata/dataset/util/list.h" -#include "mindspore-lite/minddata/dataset/util/log_adapter.h" -#include "mindspore-lite/minddata/dataset/util/wait_post.h" - -namespace mindspore { -namespace dataset { -const uint32_t kWaitInterruptTaskTime = 30; // the wait time of interrupt task - -class TaskManager; - -class Task : public IntrpResource { - public: - friend class TaskManager; - friend class TaskGroup; - - enum class WaitFlag : int { kBlocking, kNonBlocking }; - - Task(const std::string &myName, const std::function &f, int32_t operator_id = -1); - - // Future objects are not copyable. - Task(const Task &) = delete; - - ~Task() override; - - Task &operator=(const Task &) = delete; - - // Move constructor and Assignment are not supported. - // Too many things in this class. - Task(Task &&) = delete; - - Task &operator=(Task &&) = delete; - - Status GetTaskErrorIfAny() const; - - void ChangeName(const std::string &newName) { my_name_ = newName; } - - // To execute the _fncObj - void operator()(); - - Node node; - Node group; - Node free; - - // Run the task - Status Run(); - - Status Join(WaitFlag wf = WaitFlag::kBlocking); - - bool Running() const { return running_; } - - bool CaughtSevereException() const { return caught_severe_exception_; } - - bool IsMasterThread() const { return is_master_; } - - std::thread::id get_id() { return id_; } - - pid_t get_linux_id() { return thread_id_; } - - std::string MyName() const { return my_name_; } - - int32_t get_operator_id() { return operator_id_; } - - // An operator used by std::find - bool operator==(const Task &other) const { return (this == &other); } - - bool operator!=(const Task &other) const { return !(*this == other); } - - void Post() { wp_.Set(); } - - Status Wait() { return (wp_.Wait()); } - - void Clear() { wp_.Clear(); } - - static Status OverrideInterruptRc(const Status &rc); - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - pthread_t GetNativeHandle() const; -#endif - - pid_t GetProcessID() { return process_id_; } - - private: - mutable std::mutex mux_; - std::string my_name_; - int32_t operator_id_; - pid_t process_id_; // process id (like: 11418), which can be queried by command "ps -ef | grep pid" - pid_t thread_id_; // linux thread id (like: 11534), which can be queried by command "top -H" - Status rc_; - WaitPost wp_; - // Task need to provide definition for this function. It - // will be called by thread function. - std::function fnc_obj_; - // Misc fields used by TaskManager. - TaskGroup *task_group_; - std::future thrd_; - std::thread::id id_; // thread id (like: 140299045656320) of type std::thread::id uniquely identifying the thread - bool is_master_; - volatile bool running_; - volatile bool caught_severe_exception_; - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - pthread_t native_handle_; -#else - uint64_t native_handle_; -#endif - - void ShutdownGroup(); - TaskGroup *MyTaskGroup(); - void set_task_group(TaskGroup *vg); -}; - -extern thread_local Task *gMyTask; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_H_ diff --git a/mindspore-lite/minddata/dataset/util/task_manager.cc b/mindspore-lite/minddata/dataset/util/task_manager.cc deleted file mode 100644 index 081b24a41..000000000 --- a/mindspore-lite/minddata/dataset/util/task_manager.cc +++ /dev/null @@ -1,403 +0,0 @@ -/** - * Copyright 2019-2022 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/task_manager.h" -#include -#include -#include -#include "include/securec.h" -#include "utils/ms_utils.h" - -namespace mindspore { -namespace dataset { -TaskManager *TaskManager::instance_ = nullptr; -std::once_flag TaskManager::init_instance_flag_; -// This takes the same parameter as Task constructor. -Status TaskManager::CreateAsyncTask(const std::string &my_name, const std::function &f, TaskGroup *vg, - Task **task, int32_t operator_id) { - // We need to block destructor coming otherwise we will deadlock. We will grab the - // stateLock in shared allowing CreateAsyncTask to run concurrently. - SharedLock stateLck(&state_lock_); - // Now double check the state - if (ServiceState() == STATE::kStopInProg || ServiceState() == STATE::kStopped) { - RETURN_STATUS_ERROR(StatusCode::kMDInterrupted, "TaskManager is shutting down"); - } - RETURN_IF_NOT_OK(GetFreeTask(my_name, f, task, operator_id)); - if (vg == nullptr) { - RETURN_STATUS_UNEXPECTED("TaskGroup is null"); - } - // Previously there is a timing hole where the thread is spawn but hit error immediately before we can set - // the TaskGroup pointer. We will do the set here before we call run(). The run() will do the registration. - (*task)->set_task_group(vg); - // Link to the master lru list. - { - UniqueLock lck(&lru_lock_); - lru_.Append(*task); - } - // Link to the group list as well before we spawn. - { - UniqueLock lck(&vg->rw_lock_); - vg->grp_list_.Append(*task); - } - // Track all the TaskGroup. Used for control-c - { - LockGuard lck(&tg_lock_); - (void)this->grp_list_.insert(vg); - } - RETURN_IF_NOT_OK((*task)->wp_.Register(vg)); - RETURN_IF_NOT_OK((*task)->Run()); - // Wait for the thread to initialize successfully. - RETURN_IF_NOT_OK((*task)->Wait()); - (*task)->Clear(); - return Status::OK(); -} - -Status TaskManager::join_all() { - Status rc; - Status rc2; - SharedLock lck(&lru_lock_); - for (Task &tk : lru_) { - rc = tk.Join(); - if (rc.IsError()) { - rc2 = rc; - } - } - return rc2; -} - -void TaskManager::interrupt_all() noexcept { - global_interrupt_ = 1; - LockGuard lck(&tg_lock_); - for (TaskGroup *vg : grp_list_) { - auto svc = vg->GetIntrpService(); - if (svc) { - // Stop the interrupt service. No new request is accepted. - Status rc = svc->ServiceStop(); - if (rc.IsError()) { - MS_LOG(ERROR) << "Error while stopping the service. Message: " << rc; - } - svc->InterruptAll(); - } - } - master_->Interrupt(); -} - -Task *TaskManager::FindMe() { -#if !defined(_WIN32) && !defined(_WIN64) - return gMyTask; -#else - TaskManager &tm = TaskManager::GetInstance(); - SharedLock lock(&tm.lru_lock_); - auto id = this_thread::get_id(); - for (auto iter = tm.lru_.begin(); iter != tm.lru_.end(); ++iter) { - if (iter->id_ == id && iter->running_) { - return &(*iter); - } - } - // If we get here, either I am the watchdog or the master thread. - if (tm.master_->id_ == id) { - return tm.master_.get(); - } else if (tm.watchdog_ != nullptr && tm.watchdog_->id_ == id) { - return tm.watchdog_; - } - MS_LOG(ERROR) << "Task not found."; - return nullptr; -#endif -} - -TaskManager::TaskManager() try : global_interrupt_(0), - lru_(&Task::node), - free_lst_(&Task::free), - watchdog_grp_(nullptr), - watchdog_(nullptr) { - auto alloc = Services::GetAllocator(); - // Create a dummy Task for the master thread (this thread) - master_ = std::allocate_shared(alloc, "master", []() -> Status { return Status::OK(); }); - master_->id_ = this_thread::get_id(); - master_->running_ = true; - master_->is_master_ = true; -#if !defined(_WIN32) && !defined(_WIN64) - gMyTask = master_.get(); -#if !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - // Initialize the semaphore for the watchdog - errno_t rc = sem_init(&sem_, 0, 0); - if (rc == -1) { - MS_LOG(ERROR) << "Unable to initialize a semaphore. Errno = " << rc << "."; - std::terminate(); - } -#endif -#endif -} catch (const std::exception &e) { - MS_LOG(ERROR) << "MindData initialization failed: " << e.what() << "."; - std::terminate(); -} - -TaskManager::~TaskManager() { - if (watchdog_) { - WakeUpWatchDog(); - auto s = watchdog_->Join(); - if (s.IsError()) { - MS_LOG(ERROR) << s.ToString(); - } - // watchdog_grp_ and watchdog_ pointers come from Services::GetInstance().GetServiceMemPool() which we will free it - // on shutdown. So no need to free these pointers one by one. - watchdog_grp_ = nullptr; - watchdog_ = nullptr; - } -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - (void)sem_destroy(&sem_); -#endif -} - -Status TaskManager::DoServiceStart() { - MS_LOG(INFO) << "Starting Task Manager."; -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - // Create a watchdog for control-c - std::shared_ptr mp = Services::GetInstance().GetServiceMemPool(); - // A dummy group just for the watchdog. We aren't really using it. But most code assumes a thread must - // belong to a group. - auto f = std::bind(&TaskManager::WatchDog, this); - Status rc; - watchdog_grp_ = new (&rc, mp) TaskGroup(); - RETURN_IF_NOT_OK(rc); - rc = watchdog_grp_->CreateAsyncTask("Watchdog", f, &watchdog_); - if (rc.IsError()) { - ::operator delete(watchdog_grp_, mp); - watchdog_grp_ = nullptr; - return rc; - } - (void)grp_list_.erase(watchdog_grp_); - lru_.Remove(watchdog_); -#endif - return Status::OK(); -} - -Status TaskManager::DoServiceStop() { - WakeUpWatchDog(); - interrupt_all(); - return Status::OK(); -} - -Status TaskManager::WatchDog() { - TaskManager::FindMe()->Post(); -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - errno_t err = sem_wait(&sem_); - if (err == -1) { - RETURN_STATUS_UNEXPECTED("Errno = " + std::to_string(errno)); - } - // We are woken up by control-c and we are going to stop all threads that are running. - // In addition, we also want to prevent new thread from creating. This can be done - // easily by calling the parent function. - RETURN_IF_NOT_OK(ServiceStop()); -#endif - return Status::OK(); -} - -// Follow the group link and interrupt other -// Task in the same group. It is used by -// Watchdog only. -void TaskManager::InterruptGroup(Task &curTk) { - TaskGroup *vg = curTk.MyTaskGroup(); - vg->interrupt_all(); -} - -void TaskManager::InterruptMaster(const Status &rc) { - TaskManager &tm = TaskManager::GetInstance(); - std::shared_ptr master = tm.master_; - std::lock_guard lck(master->mux_); - master->Interrupt(); - if (rc.IsError() && master->rc_.IsOk()) { - master->rc_ = rc; - master->caught_severe_exception_ = true; - // Move log error here for some scenarios didn't call GetMasterThreadRc - if (master->rc_.StatusCode() != mindspore::StatusCode::kMDPyFuncException) { - // use python operation, the error had been raised in python layer. So disable log prompt here. - // non-sink + non-pyfunc -> print error - // non-sink + pyfunc -> don't print error (the error has been raised in python layer) - // sink + non-pyfunc -> print error - // sink + pyfunc -> print error in ShutdownGroup -#if !defined(__APPLE__) && !defined(BUILD_LITE) && !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && \ - !defined(ANDROID) - bool independent_dataset_env = false; - std::string env_independent_dataset = common::GetEnv("MS_INDEPENDENT_DATASET"); - transform(env_independent_dataset.begin(), env_independent_dataset.end(), env_independent_dataset.begin(), - ::tolower); - if (env_independent_dataset == "true") { - independent_dataset_env = true; - } else { - independent_dataset_env = false; - } - - pid_t current_process_id = 0; -#if defined(_WIN32) || defined(_WIN64) - current_process_id = GetCurrentProcessId(); -#else - current_process_id = getpid(); -#endif - // independent dataset process mode, ignore the subprocess prompt error - // master->GetProcessID() is the main process id - if (independent_dataset_env == true && master->GetProcessID() != current_process_id) { - MS_LOG(INFO) << "[Independent Dataset Process] Ignore err msg in independent dataset process. It is: " - << master->rc_; - return; - } -#endif - MS_LOG(ERROR) << "MindSpore dataset is terminated with err msg: " << master->rc_; - } - } -} - -Status TaskManager::GetMasterThreadRc() { - TaskManager &tm = TaskManager::GetInstance(); - std::shared_ptr master = tm.master_; - Status rc = tm.master_->GetTaskErrorIfAny(); - if (rc.IsError()) { - // Reset the state once we retrieve the value. - std::lock_guard lck(master->mux_); - master->rc_ = Status::OK(); - master->caught_severe_exception_ = false; - master->ResetIntrpState(); - } - return rc; -} - -void TaskManager::ReturnFreeTask(Task *p) noexcept { - // Take it out from lru_ if any - { - UniqueLock lck(&lru_lock_); - auto iter = lru_.begin(); - for (; iter != lru_.end(); ++iter) { - if (*iter == *p) { - break; - } - } - if (iter != lru_.end()) { - lru_.Remove(p); - } - } - // We need to deallocate the string resources associated with the Task class - // before we cache its memory for future use. - p->~Task(); - // Put it back into free list - { - LockGuard lck(&free_lock_); - free_lst_.Append(p); - } -} - -Status TaskManager::GetFreeTask(const std::string &my_name, const std::function &f, Task **p, - int32_t operator_id) { - if (p == nullptr) { - RETURN_STATUS_UNEXPECTED("p is null"); - } - Task *q = nullptr; - // First try the free list - { - LockGuard lck(&free_lock_); - if (free_lst_.count > 0) { - q = free_lst_.head; - free_lst_.Remove(q); - } - } - if (q) { - new (q) Task(my_name, f, operator_id); - } else { - std::shared_ptr mp = Services::GetInstance().GetServiceMemPool(); - Status rc; - q = new (&rc, mp) Task(my_name, f, operator_id); - RETURN_IF_NOT_OK(rc); - } - *p = q; - return Status::OK(); -} - -Status TaskGroup::CreateAsyncTask(const std::string &my_name, const std::function &f, Task **ppTask, - int32_t operator_id) { - // We need to block ~TaskGroup coming otherwise we will deadlock. We will grab the - // stateLock in shared allowing CreateAsyncTask to run concurrently. - SharedLock state_lck(&state_lock_); - // Now double check the state - if (ServiceState() != STATE::kRunning) { - RETURN_STATUS_ERROR(StatusCode::kMDInterrupted, "Taskgroup is shutting down"); - } - TaskManager &dm = TaskManager::GetInstance(); - Task *pTask = nullptr; - RETURN_IF_NOT_OK(dm.CreateAsyncTask(my_name, f, this, &pTask, operator_id)); - if (ppTask) { - *ppTask = pTask; - } - return Status::OK(); -} - -void TaskGroup::interrupt_all() noexcept { - // There is a racing condition if we don't stop the interrupt service at this point. New resource - // may come in and not being picked up after we call InterruptAll(). So stop new comers and then - // interrupt any existing resources. - (void)intrp_svc_->ServiceStop(); - intrp_svc_->InterruptAll(); -} - -Status TaskGroup::join_all(Task::WaitFlag wf) { - Status rc; - Status rc2; - SharedLock lck(&rw_lock_); - for (Task &tk : grp_list_) { - rc = tk.Join(wf); - if (rc.IsError()) { - rc2 = rc; - } - } - return rc2; -} - -Status TaskGroup::DoServiceStop() { - interrupt_all(); - return (join_all(Task::WaitFlag::kNonBlocking)); -} - -TaskGroup::TaskGroup() : has_dataqueue_(false), grp_list_(&Task::group), intrp_svc_(nullptr), rc_(Status::OK()) { - auto alloc = Services::GetAllocator(); - intrp_svc_ = std::allocate_shared(alloc); - (void)Service::ServiceStart(); -} - -TaskGroup::~TaskGroup() { - (void)Service::ServiceStop(); - // The TaskGroup is going out of scope, and we can return the Task list to the free list. - Task *cur = grp_list_.head; - TaskManager &tm = TaskManager::GetInstance(); - while (cur) { - Task *next = cur->group.next; - grp_list_.Remove(cur); - tm.ReturnFreeTask(cur); - cur = next; - } - { - LockGuard lck(&tm.tg_lock_); - (void)tm.grp_list_.erase(this); - } -} - -Status TaskGroup::GetTaskErrorIfAny() { - SharedLock lck(&rw_lock_); - for (Task &tk : grp_list_) { - RETURN_IF_NOT_OK(tk.GetTaskErrorIfAny()); - } - return Status::OK(); -} - -std::shared_ptr TaskGroup::GetIntrpService() { return intrp_svc_; } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/task_manager.h b/mindspore-lite/minddata/dataset/util/task_manager.h deleted file mode 100644 index 88c04c4f6..000000000 --- a/mindspore-lite/minddata/dataset/util/task_manager.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_MANAGER_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_MANAGER_H_ - -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) -#include -#endif -#include // for sig_atomic_t -#include -#include -#include -#include -#include -#include "mindspore-lite/minddata/dataset/util/allocator.h" -#include "mindspore-lite/minddata/dataset/util/intrp_service.h" -#include "mindspore-lite/minddata/dataset/util/lock.h" -#include "mindspore-lite/minddata/dataset/util/services.h" -#include "mindspore-lite/minddata/dataset/util/status.h" -#include "mindspore-lite/minddata/dataset/util/task.h" - -namespace mindspore { -namespace dataset { -namespace thread { -using id = std::thread::id; -} // namespace thread - -namespace this_thread { -inline thread::id get_id() { return std::this_thread::get_id(); } -} // namespace this_thread - -class TaskManager : public Service { - public: - friend class Services; - - friend class TaskGroup; - - ~TaskManager() override; - - TaskManager(const TaskManager &) = delete; - - TaskManager &operator=(const TaskManager &) = delete; - - static Status CreateInstance() { - std::call_once(init_instance_flag_, [&]() -> Status { - auto &svcManager = Services::GetInstance(); - RETURN_IF_NOT_OK(svcManager.AddHook(&instance_)); - return Status::OK(); - }); - return Status::OK(); - } - - static TaskManager &GetInstance() noexcept { return *instance_; } - - Status DoServiceStart() override; - - Status DoServiceStop() override; - - // A public global interrupt flag for signal handlers - volatile sig_atomic_t global_interrupt_; - - // API - // This takes the same parameter as Task constructor. Take a look - // of the test-thread.cc for usage. - Status CreateAsyncTask(const std::string &my_name, const std::function &f, TaskGroup *vg, Task **, - int32_t operator_id = -1); - - // Same usage as boot thread group - Status join_all(); - - void interrupt_all() noexcept; - - // Locate a particular Task. - static Task *FindMe(); - - static void InterruptGroup(Task &); - - static Status GetMasterThreadRc(); - - static void InterruptMaster(const Status &rc = Status::OK()); - - static void WakeUpWatchDog() { -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - TaskManager &tm = TaskManager::GetInstance(); - (void)sem_post(&tm.sem_); -#endif - } - - void ReturnFreeTask(Task *p) noexcept; - - Status GetFreeTask(const std::string &my_name, const std::function &f, Task **p, int32_t operator_id = -1); - - Status WatchDog(); - - private: - static std::once_flag init_instance_flag_; - static TaskManager *instance_; - RWLock lru_lock_; - SpinLock free_lock_; - SpinLock tg_lock_; - std::shared_ptr master_; - List lru_; - List free_lst_; -#if !defined(_WIN32) && !defined(_WIN64) && !defined(__ANDROID__) && !defined(ANDROID) && !defined(__APPLE__) - sem_t sem_; -#endif - TaskGroup *watchdog_grp_; - std::set grp_list_; - Task *watchdog_; - - TaskManager(); -}; - -// A group of related tasks. -class TaskGroup : public Service { - public: - friend class Task; - friend class TaskManager; - - Status CreateAsyncTask(const std::string &my_name, const std::function &f, Task **pTask = nullptr, - int32_t operator_id = -1); - - void interrupt_all() noexcept; - - Status join_all(Task::WaitFlag wf = Task::WaitFlag::kBlocking); - - int size() const noexcept { return grp_list_.count; } - - List GetTask() const noexcept { return grp_list_; } - - Status DoServiceStart() override { return Status::OK(); } - - Status DoServiceStop() override; - - TaskGroup(); - - ~TaskGroup() override; - - Status GetTaskErrorIfAny(); - - std::shared_ptr GetIntrpService(); - - void HasDataQueue(bool has_dataqueue) { has_dataqueue_ = has_dataqueue; } - - private: - Status rc_; - bool has_dataqueue_; - // Can't use rw_lock_ as we will lead to deadlatch. Create another mutex to serialize access to rc_. - std::mutex rc_mux_; - RWLock rw_lock_; - List grp_list_; - std::shared_ptr intrp_svc_; -}; - -namespace this_thread { -inline bool is_interrupted() { - TaskManager &tm = TaskManager::GetInstance(); - if (tm.global_interrupt_ == 1) { - return true; - } - Task *my_task = TaskManager::FindMe(); - return my_task->Interrupted(); -} - -inline bool is_master_thread() { - Task *my_task = TaskManager::FindMe(); - return my_task->IsMasterThread(); -} - -inline Status GetInterruptStatus() { - Task *my_task = TaskManager::FindMe(); - return my_task->GetInterruptStatus(); -} -} // namespace this_thread - -#define RETURN_IF_INTERRUPTED() \ - do { \ - if (mindspore::dataset::this_thread::is_interrupted()) { \ - return Task::OverrideInterruptRc(this_thread::GetInterruptStatus()); \ - } \ - } while (false) - -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TASK_MANAGER_H_ diff --git a/mindspore-lite/minddata/dataset/util/treap.h b/mindspore-lite/minddata/dataset/util/treap.h deleted file mode 100644 index 8e8003f2c..000000000 --- a/mindspore-lite/minddata/dataset/util/treap.h +++ /dev/null @@ -1,407 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TREAP_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TREAP_H_ - -#include -#include -#include -#include -#include - -namespace mindspore { -namespace dataset { -// A treap is a combination of binary search tree and heap. Each key is given a priority. The priority -// for any non-leaf node is greater than or equal to the priority of its children. -// @tparam K -// Data type of key -// @tparam P -// Data type of priority -// @tparam KC -// Class to compare key. Default to std::less -// @tparam KP -// Class to compare priority. Default to std:less -template , typename KP = std::less

> -class Treap { - public: - using key_type = K; - using priority_type = P; - using key_compare = KC; - using priority_compare = KP; - - struct NodeValue { - key_type key; - priority_type priority; - }; - - class TreapNode { - public: - TreapNode() : left(nullptr), right(nullptr) {} - ~TreapNode() { - left = nullptr; - right = nullptr; - } - NodeValue nv; - TreapNode *left; - TreapNode *right; - }; - - // search API - // @param k - // key to search for - // @return - // a pair is returned. The 2nd value of type bool indicate if the search is successful. - // If true, the first value of the pair contains the key and the priority. - std::pair Search(key_type k) const { - auto *n = Search(root_, k); - if (n != nullptr) { - return std::make_pair(n->nv, true); - } else { - return std::make_pair(NodeValue{key_type(), priority_type()}, false); - } - } - - // @return - // Return the root of the heap. It has the highest priority. But not necessarily the first key. - std::pair Top() const { - if (root_ != nullptr) { - return std::make_pair(root_->nv, true); - } else { - return std::make_pair(NodeValue{key_type(), priority_type()}, false); - } - } - - // Remove the root of the heap. - void Pop() { - if (root_ != nullptr) { - DeleteKey(root_->nv.key); - } - } - - // Insert API. - // @param k - // The key to insert. - // @param p - // The priority of the key. - void Insert(key_type k, priority_type p) { root_ = Insert(root_, k, p); } - - // Delete a key. - // @param k - void DeleteKey(key_type k) { root_ = DeleteNode(root_, k); } - - Treap() : root_(nullptr), count_(0) { free_list_.reserve(kResvSz); } - - ~Treap() noexcept { - DeleteTreap(root_); - while (!free_list_.empty()) { - TreapNode *n = free_list_.back(); - delete (n); - free_list_.pop_back(); - } - } - - class iterator : public std::iterator { - public: - explicit iterator(Treap *tr) : tr_(tr), cur_(nullptr) { - if (tr_ != nullptr) { - cur_ = tr_->root_; - while (cur_ != nullptr) { - stack_.push(cur_); - cur_ = cur_->left; - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - } - ~iterator() { - tr_ = nullptr; - cur_ = nullptr; - } - - NodeValue &operator*() { return cur_->nv; } - - NodeValue *operator->() { return &(cur_->nv); } - - const TreapNode &operator*() const { return *cur_; } - - const TreapNode *operator->() const { return cur_; } - - bool operator==(const iterator &rhs) const { return cur_ == rhs.cur_; } - - bool operator!=(const iterator &rhs) const { return cur_ != rhs.cur_; } - - // Prefix increment - iterator &operator++() { - if (cur_) { - stack_.pop(); - if (cur_->right) { - TreapNode *n = cur_->right; - while (n) { - stack_.push(n); - n = n->left; - } - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - return *this; - } - - // Postfix increment - iterator operator++(int junk) { - iterator tmp(*this); - if (cur_) { - stack_.pop(); - if (cur_->right) { - TreapNode *n = cur_->right; - while (n) { - stack_.push(n); - n = n->left; - } - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - return tmp; - } - - private: - Treap *tr_; - TreapNode *cur_; - std::stack stack_; - }; - - class const_iterator : public std::iterator { - public: - explicit const_iterator(const Treap *tr) : tr_(tr), cur_(nullptr) { - if (tr_ != nullptr) { - cur_ = tr_->root_; - while (cur_ != nullptr) { - stack_.push(cur_); - cur_ = cur_->left; - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - } - ~const_iterator() { - tr_ = nullptr; - cur_ = nullptr; - } - - const NodeValue &operator*() const { return cur_->nv; } - - const NodeValue *operator->() const { return &(cur_->nv); } - - bool operator==(const const_iterator &rhs) const { return cur_ == rhs.cur_; } - - bool operator!=(const const_iterator &rhs) const { return cur_ != rhs.cur_; } - - // Prefix increment - const_iterator &operator++() { - if (cur_) { - stack_.pop(); - if (cur_->right != nullptr) { - TreapNode *n = cur_->right; - while (n) { - stack_.push(n); - n = n->left; - } - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - return *this; - } - - // Postfix increment - const_iterator operator++(int junk) { - iterator tmp(*this); - if (cur_) { - stack_.pop(); - if ((cur_->right) != nullptr) { - TreapNode *n = cur_->right; - while (n) { - stack_.push(n); - n = n->left; - } - } - } - if (!stack_.empty()) { - cur_ = stack_.top(); - } else { - cur_ = nullptr; - } - return tmp; - } - - private: - const Treap *tr_; - TreapNode *cur_; - std::stack stack_; - }; - - iterator begin() { return iterator(this); } - - iterator end() { return iterator(nullptr); } - - const_iterator begin() const { return const_iterator(this); } - - const_iterator end() const { return const_iterator(nullptr); } - - const_iterator cbegin() { return const_iterator(this); } - - const_iterator cend() { return const_iterator(nullptr); } - - bool empty() { return root_ == nullptr; } - - size_t size() { return count_; } - - private: - TreapNode *NewNode() { - TreapNode *n = nullptr; - if (!free_list_.empty()) { - n = free_list_.back(); - free_list_.pop_back(); - new (n) TreapNode(); - } else { - n = new TreapNode(); - } - return n; - } - - void FreeNode(TreapNode *n) { free_list_.push_back(n); } - - void DeleteTreap(TreapNode *n) noexcept { - if (n == nullptr) { - return; - } - TreapNode *x = n->left; - TreapNode *y = n->right; - delete (n); - DeleteTreap(x); - DeleteTreap(y); - } - - TreapNode *RightRotate(TreapNode *y) { - TreapNode *x = y->left; - TreapNode *T2 = x->right; - x->right = y; - y->left = T2; - return x; - } - - TreapNode *LeftRotate(TreapNode *x) { - TreapNode *y = x->right; - TreapNode *T2 = y->left; - y->left = x; - x->right = T2; - return y; - } - - TreapNode *Search(TreapNode *n, key_type k) const { - key_compare keyCompare; - if (n == nullptr) { - return n; - } else if (keyCompare(k, n->nv.key)) { - return Search(n->left, k); - } else if (keyCompare(n->nv.key, k)) { - return Search(n->right, k); - } else { - return n; - } - } - - TreapNode *Insert(TreapNode *n, key_type k, priority_type p) { - key_compare keyCompare; - priority_compare priorityCompare; - if (n == nullptr) { - n = NewNode(); - n->nv.key = k; - n->nv.priority = p; - count_++; - return n; - } - if (keyCompare(k, n->nv.key)) { - n->left = Insert(n->left, k, p); - if (priorityCompare(n->nv.priority, n->left->nv.priority)) { - n = RightRotate(n); - } - } else if (keyCompare(n->nv.key, k)) { - n->right = Insert(n->right, k, p); - if (priorityCompare(n->nv.priority, n->right->nv.priority)) { - n = LeftRotate(n); - } - } else { - // If we insert the same key again, do nothing. - return n; - } - return n; - } - - TreapNode *DeleteNode(TreapNode *n, key_type k) { - key_compare keyCompare; - priority_compare priorityCompare; - if (n == nullptr) { - return n; - } - if (keyCompare(k, n->nv.key)) { - n->left = DeleteNode(n->left, k); - } else if (keyCompare(n->nv.key, k)) { - n->right = DeleteNode(n->right, k); - } else if (n->left == nullptr) { - TreapNode *t = n; - n = n->right; - FreeNode(t); - count_--; - } else if (n->right == nullptr) { - TreapNode *t = n; - n = n->left; - FreeNode(t); - count_--; - } else if (priorityCompare(n->left->nv.priority, n->right->nv.priority)) { - n = LeftRotate(n); - n->left = DeleteNode(n->left, k); - } else { - n = RightRotate(n); - n->right = DeleteNode(n->right, k); - } - return n; - } - - static constexpr int kResvSz = 512; - TreapNode *root_; - size_t count_; - std::vector free_list_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_TREAP_H_ diff --git a/mindspore-lite/minddata/dataset/util/validators.cc b/mindspore-lite/minddata/dataset/util/validators.cc deleted file mode 100644 index 4a9c65fec..000000000 --- a/mindspore-lite/minddata/dataset/util/validators.cc +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/validators.h" - -namespace mindspore { -namespace dataset { -// implement the validate function here -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/validators.h b/mindspore-lite/minddata/dataset/util/validators.h deleted file mode 100644 index 6ce42d05d..000000000 --- a/mindspore-lite/minddata/dataset/util/validators.h +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ - -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_VALIDATORS_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_VALIDATORS_H_ - -#include -#include -#include -#include - -#include - -#include "mindspore-lite/minddata/dataset/core/tensor.h" -#include "mindspore-lite/minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// validator Parameter in json file -inline Status ValidateParamInJson(const nlohmann::json &json_obj, const std::string ¶m_name, - const std::string &operator_name) { - if (json_obj.find(param_name) == json_obj.end()) { - std::string err_msg = "Failed to find key '" + param_name + "' in " + operator_name + - "' JSON file or input dict, check input content of deserialize()."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -inline Status ValidateTensorShape(const std::string &op_name, bool cond, const std::string &expected_shape = "", - const std::string &actual_dim = "") { - if (!cond) { - std::string err_msg = op_name + ": the shape of input tensor does not match the requirement of operator."; - if (expected_shape != "") { - err_msg += " Expecting tensor in shape of " + expected_shape + "."; - } - if (actual_dim != "") { - err_msg += " But got tensor with dimension " + actual_dim + "."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -inline Status ValidateLowRank(const std::string &op_name, const std::shared_ptr &input, dsize_t threshold = 0, - const std::string &expected_shape = "") { - dsize_t dim = input->shape().Size(); - return ValidateTensorShape(op_name, dim >= threshold, expected_shape, std::to_string(dim)); -} - -inline Status ValidateTensorType(const std::string &op_name, bool cond, const std::string &expected_type = "", - const std::string &actual_type = "") { - if (!cond) { - std::string err_msg = op_name + ": the data type of input tensor does not match the requirement of operator."; - if (expected_type != "") { - err_msg += " Expecting tensor in type of " + expected_type + "."; - } - if (actual_type != "") { - err_msg += " But got type " + actual_type + "."; - } - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -inline Status ValidateTensorNumeric(const std::string &op_name, const std::shared_ptr &input) { - return ValidateTensorType(op_name, input->type().IsNumeric(), "[int, float, double]", input->type().ToString()); -} - -inline Status ValidateTensorFloat(const std::string &op_name, const std::shared_ptr &input) { - return ValidateTensorType(op_name, input->type().IsFloat(), "[float, double]", input->type().ToString()); -} - -template -inline Status ValidateEqual(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value != other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' should be equal to '" + other_name + - "', but got: " + param_name + " " + std::to_string(param_value) + " while " + other_name + - " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNotEqual(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value == other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' can not be equal to '" + other_name + - "', but got: " + param_name + " " + std::to_string(param_value) + " while " + other_name + - " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateGreaterThan(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value <= other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' should be greater than '" + other_name + - "', but got: " + param_name + " " + std::to_string(param_value) + " while " + other_name + - " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateLessThan(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value >= other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' should be less than '" + other_name + - "', but got: " + param_name + " " + std::to_string(param_value) + " while " + other_name + - " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNoGreaterThan(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value > other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' should be no greater than '" + - other_name + "', but got: " + param_name + " " + std::to_string(param_value) + " while " + - other_name + " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNoLessThan(const std::string &op_name, const std::string ¶m_name, T param_value, - const std::string &other_name, T other_value) { - if (param_value < other_value) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + "' should be no less than '" + other_name + - "', but got: " + param_name + " " + std::to_string(param_value) + " while " + other_name + - " " + std::to_string(other_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidatePositive(const std::string &op_name, const std::string ¶m_name, T param_value) { - if (param_value <= 0) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + - "' should be positive, but got: " + std::to_string(param_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNegative(const std::string &op_name, const std::string ¶m_name, T param_value) { - if (param_value >= 0) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + - "' should be negative, but got: " + std::to_string(param_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNonPositive(const std::string &op_name, const std::string ¶m_name, T param_value) { - if (param_value > 0) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + - "' should be non positive, but got: " + std::to_string(param_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -template -inline Status ValidateNonNegative(const std::string &op_name, const std::string ¶m_name, T param_value) { - if (param_value < 0) { - std::string err_msg = op_name + ": invalid parameter, '" + param_name + - "' should be non negative, but got: " + std::to_string(param_value) + "."; - RETURN_STATUS_UNEXPECTED(err_msg); - } - return Status::OK(); -} - -inline std::string DataTypeSetToString(const std::set &valid_dtype) { - std::string init; - std::string err_msg = - std::accumulate(valid_dtype.begin(), valid_dtype.end(), init, [](const std::string &str, uint8_t dtype) { - if (str.empty()) { - return DataType(DataType::Type(dtype)).ToString(); - } else { - return str + ", " + DataType(DataType::Type(dtype)).ToString(); - } - }); - return "(" + err_msg + ")"; -} - -template -std::string NumberSetToString(const std::set &valid_value) { - std::string init; - std::string err_msg = - std::accumulate(valid_value.begin(), valid_value.end(), init, [](const std::string &str, T value) { - if (str.empty()) { - return std::to_string(value); - } else { - return str + ", " + std::to_string(value); - } - }); - return "(" + err_msg + ")"; -} -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_VALIDATORS_H_ diff --git a/mindspore-lite/minddata/dataset/util/wait_post.cc b/mindspore-lite/minddata/dataset/util/wait_post.cc deleted file mode 100644 index 82f97feb3..000000000 --- a/mindspore-lite/minddata/dataset/util/wait_post.cc +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * 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. - */ -#include "mindspore-lite/minddata/dataset/util/wait_post.h" -#include "mindspore-lite/minddata/dataset/util/task_manager.h" - -namespace mindspore { -namespace dataset { -WaitPost::WaitPost() : value_(0) {} - -Status WaitPost::Wait() { - std::unique_lock lck(mutex_); - return (wait_cond_.Wait(&lck, [this]() { return value_ != 0; })); -} - -void WaitPost::Set() { - std::unique_lock lck(mutex_); - value_ = 1; - wait_cond_.NotifyAll(); -} - -void WaitPost::Clear() { - std::unique_lock lck(mutex_); - value_ = 0; -} - -Status WaitPost::Register(TaskGroup *vg) { return wait_cond_.Register(vg->GetIntrpService()); } - -void WaitPost::ResetIntrpState() { wait_cond_.ResetIntrpState(); } - -Status WaitPost::Deregister() { return wait_cond_.Deregister(); } -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/dataset/util/wait_post.h b/mindspore-lite/minddata/dataset/util/wait_post.h deleted file mode 100644 index 38522f32f..000000000 --- a/mindspore-lite/minddata/dataset/util/wait_post.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2019-2024 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_WAIT_POST_H_ -#define MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_WAIT_POST_H_ - -#include - -#include "mindspore-lite/minddata/dataset/util/cond_var.h" - -namespace mindspore { -namespace dataset { -class TaskGroup; - -class WaitPost { - public: - WaitPost(); - - ~WaitPost() = default; - - Status Wait(); - - void Set(); - - void Clear(); - - Status Register(TaskGroup *vg); - - Status Deregister(); - - void ResetIntrpState(); - - private: - std::mutex mutex_; - CondVar wait_cond_; - int value_; -}; -} // namespace dataset -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_UTIL_WAIT_POST_H_ diff --git a/mindspore-lite/minddata/example/CMakeLists.txt b/mindspore-lite/minddata/example/CMakeLists.txt deleted file mode 100644 index 7deb5e1a8..000000000 --- a/mindspore-lite/minddata/example/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -cmake_minimum_required(VERSION 3.14.1) -project(testlenet) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -fPIC -std=c++17") - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare") - -set(MS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mindspore-lite-1.5.0-linux-x64/runtime") -set(LITECV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/mindspore-lite-1.5.0-linux-x64/runtime/include/dataset") - -include_directories(${MS_DIR} ${LITECV_DIR}) - - -add_executable(testlenet - ${CMAKE_CURRENT_SOURCE_DIR}/testlenet.cpp - ) - -target_link_libraries(testlenet - ${MS_DIR}/lib/libminddata-lite.so - ${MS_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so.62 - ${MS_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so.0 - ${MS_DIR}/lib/libmindspore-lite.so - pthread) - -add_executable(testresize - ${CMAKE_CURRENT_SOURCE_DIR}/testresize.cpp - ) - -target_link_libraries(testresize - ${MS_DIR}/lib/libminddata-lite.so - ${MS_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so.62 - ${MS_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so.0 - ${MS_DIR}/lib/libmindspore-lite.so - pthread) - -add_executable(testlitecv - ${CMAKE_CURRENT_SOURCE_DIR}/testlitecv.cpp - ) - -target_link_libraries(testlitecv - ${MS_DIR}/lib/libminddata-lite.so - ${MS_DIR}/third_party/libjpeg-turbo/lib/libjpeg.so.62 - ${MS_DIR}/third_party/libjpeg-turbo/lib/libturbojpeg.so.0 - pthread) \ No newline at end of file diff --git a/mindspore-lite/minddata/example/testlenet.cpp b/mindspore-lite/minddata/example/testlenet.cpp deleted file mode 100644 index a26f92a02..000000000 --- a/mindspore-lite/minddata/example/testlenet.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/dataset/datasets.h" -#include "include/dataset/iterator.h" -#include "include/dataset/vision_lite.h" -#include "include/dataset/transforms.h" -#include "include/api/types.h" - -using mindspore::dataset::Dataset; -using mindspore::dataset::Iterator; -using mindspore::dataset::Mnist; -using mindspore::dataset::TensorTransform; - -int main(int argc, char **argv) { - std::string folder_path = "./testMnistData/"; - std::shared_ptr ds = Mnist(folder_path, "all"); - - std::shared_ptr resize(new mindspore::dataset::vision::Resize({32, 32})); - ds = ds->Map({resize}); - - ds = ds->Shuffle(2); - ds = ds->Batch(2); - - std::shared_ptr iter = ds->CreateIterator(); - - std::unordered_map row; - iter->GetNextRow(&row); - - while (row.size() != 0) { - iter->GetNextRow(&row); - } - - iter->Stop(); -} diff --git a/mindspore-lite/minddata/example/testlitecv.cpp b/mindspore-lite/minddata/example/testlitecv.cpp deleted file mode 100644 index 757a22bad..000000000 --- a/mindspore-lite/minddata/example/testlitecv.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/api/types.h" -#include "include/dataset/lite_cv/lite_mat.h" -#include "include/dataset/lite_cv/image_process.h" -#include "include/dataset/vision_lite.h" -#include "include/dataset/execute.h" - -using mindspore::dataset::Execute; -using mindspore::dataset::LDataType; -using mindspore::dataset::LiteMat; -using mindspore::dataset::PaddBorderType; -using mindspore::dataset::vision::Decode; - -int main(int argc, char **argv) { - std::ifstream ifs("../../../../tests/ut/data/dataset/apple.jpg"); - - if (!ifs.is_open() || !ifs.good()) { - std::cout << "fail to load image, check image path" << std::endl; - return -1; - } - - ifs.seekg(0, std::ios::end); - size_t size = ifs.tellg(); - mindspore::MSTensor image("file", mindspore::DataType::kNumberTypeUInt8, {static_cast(size)}, nullptr, size); - - ifs.seekg(0, std::ios::beg); - ifs.read(reinterpret_cast(image.MutableData()), size); - ifs.close(); - - auto decode = Decode(); - auto executor = Execute(decode); - executor(image, &image); - - constexpr int32_t image_h = 0; - constexpr int32_t image_w = 1; - constexpr int32_t image_c = 2; - LiteMat lite_mat_rgb(image.Shape()[image_w], image.Shape()[image_h], image.Shape()[image_c], - const_cast(image.Data().get()), LDataType::UINT8); - std::cout << "lite_mat_rgb: height=" << lite_mat_rgb.height_ << ", width=" << lite_mat_rgb.width_ << std::endl; - - LiteMat lite_mat_resize; - constexpr int32_t target_size = 256; - ResizeBilinear(lite_mat_rgb, lite_mat_resize, target_size, target_size); - std::cout << "lite_mat_resize: height=" << lite_mat_resize.height_ << ", width=" << lite_mat_resize.width_ - << std::endl; - - LiteMat lite_mat_pad; - constexpr int32_t pad_top = 30; - constexpr int32_t pad_bottom = 30; - constexpr int32_t pad_left = 10; - constexpr int32_t pad_right = 10; - constexpr int32_t pad_color = 255; - Pad(lite_mat_resize, lite_mat_pad, pad_top, pad_bottom, pad_left, pad_right, PaddBorderType::PADD_BORDER_CONSTANT, - pad_color, pad_color, pad_color); - std::cout << "lite_mat_pad: height=" << lite_mat_pad.height_ << ", width=" << lite_mat_pad.width_ << std::endl; -} diff --git a/mindspore-lite/minddata/example/testresize.cpp b/mindspore-lite/minddata/example/testresize.cpp deleted file mode 100644 index 907ccc88a..000000000 --- a/mindspore-lite/minddata/example/testresize.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright 2021 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/dataset/datasets.h" -#include "include/dataset/iterator.h" -#include "include/dataset/vision_lite.h" -#include "include/dataset/transforms.h" -#include "include/api/types.h" - -using mindspore::dataset::Album; -using mindspore::dataset::Dataset; -using mindspore::dataset::Iterator; -using mindspore::dataset::SequentialSampler; -using mindspore::dataset::TensorTransform; -using mindspore::dataset::vision::ResizePreserveAR; - - -int main(int argc, char **argv) { - std::string folder_path = "./testAlbum/images"; - std::string schema_file = "./testAlbum/datasetSchema.json"; - std::vector column_names = {"image", "label", "id"}; - - // Create a Album Dataset - std::shared_ptr ds = - Album(folder_path, schema_file, column_names, true, std::make_shared(0, 1)); - ds = ds->SetNumWorkers(1); - - std::shared_ptr resize(new ResizePreserveAR(1000, 1000)); - ds = ds->Map({resize}, {"image"}, {"image", "ratio", "invM"}); - - std::shared_ptr iter = ds->CreateIterator(); - - std::unordered_map row; - iter->GetNextRow(&row); - - while (row.size() != 0) { - iter->GetNextRow(&row); - } - - iter->Stop(); -} diff --git a/mindspore-lite/minddata/wrapper/MDToDApi.cc b/mindspore-lite/minddata/wrapper/MDToDApi.cc deleted file mode 100644 index 62c9d2f5f..000000000 --- a/mindspore-lite/minddata/wrapper/MDToDApi.cc +++ /dev/null @@ -1,471 +0,0 @@ -/** - * Copyright 2020-2023 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "MDToDApi.h" // NOLINT - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "album_op_android.h" // NOLINT -#include "minddata/dataset/include/dataset/execute.h" -#include "minddata/dataset/include/dataset/type_id.h" -#include "minddata/dataset/util/path.h" -#include "minddata/dataset/include/dataset/vision.h" -#include "minddata/dataset/include/dataset/data_helper.h" -#include "minddata/dataset/core/de_tensor.h" -#include "include/api/types.h" -#if defined(__ANDROID__) || defined(ANDROID) -#include -#include -#endif - -using mindspore::dataset::Path; -using mindspore::dataset::Tensor; - -using TensorOperation = mindspore::dataset::TensorOperation; -using RotateOperation = mindspore::dataset::vision::RotateOperation; - -using mindspore::LogStream; -using mindspore::MsLogLevel::DEBUG; -using mindspore::MsLogLevel::ERROR; -using mindspore::MsLogLevel::INFO; - -using mindspore::Status; -using mindspore::dataset::BorderType; -using mindspore::dataset::InterpolationMode; - -namespace mindspore { -class MDToDApi { - public: - std::shared_ptr _iter; - std::vector> _augs; - std::string _storage_folder; - std::string _folder_path; - bool _hasBatch; - int64_t _file_id; - - public: - MDToDApi() : _iter(nullptr), _augs({}), _storage_folder(""), _file_id(-1), _hasBatch(false) { - MS_LOG(INFO) << "MDToDAPI Call constructor"; - } - ~MDToDApi() { - MS_LOG(INFO) << "MDToDAPI Call destractor"; - // dereference dataset and iterator - _augs.clear(); - } -}; - -std::vector MDToDBuffToVector(MDToDBuff_t StrBuff) { - std::vector strVector; - if (StrBuff.DataSize > 0) { - const char *p = static_cast(StrBuff.Buff); - do { - strVector.push_back(std::string(p)); - p += strVector.back().size() + 1; - } while (p < static_cast(StrBuff.Buff) + StrBuff.DataSize); - } - return strVector; -} - -extern "C" - -int MDToDApi_pathTest(const char *path) { - Path f(path); - MS_LOG(INFO) << f.Exists() << f.IsDirectory() << f.ParentPath(); - // Print out the first few items in the directory - auto dir_it = Path::DirIterator::OpenDirectory(&f); - MS_LOG(INFO) << dir_it.get(); - int i = 0; - const int path_len_limit = 5; - while (dir_it->hasNext()) { - Path v = dir_it->next(); - MS_LOG(INFO) << v.toString() << "\n"; - i++; - if (i > path_len_limit) { - break; - } - } - return 0; -} - -extern "C" MDToDApi *MDToDApi_createPipeLine(MDToDConf_t MDConf) { - MS_LOG(INFO) << "Start createPipeLine"; - std::string folder_path(MDConf.pFolderPath); - std::string schema_file(MDConf.pSchemFile); - std::vector column_names = MDToDBuffToVector(MDConf.columnsToReadBuff); - if (std::find(column_names.begin(), column_names.end(), "id") == column_names.end()) { - MS_LOG(INFO) << "Column id not foud adding it "; - column_names.push_back("id"); - } - std::vector> mapOperations; - if (std::find(column_names.begin(), column_names.end(), "image") != column_names.end()) { - MS_LOG(INFO) << "Found column image create map with:"; - MS_LOG(INFO) << "resize: { " << MDConf.ResizeSizeWH[0] << ", " << MDConf.ResizeSizeWH[1] << " }"; - MS_LOG(INFO) << "crop: { " << MDConf.CropSizeWH[0] << ", " << MDConf.CropSizeWH[1] << " }"; - MS_LOG(INFO) << "MEAN: { " << MDConf.MEAN[0] << ", " << MDConf.MEAN[1] << ", " << MDConf.MEAN[2] << " }"; - MS_LOG(INFO) << "STD: { " << MDConf.STD[0] << ", " << MDConf.STD[1] << ", " << MDConf.STD[2] << " }"; - - if ((MDConf.ResizeSizeWH[0] != 0) && (MDConf.ResizeSizeWH[1] != 0)) { - std::shared_ptr resize_op = - mindspore::dataset::vision::Resize({MDConf.ResizeSizeWH[0], MDConf.ResizeSizeWH[1]}); - MS_LOG(INFO) << "Push back resize"; - mapOperations.push_back(resize_op); - } - if (MDConf.fixOrientation == 1) { - std::shared_ptr rotate_op = mindspore::dataset::vision::Rotate(); - MS_LOG(INFO) << "Push back rotate"; - mapOperations.push_back(rotate_op); - // hasBatch = true; Batch not currently supported inMInddata-Lite - } - if ((MDConf.CropSizeWH[0] != 0) && (MDConf.CropSizeWH[1] != 0)) { - std::vector Crop(MDConf.CropSizeWH, MDConf.CropSizeWH + 2); - std::shared_ptr center_crop_op = mindspore::dataset::vision::CenterCrop(Crop); - MS_LOG(INFO) << "Push back crop"; - mapOperations.push_back(center_crop_op); - // hasBatch = true; Batch not currently supported inMInddata-Lite - } - } - - MS_LOG(INFO) << "Read id=" << MDConf.fileid << " (-1) for all"; - std::shared_ptr iter = nullptr; - const std::set exts = {}; - if (MDConf.fileid > -1) { - // read specific image using SequentialSampler witn - iter = - std::make_shared(folder_path, true, schema_file, column_names, exts, MDConf.fileid); - } else { - iter = std::make_shared(folder_path, true, schema_file, column_names, exts); - } - - // Create objects for the tensor ops - MS_LOG(INFO) << " Create pipeline parameters"; - MS_LOG(INFO) << "floder path: " << folder_path << " , schema json: " << schema_file; - MS_LOG(INFO) << "Reading columns:"; - for (auto str : column_names) { - MS_LOG(INFO) << str << " "; - } - bool hasBatch = false; - - MDToDApi *pMDToDApi = new MDToDApi; - pMDToDApi->_iter = iter; - pMDToDApi->_augs = mapOperations; - pMDToDApi->_storage_folder = std::string(MDConf.pStoragePath); - pMDToDApi->_folder_path = folder_path; - pMDToDApi->_hasBatch = hasBatch; - return pMDToDApi; -} - -template -void MDBuffToVector(const MDToDBuff_t &MDBuff, std::vector *vec) { - vec->clear(); - if (MDBuff.DataSize > 0) { - int nofElements = MDBuff.DataSize / sizeof(T); - vec->assign(reinterpret_cast(MDBuff.Buff), reinterpret_cast(MDBuff.Buff) + nofElements); - } -} - -template -void GetValue(std::unordered_map> row, std::string columnName, T *o) { - auto column = row[columnName]; - if (column != NULL) { - MS_LOG(INFO) << "Tensor " << columnName << " shape: " << column->shape() << " type: " << column->type() - << " bytes: " << column->SizeInBytes(); - column->GetItemAt(o, {}); - MS_LOG(INFO) << columnName << ": " << +*o; - } else { - MS_LOG(INFO) << "Tensor " << columnName << " Not found" - << "."; - *o = 0; - } -} - -void GetTensorToBuff(std::unordered_map> row, std::string columnName, - bool hasBatch, MDToDBuff_t *resBuff) { - auto column = row[columnName]; - resBuff->TensorSize[0] = resBuff->TensorSize[1] = resBuff->TensorSize[2] = resBuff->TensorSize[3] = - 0; // Mark all dims do not exist in tensor - int firstDim = (hasBatch) ? 1 : 0; - if (column != NULL) { - MS_LOG(INFO) << "Tensor " << columnName << " shape: " << column->shape() << " type: " << column->type() - << " bytes: " << column->SizeInBytes() << "nof elements: " << column->shape()[firstDim]; - auto tesoreShape = column->shape().AsVector(); - for (int ix = 0; ix < tesoreShape.size(); ix++) { - MS_LOG(INFO) << "Tensor " << columnName << " shape[" << ix << "] = " << tesoreShape[ix]; - resBuff->TensorSize[ix] = tesoreShape[ix]; - } - if (!hasBatch) { - for (int ix = 3; ix > 0; ix--) { - resBuff->TensorSize[ix] = resBuff->TensorSize[ix - 1]; - } - resBuff->TensorSize[0] = 1; - } - if (column->shape()[firstDim] > 0) { - if (mindspore::dataset::DataType::DE_STRING == column->type()) { - std::string str; - for (int ix = 0; ix < column->shape()[firstDim]; ix++) { - std::string_view strView; - if (hasBatch) { - column->GetItemAt(&strView, {0, ix}); - } else { - column->GetItemAt(&strView, {ix}); - } - MS_LOG(INFO) << "string " << columnName << "[" << ix << "]:" << strView << " (size: " << strView.size() - << ")"; - str.append(strView); - str.push_back('\0'); - } - resBuff->DataSize = str.size(); - errno_t ret = memcpy_s(resBuff->Buff, resBuff->MaxBuffSize, str.data(), resBuff->DataSize); - if (ret != EOK) { - resBuff->DataSize = 0; // memcpy fail amount of data copied is 0 - MS_LOG(ERROR) << "memcpy_s return: " << ret; - } - } else { - mindspore::dataset::DataHelper dh; - resBuff->DataSize = - dh.DumpData(column->GetBuffer(), column->SizeInBytes(), resBuff->Buff, resBuff->MaxBuffSize); - } - MS_LOG(INFO) << columnName << " " << resBuff->DataSize - << " bytesCopyed to buff (MaxBuffSize: " << resBuff->MaxBuffSize << ") "; - if (resBuff->DataSize == 0) { - MS_LOG(ERROR) << "COPY FAIL!!!! " << columnName << " Too large" - << "."; // memcpy failed - } - } else { - MS_LOG(INFO) << "Tensor " << columnName << " is empty (has size 0)"; - } - } else { - MS_LOG(INFO) << "Tensor " << columnName << " was not read."; - } -} - -extern "C" int MDToDApi_GetNext(MDToDApi *pMDToDApi, MDToDResult_t *results) { - MS_LOG(INFO) << "Start GetNext"; - if (pMDToDApi == nullptr || pMDToDApi->_iter == nullptr) { - MS_LOG(ERROR) << "GetNext called with null ptr. abort"; - return -1; - } - - // Set default - results->fileid = -1; - results->embeddingBuff.DataSize = 0; - results->imageBuff.DataSize = 0; - MS_LOG(INFO) << "Start GetNext [1]" << pMDToDApi; - // get next row for dataset - std::unordered_map> row; - // create Execute functions, this replaces Map in Pipeline - - bool ret = pMDToDApi->_iter->GetNextRow(&row); - uint32_t orientation = 0; - if (row.size() != 0 && ret) { - GetValue(row, "orientation", &orientation); - MS_LOG(INFO) << "get orientation from row = " << orientation; - if ((pMDToDApi->_augs).size() > 0) { - // String and Tensors - - // for each operation, run eager mode, single threaded operation, will have to memcpy - // regardless - for (int i = 0; i < (pMDToDApi->_augs).size(); i++) { - // each Execute call will invoke a memcpy, this cannot really be optimized further - // for this use case, std move is added for fail save. - if (pMDToDApi->_augs[i]->Name() == "Rotate") { - if (orientation > 1) { - RotateOperation *p = static_cast(pMDToDApi->_augs[i].get()); - p->setAngle(orientation); - orientation = 0; // clear oriation filed if already performed - } else { - continue; - } - } - mindspore::MSTensor image(std::make_shared(row["image"])); - (void)mindspore::dataset::Execute((pMDToDApi->_augs)[i])(image, &image); - mindspore::dataset::Tensor::CreateFromMemory( - mindspore::dataset::TensorShape(image.Shape()), - mindspore::dataset::MSTypeToDEType(static_cast(image.DataType())), - (const uint8_t *)(image.Data().get()), &(row["image"])); - if (row["image"] == nullptr) { - // nullptr means that the eager mode image processing failed, we fail in this case - return -1; - } - } - } - // FILE ID - GetValue(row, "id", &results->fileid); - pMDToDApi->_file_id = results->fileid; // hold current file id to enable embeddings update (no itr->getCurrent) - // IS FOR TRAIN - GetValue(row, "_isForTrain", &results->isForTrain); - GetValue(row, "_noOfFaces", &results->noOfFaces); - results->orientation = static_cast(orientation); - // String and Tensors - GetTensorToBuff(row, "image_filename", pMDToDApi->_hasBatch, &results->fileNameBuff); - GetTensorToBuff(row, "image", pMDToDApi->_hasBatch, &results->imageBuff); - GetTensorToBuff(row, "_embedding", pMDToDApi->_hasBatch, &results->embeddingBuff); - GetTensorToBuff(row, "label", pMDToDApi->_hasBatch, &results->labelBuff); - GetTensorToBuff(row, "_boundingBoxes", pMDToDApi->_hasBatch, &results->boundingBoxesBuff); - GetTensorToBuff(row, "_confidences", pMDToDApi->_hasBatch, &results->confidencesBuff); - GetTensorToBuff(row, "_landmarks", pMDToDApi->_hasBatch, &results->landmarksBuff); - GetTensorToBuff(row, "_faceFileNames", pMDToDApi->_hasBatch, &results->faceFileNamesBuff); - GetTensorToBuff(row, "_imageQualities", pMDToDApi->_hasBatch, &results->imageQualitiesBuff); - GetTensorToBuff(row, "_faceEmbeddings", pMDToDApi->_hasBatch, &results->faceEmbeddingsBuff); - return 0; - } - return -1; -} - -extern "C" int MDToDApi_Stop(MDToDApi *pMDToDApi) { - // Manually terminate the pipeline - MS_LOG(INFO) << "pipeline stopped"; - return 0; -} - -extern "C" int MDToDApi_Destroy(MDToDApi *pMDToDApi) { - MS_LOG(INFO) << "pipeline deleted start"; - delete pMDToDApi; - MS_LOG(INFO) << "pipeline deleted end"; - return 0; -} - -int GetJsonFullFileName(const MDToDApi *pMDToDApi, std::string *filePath) { - int64_t file_id = pMDToDApi->_file_id; - if (file_id < 0) { - MS_LOG(ERROR) << "Illegal file ID to update: " << file_id << "."; - return -1; - } - std::string converted = std::to_string(pMDToDApi->_file_id); - *filePath = pMDToDApi->_folder_path + "/" + converted + ".json"; - return 0; -} - -extern "C" int MDToDApi_UpdateEmbeding(MDToDApi *pMDToDApi, const char *column, float *emmbeddings, - size_t emmbeddingsSize) { - auto columnName = std::string(column); - MS_LOG(INFO) << "Start Update " << columnName; - - std::string converted = std::to_string(pMDToDApi->_file_id); - std::string embedding_file_path = pMDToDApi->_storage_folder + "/" + converted + columnName + ".bin"; - mindspore::dataset::DataHelper dh; - MS_LOG(INFO) << "Try to Save file " << embedding_file_path; - std::vector bin_content(emmbeddings, emmbeddings + emmbeddingsSize); - Status rc = dh.template WriteBinFile(embedding_file_path, bin_content); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to write embedding file: " << embedding_file_path << "."; - return -1; - } - MS_LOG(INFO) << "Saved file " << embedding_file_path; - - std::string file_path; - if (GetJsonFullFileName(pMDToDApi, &file_path) != 0) { - MS_LOG(ERROR) << "Failed to update " << columnName; - return -1; - } - - MS_LOG(INFO) << "Updating json file: " << file_path; - rc = dh.UpdateValue(file_path, std::string(column), embedding_file_path); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to update json: " << file_path << "."; - return -1; - } - return 0; -} - -extern "C" int MDToDApi_UpdateStringArray(MDToDApi *pMDToDApi, const char *column, MDToDBuff_t MDbuff) { - auto columnName = std::string(column); - std::string file_path; - if (GetJsonFullFileName(pMDToDApi, &file_path) != 0) { - MS_LOG(ERROR) << "Failed to update " << columnName; - return -1; - } - MS_LOG(INFO) << "Start Update string Array column: " << columnName << " in file " << file_path; - mindspore::dataset::DataHelper dh; - std::vector strVec; - if (MDbuff.DataSize > 0) { - const char *p = reinterpret_cast(MDbuff.Buff); - do { - strVec.push_back(std::string(p)); - p += strVec.back().size() + 1; - } while (p < reinterpret_cast(MDbuff.Buff) + MDbuff.DataSize); - } - Status rc = dh.UpdateArray(file_path, columnName, strVec); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to update json: " << file_path << "."; - return -1; - } - return 0; -} - -extern "C" int MDToDApi_UpdateFloatArray(MDToDApi *pMDToDApi, const char *column, MDToDBuff_t MDBuff) { - auto columnName = std::string(column); - std::string file_path; - if (GetJsonFullFileName(pMDToDApi, &file_path) != 0) { - MS_LOG(ERROR) << "Failed to updaet " << columnName; - return -1; - } - MS_LOG(INFO) << "Start Update float Array column: " << columnName << " in file " << file_path; - mindspore::dataset::DataHelper dh; - std::vector vec; - MDBuffToVector(MDBuff, &vec); - Status rc = dh.UpdateArray(file_path, columnName, vec); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to update json: " << file_path << "."; - return -1; - } - return 0; -} - -extern "C" int MDToDApi_UpdateIsForTrain(MDToDApi *pMDToDApi, int32_t isForTrain) { - int64_t file_id = pMDToDApi->_file_id; - MS_LOG(INFO) << "Start Update isForTRain for id: " << file_id << " To " << isForTrain; - - if (file_id < 0) { - return -1; - } - std::string converted = std::to_string(pMDToDApi->_file_id); - std::string file_path = pMDToDApi->_folder_path + "/" + converted + ".json"; - mindspore::dataset::DataHelper dh; - MS_LOG(INFO) << "Updating file: " << file_path; - Status rc = dh.UpdateValue(file_path, "_isForTrain", isForTrain, ""); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to update json: " << file_path << "."; - return -1; - } - return 0; -} - -extern "C" int MDToDApi_UpdateNoOfFaces(MDToDApi *pMDToDApi, int32_t noOfFaces) { - int64_t file_id = pMDToDApi->_file_id; - MS_LOG(INFO) << "Start Update noOfFaces for id: " << file_id << " To " << noOfFaces; - - if (file_id < 0) { - return -1; - } - std::string converted = std::to_string(pMDToDApi->_file_id); - std::string file_path = pMDToDApi->_folder_path + "/" + converted + ".json"; - mindspore::dataset::DataHelper dh; - MS_LOG(INFO) << "Updating file: " << file_path; - Status rc = dh.UpdateValue(file_path, "_noOfFaces", noOfFaces, ""); - if (rc.IsError()) { - MS_LOG(ERROR) << "Fail to update json: " << file_path << "."; - return -1; - } - return 0; -} -} // namespace mindspore diff --git a/mindspore-lite/minddata/wrapper/MDToDApi.h b/mindspore-lite/minddata/wrapper/MDToDApi.h deleted file mode 100644 index 4dcab8f9b..000000000 --- a/mindspore-lite/minddata/wrapper/MDToDApi.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_LITE_MINDDATA_WRAPPER_MDTODAPI_H_ -#define MINDSPORE_LITE_MINDDATA_WRAPPER_MDTODAPI_H_ - -#include -#include - -namespace mindspore { -class MDToDApi; - -typedef struct MDToDBuff { - void *Buff; - size_t DataSize; - size_t TensorSize[4]; - size_t MaxBuffSize; -} MDToDBuff_t; - -typedef struct MDToDConf { - const char *pFolderPath; - const char *pSchemFile; - const char *pStoragePath; - MDToDBuff_t columnsToReadBuff; - float MEAN[3]; - float STD[3]; - int ResizeSizeWH[2]; - int fixOrientation; - int CropSizeWH[2]; - int64_t fileid; // -1 All files, otherwise get a single specific file -} MDToDConf_t; - -typedef struct MDToDResult { - int64_t fileid; - int32_t isForTrain; - int32_t noOfFaces; - int32_t orientation; - MDToDBuff_t fileNameBuff; - MDToDBuff_t labelBuff; - MDToDBuff_t imageBuff; - MDToDBuff_t embeddingBuff; - MDToDBuff_t boundingBoxesBuff; - MDToDBuff_t confidencesBuff; - MDToDBuff_t landmarksBuff; - MDToDBuff_t faceFileNamesBuff; - MDToDBuff_t imageQualitiesBuff; - MDToDBuff_t faceEmbeddingsBuff; -} MDToDResult_t; -} // namespace mindspore - -using (*MDToDApi_pathTest_t)(const char *path) = int; -using (*MDToDApi_testAlbum_t)() = int; -using *(*MDToDApi_createPipeLine_t)(MDToDConf_t MDConf) = MDToDApi; -using (*MDToDApi_GetNext_t)(MDToDApi *pMDToDApi, MDToDResult_t *results) = int; -using (*MDToDApi_UpdateEmbeding_t)(MDToDApi *pMDToDApi, const char *column, float *emmbeddings, - size_t emmbeddingsSize) = int; -using (*MDToDApi_UpdateStringArray_t)(MDToDApi *pMDToDApi, const char *column, MDToDBuff_t MDbuff) = int; -using (*MDToDApi_UpdateFloatArray_t)(MDToDApi *pMDToDApi, const char *column, MDToDBuff_t MDbuff) = int; -using (*MDToDApi_UpdateIsForTrain_t)(MDToDApi *pMDToDApi, uint8_t isForTrain) = int; -using (*MDToDApi_UpdateNoOfFaces_t)(MDToDApi *pMDToDApi, int32_t noOfFaces) = int; -using (*MDToDApi_Stop_t)(MDToDApi *pMDToDApi) = int; -using (*MDToDApi_Destroy_t)(MDToDApi *pMDToDApi) = int; - -#endif diff --git a/mindspore-lite/minddata/wrapper/album_op_android.cc b/mindspore-lite/minddata/wrapper/album_op_android.cc deleted file mode 100644 index af5c02656..000000000 --- a/mindspore-lite/minddata/wrapper/album_op_android.cc +++ /dev/null @@ -1,518 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include "album_op_android.h" // NOLINT -#include -#include -#include "minddata/dataset/core/tensor_shape.h" -#include "minddata/dataset/kernels/image/lite_image_utils.h" -#include "minddata/dataset/kernels/image/exif_utils.h" - -namespace mindspore { -namespace dataset { - -AlbumOp::AlbumOp(const std::string &file_dir, bool do_decode, const std::string &schema_file, - const std::vector &column_names, const std::set &exts) - : folder_path_(file_dir), - decode_(do_decode), - extensions_(exts), - schema_file_(schema_file), - row_cnt_(0), - buf_cnt_(0), - current_cnt_(0), - dirname_offset_(0), - sampler_(false), - sampler_index_(0), - rotate_(true), - column_names_(column_names) { - PrescanEntry(); -} - -AlbumOp::AlbumOp(const std::string &file_dir, bool do_decode, const std::string &schema_file, - const std::vector &column_names, const std::set &exts, uint32_t index) - : folder_path_(file_dir), - decode_(do_decode), - extensions_(exts), - schema_file_(schema_file), - row_cnt_(0), - buf_cnt_(0), - current_cnt_(0), - dirname_offset_(0), - sampler_(true), - sampler_index_(index), - rotate_(true), - column_names_(column_names) { - PrescanEntry(); -} - -// Helper function for string comparison -// album sorts the files via numerical values, so this is not a simple string comparison -bool StrComp(const std::string &a, const std::string &b) { - // returns 1 if string "a" represent a numeric value less than string "b" - // the following will always return name, provided there is only one "." character in name - // "." character is guaranteed to exist since the extension is checked before this function call. - int64_t value_a = std::atoi(a.substr(1, a.find(".")).c_str()); - int64_t value_b = std::atoi(b.substr(1, b.find(".")).c_str()); - return value_a < value_b; -} - -// Single thread to go through the folder directory and gets all file names -// calculate numRows then return -Status AlbumOp::PrescanEntry() { - data_schema_ = std::make_unique(); - Path schema_file(schema_file_); - if (schema_file_ == "" || !schema_file.Exists()) { - RETURN_STATUS_UNEXPECTED("Invalid file, schema_file is invalid or not set: " + schema_file_); - } else { - MS_LOG(INFO) << "Schema file provided: " << schema_file_ << "."; - data_schema_->LoadSchemaFile(schema_file_, columns_to_load_); - } - - Path folder(folder_path_); - dirname_offset_ = folder_path_.length(); - std::shared_ptr dirItr = Path::DirIterator::OpenDirectory(&folder); - if (folder.Exists() == false || dirItr == nullptr) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open folder: " + folder_path_); - } - MS_LOG(INFO) << "Album folder Path found: " << folder_path_ << "."; - - while (dirItr->hasNext()) { - Path file = dirItr->next(); - if (extensions_.empty() || extensions_.find(file.Extension()) != extensions_.end()) { - (void)image_rows_.push_back(file.toString().substr(dirname_offset_)); - } else { - MS_LOG(WARNING) << "Album operator unsupported file found: " << file.toString() - << ", extension: " << file.Extension() << "."; - } - } - - std::sort(image_rows_.begin(), image_rows_.end(), StrComp); - - if (image_rows_.size() == 0) { - RETURN_STATUS_UNEXPECTED( - "Invalid data, no valid data matching the dataset API AlbumDataset. Please check file path or dataset API."); - } - - if (sampler_) { - if (sampler_index_ < 0 || sampler_index_ >= image_rows_.size()) { - RETURN_STATUS_UNEXPECTED("the sampler index was out of range"); - } - std::vector tmp; - tmp.emplace_back(image_rows_[sampler_index_]); - image_rows_.clear(); - image_rows_ = tmp; - } - - return Status::OK(); -} - -// contains the main logic of pulling a IOBlock from IOBlockQueue, load a buffer and push the buffer to out_connector_ -// IMPORTANT: 1 IOBlock produces 1 DataBuffer -bool AlbumOp::GetNextRow(std::unordered_map> *map_row) { - if (map_row == nullptr) { - MS_LOG(ERROR) << "GetNextRow in AlbumOp: the point of map_row is nullptr"; - return false; - } - - if (current_cnt_ == image_rows_.size()) { - return false; - } - - Status ret = LoadTensorRow(current_cnt_, image_rows_[current_cnt_], map_row); - if (ret.IsError()) { - MS_LOG(ERROR) << "GetNextRow in AlbumOp: " << ret.ToString() << "\n"; - return false; - } - current_cnt_++; - return true; -} - -// Only support JPEG/PNG/GIF/BMP -// Optimization: Could take in a tensor -// This function does not return status because we want to just skip bad input, not crash -bool AlbumOp::CheckImageType(const std::string &file_name, bool *valid) { - std::ifstream file_handle; - constexpr int read_num = 3; - *valid = false; - auto file_name_realpath = FileUtils::GetRealPath(file_name.c_str()); - if (!file_name_realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file_name << " does not exist."; - return false; - } - file_handle.open(file_name_realpath.value(), std::ios::binary | std::ios::in); - if (!file_handle.is_open()) { - return false; - } - unsigned char file_type[read_num]; - (void)file_handle.read(reinterpret_cast(file_type), read_num); - - if (file_handle.fail()) { - file_handle.close(); - return false; - } - file_handle.close(); - if (file_type[0] == 0xff && file_type[1] == 0xd8 && file_type[2] == 0xff) { - // Normal JPEGs start with \xff\xd8\xff\xe0 - // JPEG with EXIF stats with \xff\xd8\xff\xe1 - // Use \xff\xd8\xff to cover both. - *valid = true; - } - return true; -} - -bool AlbumOp::IsReadColumn(const std::string &column_name) { - for (uint32_t i = 0; i < this->column_names_.size(); i++) { - if (this->column_names_[i] == column_name) { - return true; - } - } - return false; -} - -Status AlbumOp::LoadImageTensor(const std::string &image_file_path, int32_t col_num, TensorPtr *tensor) { - TensorPtr image; - TensorPtr rotate_tensor; - std::ifstream fs; - auto image_realpath = FileUtils::GetRealPath(image_file_path.c_str()); - if (!image_realpath.has_value()) { - RETURN_STATUS_UNEXPECTED("Invalid file path, " + image_file_path + " does not exist."); - } - fs.open(image_realpath.value(), std::ios::binary | std::ios::in); - if (fs.fail()) { - MS_LOG(WARNING) << "File not found:" << image_file_path << "."; - // If file doesn't exist, we don't flag this as error in input check, simply push back empty tensor - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, tensor)); - return Status::OK(); - } - // Hack logic to replace png images with empty tensor - Path file(image_file_path); - std::set png_ext = {".png", ".PNG"}; - if (png_ext.find(file.Extension()) != png_ext.end()) { - // load empty tensor since image is not jpg - MS_LOG(INFO) << "load empty tensor since image is PNG" << image_file_path << "."; - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, tensor)); - return Status::OK(); - } - // treat bin files separately - std::set bin_ext = {".bin", ".BIN"}; - if (bin_ext.find(file.Extension()) != bin_ext.end()) { - // load empty tensor since image is not jpg - MS_LOG(INFO) << "Bin file found" << image_file_path << "."; - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_file_path, tensor)); - return Status::OK(); - } - - // check that the file is an image before decoding - bool valid = false; - bool check_success = CheckImageType(image_file_path, &valid); - if (!check_success || !valid) { - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, tensor)); - return Status::OK(); - } - // if it is a jpeg image, load and try to decode - RETURN_IF_NOT_OK(Tensor::CreateFromFile(image_file_path, &image)); - Status rc; - if (decode_ && valid) { - rc = Decode(image, tensor); - if (rc.IsError()) { - RETURN_IF_NOT_OK(LoadEmptyTensor(col_num, tensor)); - return Status::OK(); - } - } - return Status::OK(); -} - -// get orientation from EXIF file -int AlbumOp::GetOrientation(const std::string &file) { - auto file_realpath = FileUtils::GetRealPath(file.c_str()); - if (!file_realpath.has_value()) { - MS_LOG(ERROR) << "Invalid file path, " << file << " does not exist."; - return 0; - } - FILE *fp = fopen(file_realpath.value().c_str(), "rb"); - if (fp == nullptr) { - MS_LOG(ERROR) << "Can't read file for EXIF: file = " << file; - return 0; - } - fseek(fp, 0, SEEK_END); - int64_t fsize = ftell(fp); - rewind(fp); - if (fsize > INT_MAX) { - fclose(fp); - return 0; - } - unsigned char *buf = new unsigned char[fsize]; - if (fread(buf, 1, fsize, fp) != fsize) { - MS_LOG(ERROR) << "read file size error for EXIF: file = " << file; - delete[] buf; - fclose(fp); - return 0; - } - fclose(fp); - - // Parse EXIF - mindspore::dataset::ExifInfo result; - int code = result.parseOrientation(buf, fsize); - delete[] buf; - MS_LOG(INFO) << "AlbumOp::GetOrientation: orientation= " << code << "."; - return code; -} - -Status AlbumOp::LoadStringArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - std::vector data = json_obj.get>(); - - MS_LOG(INFO) << "String array label found: " << data << "."; - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, tensor)); - return Status::OK(); -} - -Status AlbumOp::LoadStringTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - std::string data = json_obj; - // now we iterate over the elements in json - - MS_LOG(INFO) << "String label found: " << data << "."; - TensorPtr label; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, tensor)); - return Status::OK(); -} - -Status AlbumOp::LoadIntArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - // consider templating this function to handle all ints - if (data_schema_->Column(col_num).Type() == DataType::DE_INT64) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_INT32) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, tensor)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid data, column type is neither int32 nor int64, it is " + - data_schema_->Column(col_num).Type().ToString()); - } - return Status::OK(); -} - -Status AlbumOp::LoadFloatArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - // consider templating this function to handle all ints - if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT64) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT32) { - std::vector data; - - // Iterate over the integer list and add those values to the output shape tensor - auto items = json_obj.items(); - using it_type = decltype(items.begin()); - (void)std::transform(items.begin(), items.end(), std::back_inserter(data), [](it_type j) { return j.value(); }); - - RETURN_IF_NOT_OK(Tensor::CreateFromVector(data, tensor)); - } else { - RETURN_STATUS_UNEXPECTED("Invalid data, column type is neither float32 nor float64, it is " + - data_schema_->Column(col_num).Type().ToString()); - } - return Status::OK(); -} - -Status AlbumOp::LoadIDTensor(const std::string &file, int32_t col_num, TensorPtr *tensor) { - if (data_schema_->Column(col_num).Type() == DataType::DE_STRING) { - RETURN_IF_NOT_OK(Tensor::CreateScalar(file, tensor)); - return Status::OK(); - } - // hack to get the file name without extension, the 1 is to get rid of the backslash character - int64_t image_id = std::atoi(file.substr(1, file.find(".")).c_str()); - RETURN_IF_NOT_OK(Tensor::CreateScalar(image_id, tensor)); - MS_LOG(INFO) << "File ID " << image_id << "."; - return Status::OK(); -} - -Status AlbumOp::LoadEmptyTensor(int32_t col_num, TensorPtr *tensor) { - // hack to get the file name without extension, the 1 is to get rid of the backslash character - RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({0}), data_schema_->Column(col_num).Type(), tensor)); - return Status::OK(); -} - -// Loads a tensor with float value, issue with float64, we don't have reverse look up to the type -// So we actually have to check what type we want to fill the tensor with. -// Float64 doesn't work with reinterpret cast here. Otherwise we limit the float in the schema to -// only be float32, seems like a weird limitation to impose -Status AlbumOp::LoadFloatTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT64) { - double data = json_obj; - MS_LOG(INFO) << "double found: " << json_obj << "."; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_FLOAT32) { - float data = json_obj; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, tensor)); - MS_LOG(INFO) << "float found: " << json_obj << "."; - } - return Status::OK(); -} - -// Loads a tensor with int value, we have to cast the value to type specified in the schema. -Status AlbumOp::LoadIntTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor) { - if (data_schema_->Column(col_num).Type() == DataType::DE_INT64) { - int64_t data = json_obj; - MS_LOG(INFO) << "int64 found: " << json_obj << "."; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, tensor)); - } else if (data_schema_->Column(col_num).Type() == DataType::DE_INT32) { - int32_t data = json_obj; - RETURN_IF_NOT_OK(Tensor::CreateScalar(data, tensor)); - MS_LOG(INFO) << "int32 found: " << json_obj << "."; - } - return Status::OK(); -} - -Status AlbumOp::LoadIntTensorRowByIndex(int index, bool is_array, const nlohmann::json &column_value, - std::unordered_map> *map_row) { - int i = index; - // int value - if (!is_array && - (data_schema_->Column(i).Type() == DataType::DE_INT64 || data_schema_->Column(i).Type() == DataType::DE_INT32)) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadIntTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - // int array - if (is_array && - (data_schema_->Column(i).Type() == DataType::DE_INT64 || data_schema_->Column(i).Type() == DataType::DE_INT32)) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadIntArrayTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - return Status::OK(); -} - -Status AlbumOp::LoadTensorRowByIndex(int index, const std::string &file, const nlohmann::json &js, - std::unordered_map> *map_row) { - int i = index; - // special case to handle - if (data_schema_->Column(i).name() == "id") { - // id is internal, special case to load from file - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadIDTensor(file, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - // find if key does not exist, insert placeholder nullptr if not found - if (js.find(data_schema_->Column(i).Name()) == js.end()) { - // iterator not found, push nullptr as placeholder - MS_LOG(INFO) << "Pushing empty tensor for column: " << data_schema_->Column(i).Name() << "."; - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadEmptyTensor(i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - nlohmann::json column_value = js.at(data_schema_->Column(i).Name()); - MS_LOG(INFO) << "This column is: " << data_schema_->Column(i).Name() << "."; - bool is_array = column_value.is_array(); - // load single string - if (column_value.is_string() && data_schema_->Column(i).Type() == DataType::DE_STRING) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadStringTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - // load string array - if (is_array && data_schema_->Column(i).Type() == DataType::DE_STRING) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadStringArrayTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - // load image file - if (column_value.is_string() && data_schema_->Column(i).Type() != DataType::DE_STRING) { - std::string image_file_path = column_value; - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadImageTensor(image_file_path, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - uint32_t orientation = GetOrientation(image_file_path); - TensorPtr scalar_tensor; - RETURN_IF_NOT_OK(Tensor::CreateScalar(orientation, &scalar_tensor)); - (*map_row)["orientation"] = scalar_tensor; - } - // load float value - if (!is_array && (data_schema_->Column(i).Type() == DataType::DE_FLOAT32 || - data_schema_->Column(i).Type() == DataType::DE_FLOAT64)) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadFloatTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - // load float array - if (is_array && (data_schema_->Column(i).Type() == DataType::DE_FLOAT32 || - data_schema_->Column(i).Type() == DataType::DE_FLOAT64)) { - TensorPtr tensor; - RETURN_IF_NOT_OK(LoadFloatArrayTensor(column_value, i, &tensor)); - (*map_row)[data_schema_->Column(i).Name()] = tensor; - } - - RETURN_IF_NOT_OK(LoadIntTensorRowByIndex(i, is_array, column_value, map_row)); - return Status::OK(); -} - -// Load 1 TensorRow (image,label) using 1 ImageColumns. 1 function call produces 1 TensorRow in a DataBuffer -// possible optimization: the helper functions of LoadTensorRow should be optimized -// to take a reference to a column descriptor? -// the design of this class is to make the code more readable, forgoing minor performance gain like -// getting rid of duplicated checks -Status AlbumOp::LoadTensorRow(row_id_type row_id, const std::string &file, - std::unordered_map> *map_row) { - // testing here is to just print out file path - MS_LOG(INFO) << "Image row file: " << file << "."; - - std::ifstream file_handle(folder_path_ + file); - if (!file_handle.is_open()) { - RETURN_STATUS_UNEXPECTED("Invalid file, failed to open json file: " + folder_path_ + file); - } - std::string line; - while (getline(file_handle, line)) { - try { - nlohmann::json js = nlohmann::json::parse(line); - MS_LOG(INFO) << "This Line: " << line << "."; - - // note if take a schema here, then we have to iterate over all column descriptors in schema and check for key - // get columns in schema: - int32_t columns = data_schema_->NumColumns(); - - // loop over each column descriptor, this can optimized by switch cases - for (int32_t i = 0; i < columns; i++) { - if (!IsReadColumn(data_schema_->Column(i).Name())) { - continue; - } - RETURN_IF_NOT_OK(LoadTensorRowByIndex(i, file, js, map_row)); - } - } catch (const std::exception &err) { - file_handle.close(); - RETURN_STATUS_UNEXPECTED("Invalid file, failed to parse json file: " + folder_path_ + file); - } - } - file_handle.close(); - return Status::OK(); -} -} // namespace dataset -} // namespace mindspore diff --git a/mindspore-lite/minddata/wrapper/album_op_android.h b/mindspore-lite/minddata/wrapper/album_op_android.h deleted file mode 100644 index 226ba66c9..000000000 --- a/mindspore-lite/minddata/wrapper/album_op_android.h +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#ifndef MINDSPORE_LITE_MINDDATA_WRAPPER_ALBUM_OP_ANDROID_H_ -#define MINDSPORE_LITE_MINDDATA_WRAPPER_ALBUM_OP_ANDROID_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "minddata/dataset/core/tensor.h" - -#include "minddata/dataset/engine/data_schema.h" -#include "minddata/dataset/util/path.h" -#include "minddata/dataset/util/status.h" - -namespace mindspore { -namespace dataset { -// Forward declares -template -class Queue; - -// Define row information as a list of file objects to read -using FolderImages = std::shared_ptr>>; - -/// \class AlbumOp -class AlbumOp { - public: - /// \brief Constructor - /// \param[in] file_dir - directory of Album - /// \param[in] do_decode - decode image files - /// \param[in] schema_file - schema file - /// \param[in] column_names - column name - /// \param[in] exts - set of file extensions to read, if empty, read everything under the dir - AlbumOp(const std::string &file_dir, bool do_decode, const std::string &schema_file, - const std::vector &column_names, const std::set &exts); - - /// \brief Constructor - /// \param[in] file_dir - directory of Album - /// \param[in] do_decode - decode image files - /// \param[in] schema_file - schema file - /// \param[in] column_names - column name - /// \param[in] exts - set of file extensions to read, if empty, read everything under the dir - /// \param[in] index - the specific file index - AlbumOp(const std::string &file_dir, bool do_decode, const std::string &schema_file, - const std::vector &column_names, const std::set &exts, uint32_t index); - - /// \brief Destructor. - ~AlbumOp() = default; - - /// \brief Initialize AlbumOp related var, calls the function to walk all files - /// \return - The error code returned - Status PrescanEntry(); - - /// \brief Initialize AlbumOp related var, calls the function to walk all files - /// \return - The error code returned - bool GetNextRow(std::unordered_map> *map_row); - - /// \brief Check if image ia valid.Only support JPEG/PNG/GIF/BMP - /// This function could be optimized to return the tensor to reduce open/closing files - /// \return bool - if file is bad then return false - bool CheckImageType(const std::string &file_name, bool *valid); - - /// \brief Name of the current Op - /// @return op name - std::string Name() const { return "AlbumOp"; } - - /// \brief disable rotate - // @return - void DisableRotate() { this->rotate_ = false; } - - private: - /// \brief Load image to tensor - /// \param[in] image_file Image name of file - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadImageTensor(const std::string &image_file, int32_t col_num, TensorPtr *tensor); - - /// \brief Load vector of ints to tensor, append tensor to tensor - /// \param[in] json_obj Json object containing multi-dimensional label - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadIntArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load vector of floatss to tensor, append tensor to tensor - /// \param[in] json_obj Json object containing array data - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadFloatArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load string array into a tensor, append tensor to tensor - /// \param[in] json_obj Json object containing string tensor - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadStringArrayTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load string into a tensor, append tensor to tensor - /// \param[in] json_obj Json object containing string tensor - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadStringTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load float value to tensor - /// \param[in] json_obj Json object containing float - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadFloatTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load int value to tensor - /// \param[in] json_obj Json object containing int - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadIntTensor(const nlohmann::json &json_obj, int32_t col_num, TensorPtr *tensor); - - /// \brief Load empty tensor to tensor - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadEmptyTensor(int32_t col_num, TensorPtr *tensor); - - /// \brief Load id from file name to tensor - /// \param[in] file The file name to get ID from - /// \param[in] col_num Column num in schema - /// \param[in,out] Tensor to push to - /// \return Status The error code returned - Status LoadIDTensor(const std::string &file, int32_t col_num, TensorPtr *tensor); - - /// \brief Load a tensor according to a json file - /// \param[in] row_id_type row_id - id for this tensor row - /// \param[in] ImageColumns file Json file location - /// \param[in,out] TensorRow Json content stored into a tensor row - /// \return Status The error code returned - Status LoadTensorRow(row_id_type row_id, const std::string &file, - std::unordered_map> *map_row); - - /// \brief get image exif orientation - /// \param[in] file file path - int GetOrientation(const std::string &file); - - /// \brief is read column name - /// \param[in] column_name - bool IsReadColumn(const std::string &column_name); - - Status LoadTensorRowByIndex(int index, const std::string &file, const nlohmann::json &js, - std::unordered_map> *map_row); - - Status LoadIntTensorRowByIndex(int index, bool is_array, const nlohmann::json &column_value, - std::unordered_map> *map_row); - - std::string folder_path_; // directory of image folder - bool decode_; - std::vector columns_to_load_; - std::set extensions_; // extensions allowed - std::unique_ptr data_schema_; - std::string schema_file_; - int64_t row_cnt_; - int64_t current_cnt_; - int64_t buf_cnt_; - int64_t dirname_offset_; - bool sampler_; - int64_t sampler_index_; - std::vector image_rows_; - bool rotate_; - std::vector column_names_; -}; -} // namespace dataset -} // namespace mindspore -#endif // MINDSPORE_LITE_MINDDATA_WRAPPER_ALBUM_OP_ANDROID_H_ diff --git a/mindspore-lite/minddata/wrapper/jni-example.cc b/mindspore-lite/minddata/wrapper/jni-example.cc deleted file mode 100644 index bb792f6c4..000000000 --- a/mindspore-lite/minddata/wrapper/jni-example.cc +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include -#include -#include -#include -#include -#include "minddata/dataset/include/dataset/datasets.h" -#include "minddata/dataset/util/path.h" -#if defined(__ANDROID__) || defined(ANDROID) -#include -#include -#endif - -extern "C" JNIEXPORT jstring JNICALL Java_com_example_mindsporepredict_MainActivity_stringFromJNI(JNIEnv *env, - jobject /* this */) { - std::string hello = "Hello World!"; - MS_LOG(DEBUG) << hello; - return env->NewStringUTF(hello.c_str()); -} - -using Dataset = mindspore::dataset::Dataset; -using Iterator = mindspore::dataset::Iterator; -using mindspore::dataset::Cifar10; -using mindspore::dataset::Path; -using mindspore::dataset::RandomSampler; -using mindspore::dataset::Tensor; - -extern "C" JNIEXPORT void JNICALL Java_com_example_mindsporepredict_MainActivity_pathTest(JNIEnv *env, - jobject /* this */, - jstring path) { - MS_LOG(WARNING) << env->GetStringUTFChars(path, 0); - Path f(env->GetStringUTFChars(path, 0)); - MS_LOG(WARNING) << f.Exists() << f.IsDirectory() << f.ParentPath(); - // Print out the first few items in the directory - auto dir_it = Path::DirIterator::OpenDirectory(&f); - MS_LOG(WARNING) << dir_it.get(); - int i = 0; - while (dir_it->hasNext()) { - Path v = dir_it->next(); - MS_LOG(WARNING) << v.toString(); - i++; - if (i > 5) break; - } -} - -extern "C" JNIEXPORT void JNICALL Java_com_example_mindsporepredict_MainActivity_TestCifar10Dataset(JNIEnv *env, - jobject /* this */, - jstring path) { - MS_LOG(INFO) << "Doing MindDataTestPipeline-TestCifar10Dataset."; - - // Create a Cifar10 Dataset - std::string folder_path = env->GetStringUTFChars(path, 0); - std::shared_ptr ds = Cifar10(folder_path, std::string(), RandomSampler(false, 10)); - - // Create an iterator over the result of the above dataset - // This will trigger the creation of the Execution Tree and launch it. - std::shared_ptr iter = ds->CreateIterator(); - - // Iterate the dataset and get each row - std::unordered_map> row; - iter->GetNextRow(&row); - - while (row.size() != 0) { - auto image = row["image"]; - MS_LOG(INFO) << "Tensor image shape: " << image->shape(); - iter->GetNextRow(&row); - } - - // Manually terminate the pipeline - iter->Stop(); -} diff --git a/mindspore-lite/minddata/wrapper/testCifar10Data/data_batch_1.bin b/mindspore-lite/minddata/wrapper/testCifar10Data/data_batch_1.bin deleted file mode 100644 index b3ec462f79967204c57db87b22ca5e632ca901a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30730000 zcmd44Wnh$9wl;dtnQo+!2->)7T|1Og<1Kp<$4Kmx%b#NFN9-QAOlSF}>GvUsrW zrn_f)X72gUeb#;}z|1-K$NhJAHG-PP5J($U^7Y!kG$ zw6wM~H#Ro#`Sth`C|5uuCunVLY5IbXz}+H|sJoLj@GVZ&Mcv(Eu?U@Xi#n+T4oCBK zot;!&+W&5_udBYXidWkr1c88SzP_%G_1^#K)6egu!lstWiuk1LvZj{iCK_8^U2QGl zAOHMyU~oVpY^|xUPYd&l$}Fj>uCA`ZpXw^W1OMlrzYX?^J32(dw(8>C!j$BcgzS>i zva<5>3SO5;^!dL(v{cl$w6zF2y2b4cl?5p=A@0f9x%mZ!#iiZdvOhjG)$&Sfn%X+L zy4(3(olU99nK4muDQTIY5%qLMR(4e8CC8`c)OJW@oz3my*4+HGP;>YA#KhE$UV+9T zuPAp18yh=k@0gPIK51u_P|%PQ;BDy_9UB*yY-*4Z>+c_ykd~2^ot2qeD;n;SHVG?g zcoBAiQBhGZZ6d>ClJa=<4Gj&Ajg9=;I?2aRUBaHG+RP|BpNPn3j)C#1MSOv zsL)gP^!4}mz8h#Y(IfUBc{DjH!hobQnOG#OyK$PNf*z4oMiZ!vC&6<{MD4X^D%K1q z&IiI3HW>Wc+15~0Ski$49{W=0aUdlf5Rsb|&N1Z`~$ZfWAz)mGzI{GGKzzoD7{umCdN3<0XGCa$4J z91;I7@-LTfYM2H^^YFjPKRq6Ya^g=9HX`37qg0(ea(9QIxv{0A>)+%b)oFs;+v+Pb z0>b@+Qh0(6)c+#?9ncRVv9LkF&y5WUcecK5U>8$E($BAF@{j7G{vJVDWo33uxSNlc z!DUT7i}1pVvhs?`DyRXp-uuftaZOovTw-Q)gsa(G>nD$%yb8(9%P%M@DXT#B_rEkn zy97kWMn)&3XT=0KzBRmk`MiBxN_tjKUSYRL+S3%`>*eYnnUIo}8t0o7A84Te?Czbn zK@m^^8Qncq#*vx+jwY`x9YfP{@)G=`(n6jaKhr&Q%R3-6I$oBuX_JYW{;eA~Z{K@l z=9gZU8)Fd^;PG1H{1JT*Z@(a`!|Jg<=B9Q&!J(lcLH>cUnH4cUd(<~I0 z7^$lfbFx;6f>0{mpzmmFtaVa5#aNsBR{qHzvJ33kP!X@XcZB^%NPt3)@NI{nDJO2< zM&f^}LG6%?Fjz=$v#lxb_RiPzcNj$yKzSj_W`fpOlb>HKf9>>x;UupFlYyTDs>?=q-YZf3{CKPZ3 z#J{GBQ;N`L&|h7hFz_V4_)pl{+)Pc7)+A-ey(sCZjKL*y_ znCt)g!IkspE}lMfc>9*ED;6!;ZwqUin_pB?-qkJcxEm)6(Vh7N|G{gvTYNie=&Ix2_%C zzH!s`J;%>#>uQ1Q*y>fwW-9Noa`6t1>~@{G{+`N??Yj>gK6+f`?8WOk=k}aBxMTH< zY03vI?43RC%smuht$+Wyt*g76i=(aC)9W`ib&eg~v313dlT@s1>=*4Zeq(AEm{wQ{ z;m^rPi4U-N_FPNt;7^4|7d9=MqOkap ziLF~?Vp?fqD=`K!7PziD)$I1!!~54x8?Si!SxIGGJ->xy9CHs@2T21z)B4W&n>*$! z|FGhY5XOTOaLjQhxV}v&XlhJ*b4&H&Pg4|>k9ISV$v;>K-k$*0rMWTw%_a3cGZhq8 z(Ckwo8x9){_XNz?+EVZT_PXZMNeVy0X<#_XKXV`3$$GW678>2ZyIo0P5^$~oY66tx zU!sSWRv-QQrzTBQ26NKaUkk>NQiXJYXNXeL^kAb&NL71Zen1#_=C1wq!m0nT0x8x*5q3YH!p_2v4Zo?piXH9|xh zs-1OD3V>hFZ>VQVi>Xg}|3Y-$#`FVJKtuh1RPsZ~!c(9OK65KkD zf>%*qR$N#EKOHqOR(Q93KDoIKd?+p&Mnzc(@Y*_7jo$zdT+mX_E9ccU@~PK~igJc? zaI?6-zo)ycp*%M^A*-qZEuemJL3ItQUN8Lc>HT1zxQ)-tPm2l)&8@FN2c;N)Jyoyo z{PpwC?}z&&ZT#|r^ym+R{4QcXCXjNc&q{g2;%`S@h? zCq{(_`TKghyM^RuXJ+7iwYL5Ix8HvM1>K8U8Y*(*B0~fGyggi9eA3cVlN%bE1?|86 z@!M}dzw7VmY_2cOjEf2h@bz+YbM=f%h>PVzH+25-$LC)@yz7&6w$v16#6<+7c{h~6 zu+UI`lK}W{zkGU!HB@^OFDE4`48uqFZZ4kwzCNhl*6}N<5A{nr+Z#(V6C=^Qrw6J# zx_WteVsvdC!@qvQ%awHr>r1l|FnS*k4|jKWM+awD_Xgly(oc9feKK@koE{qy=;!0< z0Sa3iTYE>sJH`DU-VgOigiSRCDbeA9zTO@lo*pjd7FO1F4J`seyI3+XJb=k>tSL^7 z4h``2@%HiZv@$lcu(WA}XC%ZDpdZZ(_*I3e(HNeuueXomtJkLH7S>o)wF`KhXwli`T2XidG(g+H(+|&+OaYei8>l-P#Inv+L&DqJ(0ShSNKTILS znoKmbN>xQsc1m0XSj^MI-PN_4FZ^GbVEX)=%#4({n5f9`FbEtZvX)8>749c3 zotYviC#i)U##);`KuCWe<3vDu0=1bdUXMauPyq#rH4zX_-w>P2<)4U%f+LVuA^C>| z!)!D|^|29=mf-Q&!0Ds0xh^Nr#`LwZb3#>16SYuX{SDq&o#6cP;dSk^Ck`Iaw9Ie8 z;-?091t*{UMqaSB*=r-ci|Qv2?%J|h)w7`i-Vc0g8Z>60GS=VS!sOB2i|WS@@7cU* z)9$ymq#-KG7~W8q9vSFuW%~4{#+l>$c5m9Se&dceP~T8-to_Ea?3A!jTjNJJFRC8c zwq@hGwd*(Ra;Ybmo#dZipP!o=>Fa3x=+^mDyEbpzuy*a*^_$gls-fTrZ>Y>GE{XND zGtt*U^V>HPzHakQ$I9ZueA1~6Wre(oM1Q;IcePcH@7uh79q_g5x7@)rr(;6+{Nhqx zO}e+)qg!XykL=pGe(f4+V3S6Ca$+Lk^_f{EWl6Tr^)H`RIf&-htXTt+%_sb$qoRm? z>XIX)Vm$1PZt9@=mUV=#!2k|fK@e(c>l^A~0$m(!UfjQ_rMiFH##O6Utp>h+%fVNU zj?SpwP#^2=Wc~WF-eqmo-CNeJTDfx7>a}Y(Y~N>OWo3*0>-m{3PWGk-cdlxm*t>P@ zPb*gZw0h0D&D&2req~}#j8|V7;Am;``2LMcC-!VwyBhUZt=X`3r}o3g&tEf+tk1Bw zG&Q_`^@7^&jcZo_g!b2M*>>pKod-tGsyUVoG=1~%=4B1F16%R*tJiGYwqw_st2gf% zKB*$pR9#cy^7zrUOJ`3V*uHtg`gL1&?%I7+OXtR21EZ=c9yqcfDe=_)Fc^VA}EKsE-os_%gxS6 zOHEBlj13JA3JeGcAWO~RrOcj#ATuL9Jv}WUDk2OT0{qJyc_y)te@@0>v`Kie=p-DT z0%R6!3|{1u6vIA~a8^`QRyNdAT#Av9AQ88NnoK+q@G9{Qq2!E=$Uk`kBLr1Y4rr1- zAo;CWnY7Rf6`lzk2Q*MgI4ezU)xr1GRZeO?3E{Oif$3^6bzkw8QsX_d6vmDjJ$lr{ zwfdQ@4RD65FmKRKRJ}Fj*sK}TlztdBdd%qWzaO*AyrHQc>Y-AuUs{rmtXw*4hQhc} z-;Wvd{r98CD&MZ<6JEjfU8K2f^V(T6l!1-<{`>E~A2mwhCe%-5WjPyOi^D}-HPw}K zr%oI*>bt+-zwf>uJ?SlRaY3q5tpylj~s2c$Mt*l;m1+ zt?Vp)BaKh4nm+OS(SJc*{Q1lGqkh;J86O)1@2kG@{sSl9r(5Sw8uQ)vfBDbVQrc!&|s^tr2O&K?i;Y9P@s4)|#>pXq-lAOOft5q8|ub!@?pg`@T z2jHWBP*|)B0b-72w%V2rYZuL$HGS+~{(|QJ1CKa%oZ?=cYj^a?+pMf{*}87s;)Qc& zD2-y>qx!fBvsE-N-O$4n!cdi6U%PJMteMjk$9{)5^!@i^Crnh@q^f>F_ZFtG0(_XG zzIfTJ8Iu)$7&G>VAI6RwH&JQP!INjSuHGarhwxNn=>9bS$Ek`FP{vPCP@cMQ*HKlH z@rrT?5ay|*(DLBInbRguQl30z`pkJh?LDq`PFwe;UU@krudJ*DvA(3&$JZ{LJ$K&X zwY!d=K70Pcap@XFNq>BD<>@7>qefB2}BBOuv_@aAM^ zrKKc<`MTO!nVGzOYi#_s6k%#~LG=qr{4>*2lM~~@1HIi`oE;q<9Z0PZL2+>rHIbi_ z1^G`&N{9-CbKvde34704C@sYh3yTVJL689rkQx^b4H4k~pMwJf{k=Usy?tEi?Ul(W zl1pJsEZHQI!NGxkK>Z`7uNTxHVHEfPJ|M@qU;04NO9Zs;k%*~&KVrIrtT0kg^vHV< zix|#<-%#`*l1wd#x~cu4p?@dn?d_LIX~~H82Zx4-K`=yxYXZFt4E9i5S&Z=w4$I5? zj~_n1XCwnCy$C(9@yXE-pQH^?={JFsJ)>cfI} zpN4=_`y3AP;ephfCscN?Ub|w^{8jfe``nu3ox$(VV$6=WEmt zqjeFjHrd$4&Nr`|IIw@yhNWv3&z?1F=8Uc3GPac>5K!;Kxt8}XsvO*RXwA~~Yv<0H zJA3xb)ft@>#cgV&;q?U@KhivUV*k!<%a+WaHDl(id2?rIK(CW?2mGDb#r*Y!!-o&7 zU$tcJtXVUDoHc9q?6s-fijMGJTO)(>J9lqixnkk0IkRTYoIQKa>@6wS{=g0iUh41% z*Xw!*RJX2Ny=LzGIdf*so;7>!k7tuJ3yMlBc=Y1mwLjNWKeXki#Y-2>pErNb>^XC0 zuCR$>9iEhxUszni@WJ8ow-?u}-?Dtc zg86gjE!}z9*d-t;DI=$#pn${s`$Z8~4sTe$dez1Q7oS)&2y|a=Z`IB^GzJ3mR+Sbt9+n(uBz$S4GW2o=3uem!BVfT@We#Voo{0XKw6K{5jOLoC?l12F~L zM?Jv^p%k8sa6zHX=XGCxrjxa0jadgoz1;B+p zvL47Il^z+r0@SAl@T43LX@)#Ap@-ZxF3;v53Yco^*bJhu(MB)K&1MLqi~;7TEK3$W`CqJHFG!-P_h~IjomG z@UD+=NH9V+z`-EH{c)G~9^ABO@thgc7Tidd4FJYGVj3|xz^&G&4QtG%$HtRBm2DVNo&FmzX}de|qDV^$X_Bm^_8rpE70gs^IuU+5;ioKV)~| z%J#!+7cO2jdFqs@Aek~%=}16WTvAGU2EF)!mIpdVcdTACd(O=1)24%D%B1;^+ybMp zWs=TjWU%!5*~811F91G$`m`yNrY_epbM_C9jZaGEremP^>FFgaR?nL;W5%@UbJwfh zePiqH9~u=Cmq3$+>FDbY*4njV`Qk-C?NGn_Jpy9{oJ7#}9-@4l3aPLA7Uwvcg6KjP z=pzRBm-dO{(FL_YYKS%h=+UW=SVH26r$NBgr(i#%exwjgBEcFU{XjU$1NtCo0?NYu zTx<9%1s2IzljdMJ(R`yJC@M;_37npe8vxp%JpZvLOrI!@iFx7-Mis_ zx${C)c)Si&;_%@QB9edE@UXlSrXPfIcnL1X)-pEm5jnbki?np6V$$2Jp(5BJ4t z@7lk9-h$cFl;`LshI|IJ0Np<~6Hl&z!8JsHik$io)^Y(vp(m;u7FP-Hw)THFoXTwsgTvNWY>2 zx=>yin3|fNo|#ScrRI+e)YorXJ8$-MG_HgmCMhp>3kr{lO-!cuGiKk&X( z_sqd%OXtt}aq3hk2c^l2)gPNUc=!c}NA?e31pR}>k5v{e`)Ll;!_+CWRvo$e@TG-= zo42ojFumXYzMj7BAdT(ImM&bdVw=kK`;VT#wQ_Lv@`aoO$FyToVgDn%-2-D|BLcj= zeEow$BcfyD6O$NUkb=NYl?>*)rMe_9D>EY_BQpynGYk9yuIa&?ljzXy6jp|aZI{s7 zgHO%$7l{Drdu&XS+(1Qe%TsvYq(A5ndf*t2q*sUlKW)Eb2}=+wU`!@bT^(#gp0$D? z)dXU*X3O@gT;^cxb9Nt0m7yST?^XD=#E)5DDy}z;ht` zPa73~p)XL_1|ED5axl083)R)lCFrmX9B2nto0J4A^w+OAVYJ)9{bVF$|5<_$>j5YR zKn0z!?#O_C!5PT4%Z5n;M6MDW8>>nZ3)KOALTzx4P)Ga728yJ+y|FYSI>5s#t^uBs z2#F`~usekEZefl)sxl%y%%9xWHH_+#BF%$y__$H^4D^WwLAN zee@x}BTOLueN4Z{An zwP9yVEFFILVMth(vEH*(~w9?*BgC9Qglf7-;KD}}N)QMB7 zC)IQuc_@{YJk*y;2c$#&yeQYVPYkY{IdS}y$_W*1v%G==H~>Y2i@@A1rJ?q(jP74L zeeBqY6UR?pGKTfc%FfAUxTv$KwkXiTNdM*q)gwoaoj9p>**HEqjl_s>u@Is6M7P)X zZ(LSCap2(5<0sBsH3^RbMS42xUr<$+9pU)$wvNW>WBU#qIdc50uBCrSL`*_*Dm}ln zqoN=!!1?txturde4(va4^u)Pac3vQej!&fbE0t7d$NSqo)zvtocH$5kIH93$@8%O2 z78#R3JSUL}vqHV?pWV24{_M#^dk-8_yZpk|*&BFt9I+c(s0nhmdVWj$oQ4`AbSE_* zSU9-*LjAPUcw}@e<3kXLOWcj0JvF+cbx!;C z8%K99zkpyg9}|n-sSD@^A;h^NKQ1gZJTf9OiqgB{6B3g!e#$Tslj>-0MCh!nAR7@l zyt<6k)YP1#|%P+KZi2uLH z=Lq^Q1mEJ+#)$mOTVq|*_pA`e7231tGmDjkvko?dB7d1jQ`n>)Jtp&i&(M)OeBZQQ7OhKC0v zEoE7;9&c`GU(t1H6SMAt)9i?4AAf#7Aa20kp~uVXkbfO7N;^UOkZ;1@eg6FGP-j&} zT&S~=&N;Pn7jI>X$xXu=i{xMS%dfxv(%V{=5*1+k=+Zg0Gg{i#&E$V!I#6BG|I5#R z{Mpk~5EtTS`QYN2(`srO53-xsMoA4|$*qiIF0M=p@^XA}{hW%b+9|b5)+F92fTjIEfB5{Vy(re($>#CZv!_&4PpVva z8JC!p4E`g$_x<~KJ%ZeDcL#F=ozo{zojiH!qESdVxHu-3aM|#nq^l(}(8=QEU2WB4 z$4{O*eesb`KyYYScm%_HB%Q653EnoZ3~y+iB>kYS{n*9bmnax6?i4l@#d?}OyLVml z#L;8NPM*E=)Yj3}(=RX>&kw^aY^lwS^LlNlr>lAD*wJGr&s}|C4gwF~0J29iX?sIO zc9h$z2YNRysz3pq(zyBTt)-o_o0lI_2QWZUeL;GZtFhtT+m}=h9XY9{eedZT3&MSo z;D;8Zf}HqJ_qPVO^{$YBYu$SE+{DVk#hq}}McqQ|N>vpkgj3`lTZkwD zLBS#9uM-IbxUH4szce>JDKQ}-K0ZD=DkdgYE(XXkfqW%i$(6TB2o{-y1Cksc4v?%%k4;q2L4Y}#ei1CvlT zDrsAOtgqD_mHnHS&6_cO!7BY03E^a-NxmhWWqC0^rdJQ_+_-G!6opCCm%r#j17C4| zS!GGsGp)UwS1*{Rq%d*XyiJi(w#;fH`ImI@%4;gT?w;Mbe#M-riW4VJp10^)BTZ*h zV-xYeptPiYAn?|SEvr|~o~$rIL2=T|J#g@8CMiV=fyw&bU;M8g*tl}pk4h89DNLR+ zZIyXd1xk4t-i542Sl9X9^Zf4BOXn(2_~8cy^004aXJ=!Xn}_;6WhEKS^`^(SE}8Ws zg}@b*6_ppB4TW^WK_Ts21YO5m|8hFBE=Mm)hIDn4<9^uV8h~-n-5)n z@c7A#*Kf@%?H!$1iX%y1XKMwosysc&%MIBH$bWEmcXRjj@}@NdScOuf+M1i{Yir7K zQ{!V}ktPrs8VUtUW(Wn*Kx9rq{%fi#O7ba+pPHH+2N!~55Nrrb#>qbgC9BGc^08Nd z4WQIC8X5@D1lj*)Bn}}giA*pg*W~A6qo3hl}Z>6DGN6lFN!U z5Eq(c8+R-Rkc5T&9P)I?kD(+Y#4c%Lk6#bD{!%~z!%9(9Qf3_f)j%`1pIjZ*0%iG8 z2#d9iK~RBLPRl4tv%=G0q_BZhYov5zbj=W{PT*}6WBC_c)ZW%miI z{rlnVw0^6rhHa7c_Wug}w-1AZlIC1bvnP-Abgr1zQG}?f5_n(#;O~F^+uwhAH!#pu zmF8#tRR4~)mVO>(Ae5I%Bt3nD!@vLe_kVsu_)=J$;A8XT-fhiu*W${c;tLC4zWWD< z{`ki~|L?zk7?d=YB!t*Jy?ayZ?4_U_a^Q3D;8^4T@%MlJ^RJ)!I{AeOer8W@UA>@j z**hU2DLExoBBdC^Z-4v8Uw`{37St8TxV+NS(bl@4>lpip*6rL*TWFFkj1gNq+dcwgVp@Vj^Y;#MT!1lSnexqR-t)(tZ|@()5H5Zdd* z3m6{i5jK>jhd3KQxONenYfmlhfd>SIV0DWS@bJ)3Z(TuVu+3{DL;ZUX?mxA#b%b{i z5D)~Tf*3e$B6a5Zo0!@-I=Z`9nA_MxeL=wm1(R`S2NMvVujf@)mt@3*FxwqM?o9+7 z925#*!Ay1tn~-9im!6Us57~^3g^wRc4j%ajP=w%or0`TjfFXG4$iGTWNlu~z6s%DG zqfh{zt*t^9PCj*I|z{hG{EQ6#Fs)gF@c#G$Ss2VfInbqtn0y)SObuI z&m=rI2N{}_m(0QrZQx0WcYPiC|BRv_AF@jE1QduR(t}y{Y%M}ZDDV;rF*e9IXYHXX zDajM-h$iR>+SYI2bNE*Z41*~eA|QN3_#ww+(P<=H3nMsZfl0XK1T+%3z6|Hre=GlZ zjacNvZD&KE_9@44ME+&6){2UpOkTUFgJNf_KIsn*m&gjuFCX2zd5`vU-`Z{=hx6-e z+4>4ID2jY~MgQvQGwLVQkEz*K30WF6R!bZ%=?<~=wDf%b_^I}`9a|PI)yNiNfao4L zO|3M;rXa!7+3eLz-3?3T&zQGVwW1X-h^r3`(Hay`o}A!_JmyO~7tfkDefDBYn(Dfm z8s;8K%RR$u(>+a04Rq8tEu4-R*onV$|etKrx+Idrz6(%bwuktO($$^olnd|C|&Z?5Ng+04|bj#|c zKPoFtR-ClVHaQXEol0nDXHs0Xpw#n@rpoq}^QTQwQdXL_;%yLH!7{un)+;A6*yh~P zBirXqo}{R#IBC|ZHy)nez5zjSfIB-yF>hYoyRW-p)#f!b6qP1SP?$1ji!uCT7&tO8 zUEKvnw{_L`EMKy8j>7nfa6zWcJ@Crh*2&G&3&s;%C2-KwRo}IK>1^fk3UE=BW-LDU z>@^77y}-LzNOcrHQa`$B@#48~pHQYR*nI2pOEVh>7Z0+IusI#^7k6%7vt;JfDN2e; zGnTH?cwqDzQtaeHmXC~=#q?qu~ z5LRHpL*+O+faulN!Z|O@Nl#`TK2$_Z4B;rWu?T*thvZe17v~{QCnY&KDJd}#mW-j4 znGcSqw0>lcl@!8vr@j0XCb=YXTs(*fC^@ejYXFdBWKjB;f9uBofAqT zM_ZEMS?Yol|Du8s`6hoDwDc;uEl_0c&|=~%4p)phB60$x{tzyge-eMlH?#*m5L&3m z(Gs{j)v({3fO2j>M?kSnqz2Kmpw@_hlOB~Is4f(?Vx;_fY{d{h0{6&TAphy*Z8(F% zd;^?!sj3{oC9)#3v%9veUVHeOOLZ4)JJrXllH*cw$PJZ~dpB)ZyLjHBJ#R~qW<&{* zBe=BN@4-EtD@S(iST$?q_kpyUvP;u@tKSHEiIM(Tb4~%o~STo*1?jddPHlg zc&vSCgR^~z#iNr)cdlG8RY_rj()2mTEl}iGs}U}eWSM&u`sf|nyK(tkWyOgTCQh7s zx&pj{2n}$oQ6p>;+q@sB9ooEb#uTNA;}jGWw#1g?Q%VS}GrGG1g37v_ADla~Zr0RE zN)yH_PE=gsmYJFi`@$UH4*%FvX@$|Lohu+9NMlh{n6%g=G8|#3XoM*sfdK(U%{fmF zZ(25UlH&LY;}sO9E_v*N<#9k@7fzscb_O`cdOO_SwtV4CrSao`7_Xo-bHzg>xMB?q z(}81J;m-`UwNEXcHG3+gd+d0HNwe4ANB*9rl{Hub&TqlPs~Sf(&z~_(aopH3W5z2? zoxS6Z!E?CCR*b*f+ib3B9^JBR29^jQ7_U5I;jx={L0}FX!YAx1exSN{{Q~6hOoVn( z{BgnB^SXKup1wA*AoC??YwwD?xNGzB`7@?Y#s=r?73)vx+_-P}?A03+VmR6`6NamA z+q!1?@|8cW+PHi7iOV{fiYbz19 zB@MxHeG=mEc4#LbBY?jNk%LWP@?TtlWWV%OxqblJ3YMm9K3IQ>4DrgD2a!RK&rot# zna@scC$s-$P$4o3Db z01gId>lBFxki?H825pVJ?95Pa@2F~MKU6O(0pqlHiTi&4{qu+6-md0GB;kj9dAJ7@ zGxbqe0M72{mVEx}=bt|eO52<3%aM)jh5UNg@4tpm#FX0Kak_!+up{nD#%C- z_C^A_y>BKGLDSPAJQCT*KY#l)JS1vq=9OlrM)`TTIoaDfM#sg*C%{0pzwiI}x6khf zdOKU{DvGlbL%iHwogJ-ggTlfhA|nOD);`(0zy2~LlXf&z@p4mQ13Wz3T%7Ej;XQ$Xp8w@b>k3`@+n|(aqaGz#n)gTr`QSgI|`HnH(1p9vb9h z`PKpfI>hh%{4tr-LAOLKs4C7*O-P6i_jRy!aCCMh1JCd__~1B*CV^dnd!HHu`S$hk zf$^t>05KQNNTblgwXwdkI5z`Hcrj5C5#ixsVc{*reBchO^nS7~3{jA(R&8B6}M&Z~$6bV04)iLrZwFdr$!tZ~(A~z!47M90B>&$c}{p zuO-pL;yo8GLPmO1GfwPt3Z#|Rv``_0!^=PbFe@_yS_^r#t(?yd2Y?o(a1bhJ0fCIc zYzQ1zn&BW2i~*2j5ebuDIgB48!2{@r5$WggVX@X8ek!y!amKP>zJ>8Aq# z90719VjKzA1t}1UevyVDctrj&RBn(Y-E5F@oMfBH|HvRA{~)0Efc81{z-U+tc!``J zMDmaJArD`*_W4^vnhc>hiOr16&=geG6#= zn5ie=AVAkb*pITDgfK5BM9FVES2g`BPAa9oC^y>A)y&ZFs^*0k#gu_esi)LF0-qg% zwwj{!NKY>-qkFm<8XEekWkm?_g{In`2H1*bDF2MytCOFl<@Wr ziKK(i%MK56v3>DCPg~>c*|RG83D}`ZNTBiY8l;l;>XH~g4^v})OId4;#}LhWGXKZr!}5ed)&i$1mPm;1Ch;Cd_DaQ&qB;y~*>(j|~m(-PXHr z@Z>qX3u_y@5gc<~Q<9S$9pveN^jj0e_^oX09GqN8d{_fm?a`sG>?E@P$gV{}1k{fX zUp9h(TvBN4^3sCr6dVJJgmMTC!2*HHkS8LTEn2ykmlPm$m&l@kQBjd#MVMt)SVRbM zxw4XioXm96AkZJgs~k#_3Ev&LNo5qeV;(|MBH^r%#lfLL#x~q>05p=83Je+bkZWcX z5mhD+1P4kHJ)$5)1`7@{4MdJR^Zh{$CxIjo^2g$nEW*f^{#eG4n=xWuD|u9vEx=e> z0>uVEfCfh7mrydwWW0%vt*a?W!Q_L-V+NOuDDgR^PSAUzgD)h%=)b)k8lFigBOxb+ zhKB+X2LbFS2Q&GHmSHmi`GD-K(`3^W)rslA^~i>KQzj@+7>7d=qelOLJ%pooAHOoU zvcr70v_#xEqrQ6b^hp!2xIo_%lxHm4t8q>Lsfm?69HoW^L9PDol|L?+qBL&e#0iSi zXU$!+Petp-1MCr4<1`4dcgfRT3+K+8`QywPv*#^bw)K#zmM#VH-dH%+PB5oj{ee=?^r~o%R(^pRn9~#i{oi}gIEv=hive5$P z&Xtwqr^kl)dAYkdJ2^Q!ySTY~G_nS03<&5z{7dpOlH+hpFeErAC@3&6uo0W`DAWWv zhEC+b%SlVZ=?q#TM1;dW6HWzOU4yI*TK+Q+g3*xd%h7sB2D51}-q-@j%1BL0h{yXw zfk@z}7=1DgQ2{Lh{-6~CSe5h#dr24qu^sxrA6k7PQaOsP+$jP`J`5EK>Cl!U#Fe(V z<@Nz3?V8XId>ae>(IWmE0wSS+C=>#4a^Da^rJw&o zMH_e=jwr62f>F_q1a(6v%Beo>98zI{SVqASPzH_w@m&@JMy?=Bf@XURUvNPyp$H)l zBG12H+}TuBQdTF#rWH}3KH)MX!G1*eyV}L=aB# zy1TiC6_@sZ`s+Xb{>%G!gEAR z4J!%@ax&sVe6dO80zWu3ZQ$2`{^M_--wjC1Dx2$Cn=4BTGE<^My-{EBwbAnE&R%ow79rnHxD-lTU$FjM`w56p~1dC|Mts!T+-CcudOI4 z&4~-~q6LJ#9oFB-(-|6k_W=i(Wuo?``l`~3g3Q?PAX*_fIXXFFi4cG@3Lk#`G=%OO zsw>NiveV+Dkp$@BfjbqP+`Rn)aYSJl$2!EV4cHbdE6mDFj*SWr4)phR$NqmnQ0M>= z3=j38e{754)Ok*3N^(MUSa2{l04RqM1UM#ua{|2x)d-s#YAX@rqs3Dy;&Z8~Yy$*) zAOq}_0j{_p3X0F==b_~0;Z`MNiDQ?fml8*5FA%E$1b?6^*s&c%mnloX4_gdu_Y0WY?NaJ0YRt@iKVoF#qJrD23rAB1d8oZ z5KcLDWQ}12Nn4N;OSv#i=-ECeNiA(C$lU_avba5wj1(dvm{!c-acY!BxxWyC51~F- z8&sbNXcy!g`KSI6Erj|I2$;Om2g*0{4}C*B0RpBUSZI(VAu1-E+_tJfAb&b0{_KM& zn0&L%O0>@ufUHLYphizOJ|g6$#H z6;x!WCC7&O+E~1J{^FHKUb}={ctP@y=@U2NaCu^4RIrb$v-z86hIj89M^w^1DR{>O z3u-EIQWD~$aJiVX)thJfx>qjiJa9}Xqoo+~U+D08W@|I!`oN2wJ%+| zqVG~vUMiE}NgKF zynJY=dr@2C{Q2`(C%B}_IvRSr>&pw%1O0;BJ>0F0Up_Ist#jf0xwB`_oKZK%4qKP3 zyRj%c$;8^*#n0Q$;?0u>H?L}`tDQkA(plpGQjfi@`B^ERzBs(^X=7__c>5aG0II5L zYG*E3c*w-1ed6ZwoP-chtfyVD1^3`4dRJFdRXeSI-PBel%@cRGlxN3AxI6ne+n7DR zcTe}criS{N)2Gj9+<#>$l{L0^@Jo_oLcH9ZEKE$F-o37^rGe3@tDnDS@X881fI?hN zfQu76ob61lUO&2hUH6ifmZpZ5mhOY+W;U4f9${r(VxWhMwYAxsr-pZL-MoI~(&fun zZasW%Vr@_JD{U=`^L4Ygvam3I{>D_x^^vcxQ-ic$6?)u!=0E9iPEzRD% zeEtI01DIReIl1DRCgL9m7p?$kspFO8XQw5_Mj^-$f=ina|A>kqwuD@QrC|;*df)UOG2IH|6GLLQp z_kmPs8Zg?$usS;-HzYuWv0tPTrcfA+k1iaY;`Z(78we_Je3B@$oyAT{$3u=L*CIU( z=t!Qqn^(H9G(}%Bc0%hL`A1q3lqEDtJ9R7T>m%hUFHHWS>mbJ1v>@TdDFh4=l7>`n zqC*$x1VW6hCx+oj&;Tyr;FXj$;?NOyZ4ATP+Hhz<1`97pw|#W|tm@ID8U_JXk{86NmTh-Me$$>eZ`$ z+Hl&g2w?&y|2=)VFRy5yIdwwil**C4dp555Y3ZVc3zqJ_@0&*k4$XescS=wuZ_X_2c_?u3x`;#iE5s$6v5?+42j? zS+Yu(@PLMbLN0#;f(-kSy*12%>zB1ZF`O%I(2N%j?FZH`Lk!vnlpdiq8skg`jC_? zPlG$xjvP}}Q#rAJ_r`TAm&~6%2l%`NKRvXO2<#G~++W?*Ry%S+P4(!0RA0GxK2G}4 z2=*Gh6ifVr{T!d()>1!s=;VQ2J2$Ogw_?#Eob{VGfBuT|_nwMnMLrHM@9SuuK7Rb* zj&0l4uUWl(>EeY87cN?|{D98AXJT0U#j`4h5AEB&XX}QaR<2yO1Ur2z*X+~Q zd;F4^B2@q8#ghm3;S}T6Et}V^U59%FR;}G~?9y$+XK%nqlJ3~Y7f&4AyJyet?K^gD z-Mo3rrcFDKXomxOGxz%zCc zSoAI{6D32=)Z`do2}>>(!u%xvSfLjc6ckYNROnhW#4@Q5YK4n#BNhNaUYJ>Cc|eFr zAVfuL*qUmlJs1Uf_Kar{CBrSBq>G>?MzhxCtuV_C8d|ktsSRC3Tl^%u-x2_)#W>Sd zECm39J3>KfjNI}Oys?4mQ;|0zC-~aH2rd^}Y3zP>{Md z7^`j_#Biucj(`xRD@j2O#=LR&??#IJ7Z{DO7RaE8`qEzDng)2LVmffhjgt{De+QW@ zwg%xrf$cd-HSHog)G+dGRGby$ z>>1GW&;RpToFAJ|R9TA)KZV$Az?p-AcOU!f)5DxxoP1?}`|sbo+nclF6AR1gn_3aC zm-WG^7S$KV*jwA#dG!we*Pk++RN(UpbIKaan}l7`pOLJn;7)`;kA?SyNb2nV*puo0j10 z5a?%T?TRe`JT2uQ4fcbNt zXXcKk1Snd7(+UTl+dQHi+oDd!4papC^)&P)dwt+#S0TkIWgkvj$5mZ%@S^x<_Ot1vFkq9_~ z35W+^FHqhx)g~G?zAw_pz_Dtv-eg0*^6Xvva3!1 zs+s-2%Rjj_D3}dd@NsJg2T5z4gheJs2Fk6-DC_INQ4dFlwkW4~o={5R66BII4M^)P zvA8|SSjXJav8lBy*WV_rieFdX*bEhbgjY&h?`|uwH_^Li;UIqhzB9w*j*(AtekrfE z0ozG9c7?SK&;R+Yv$`*B#qWOZLcrnK-A7JA>G@@qb-0D8xxOyl!&FZNCjl(Pl6SK7 zfW%ZEd(#(ofp$Uhsd?2!nPJwZ#=0lfFF!KF`c;}|<(iR|81C=x8ITYW8y)CnYVpS4 zmZqlG4I^`rq^Gs9Ff%W=)YmQ4#@Q~w!}_(`D`N!mFJHZS@3l1?H3`2qx+pfp+$q%6 z#LCwG&aDRq`j<3xuIt`;X^9+N^w8Z>9Pa!&!r{4z!&}@{bpPHNwQIL->c6tEbzn~` z5x15l$H)1-aC9`s5LTI$Tl`XarqLZ=u$~bqxVf&dML0d zu&cCnOSyH30OX>O>~Vz*8zc{QlLZUe^1is3TX#eDXkE=jmk7vzXD5-%FrnZTu5h|x zWC_6Sa1p|m;v~6DG&vl?&9>nP#P|4*P!e5vecEIL1vrVG4zGxK7{7gMeZSg&(0~B% ziJtP`+8NzoyO#lmv`tNn)}`PO{XIN%?UShB*|=KpW@zj?&|Gx_}DAjs{1cX~c2 z|2RMJwGUc~aq?ey-n@YNmmqj2FVqB1V_-X=t-X4O@>4paBIa%!APUw4rApDJK%?ry zI~J_|7Xy#?N6sG8ksT#FPCYip-ZYGZ+&!QsNQ5ag*(P>d`t@X=ETc5d0U|IBeM9o-xEuBh)@ zyK3d!>GQT;d(o=@kJa^XIB}Z>O?h0xbZ!hfMw(j7j6{|M22d2^@FUbgix)3muyDoZz3RI6pS(1&0!nH8T`d_-cTVluyn4x^r7PC&IRO{{iLs@Xd~ z(;vLH#-cb6vu96V;`Tplq%UIsBQzq4PQj7%V@lzlw=`7a3||_O_oyUenUA_0K?gDb zNV*#+#h13<*%Cho%0XUO><*R_Y|pYg=$KAX37u;r8nV8SENBP_D7V3c4XBC=c2E|| zfeN$WoafD=ceKSsOi5zJmNLa0Ae_zHtlj8C{oG$2L~*lKqSf4qM;(A zM@aR_qweh!^>x;JUA*K@bvc}(`~*`FtZ%3|F(t+>EIFotxpZ_Kf~E{Oe0ADGs5Y~< zcxdP5=Yyym+MtsRP!v?1?pPY;`CA9XM1;6F*m&@9JQ|)F*rlae-uXbUQ|>@RNluEV zy_sn=uDQk9hTSm6>Lba$z@lVG$}frcwtn^0p%j;!(pFAYHF7j@3ZQR5;?2VbL0x9B zy~(3T#z_T*CG7I2@^Y^JP=j;t$Nu($I4|=j`UXZm8CmQKkdl%jxTSbCL+vpi-}N-+ z209r(e(>O-O?+xrZb3moVPPSwKY%#>`@ZhlWIr2|$9MJgO(PO;M{<5)K|v9ozlWme z?>_Xkm868bTfQ;4efLdpOkz3+&;njhAB(d8{83a{kQ?b@{q(^Dvw%o6keQvApN}(4 zz3|@$2S0pzFXNYH`8&LR{LsudJRv0mr;u}iV|WO#4-5?t4fi(Y#`@x{vbAp{{)vUm zOgshQbPoiEh(!Os4qmdqqrFc=0xl@QBS63?!cI5^@v+Y$X|E|Q%4e6r<1U~I9!20W z0y)6|*auey!9T|8nH6vef=qOi4ABgr0R(@^QnPd$J|DcoI1jf8pwQhj;6J&aPl5<$ zA_G8c2nOTt2-LI9G3bJ6gl?g2UW*)+brVnKXKw}8UQ8Z zQhXf|Zh67CBp3ukVX$Zdxf{(S-&EvY3jH8W1Ifk@{NX_MNyd$lFu(t1b!apY}lH z@=tTuQ)Ojtt~OJ7`l8no>K~~Dkm44ub*Z$!+~?uZJxk}|7LLVf-IV4`OEB;c>QePy zJJ;yT>y|81R#2FH6W%;a7s1WTR3C7Efu^>N#+tcPm5?I4DK@VN_tNF#8ZI0HB)ms# zZDo0L>l|ey@J^UG&oU+sNAw{9+#!C6q?iBPDZ*ftB2MZ3Fm8g9c2EfR6KIK>F2)K4 zOW-DNCtcMAipbC%`@@8Zd!4;Ozz(sq_CeB}pt=S44`auU9XDZvm5VQ3!V@2#0Gx$} zW$mxm&HGV#JZ{+=H({NLgJ(b}5di1jpjh1UYWeIrisQ$R8$WUP8(UlfLJl?#=@X7N zaLOg>$efvqbPM0%m)356Az?U#Plx!40FD#%ZH2+wDT+!H71dwac?E?cK`AzwaFRLT zSbYmJ9&evBeaBN9PpthTViMD`fXg|M9oiJOmPY#1=_Xtt#BK^>%Uik7fUn6%$5td< z_>z*6Qrw_N)*t_ql_MKU{w;Dc=(sMO!mgr!XNDFC9Mclx64TH^fbIX()v?t9)gGas zCa@Pk3jxXtqAFC-3ik^!HrD{~rY7#oub;%1j<0dQs%S%3C1(-{qf3*eZgnPgD1= zg*8DQGHd@U#uQmEDM;!AJqU5x(YN*=^52=zCnq2rX9mB@Kb9|Wt}bhl$Km=*ze55PJ1UgE$1 z(1`^Taf75`H`eNO8GaMlccxuL;{L#++ZQjNGkcP<;=CmMOD9MYqAQncN&e|d67c~I z_q_|}&7Y#AG+DPAf)6i}Y&feg68DBo*=lhTPWL25C8hNtso6*oO;69tr9-}`FX`02 z_UQVyxsw$qDkvz*M0_TC*c(D#EA-%&Nv`;7955h?l_Wv zsYqIDYJU0TA{?-pFn*$<;`>=GYOT;hXE05D&S2by23Tef7nGLBFvZPR~lj`Th+ zfBzuhsLx7!?5V}`aK=GVdH3C?W)80I$S@2BjQL_r)7i~4+B9qGG-c(Z_nuojyWu8| zkSN00q6*@P6?E3Ky^CgVyZ6${(be4>|56L|!xsXQymlckCNLs2CslLeYe@OCz%Zh<51=oqH+9G7vsB+z1%I$9jy(_Uf#KO{;8*v zbxb9$;H^c3LQt6Ka{Y#(eTa+I6TNGX?_Jk8;}z{-@-nZesJN_L(p8@o<>mDJO-7LY zQ+3so`&Eu_-+0g4;k9vg0UYo`v8XC3*sH+PD%#=AEwxM6G&b%$qk7=pbG=8g=~;RC zd18q$GbB?GYj6KbPxGQ}kj0I?YN~qp6}4{?Q4^>105g6FdtuJT0CbICSsX9Yd|v8#b<6uKwit-4_usq7dJbaC=W* z8>1^*fi&ww!C!ptxa z3+r&NN9V8K)l*kLapb@r)f<=3+_Hp|5*`iq5LTqR2Lzelz47pY-tD`$E?>~m)jD_n zk&T;QXe8m1&K%zWf6Hf2p5R{NH^#=sCgv|5Ja_a82#z2eZ>X)bB;L`^&)vbniS7Wv z*+>c>&?Q26KIX8uH}lH!lH+1xxZC$}K8v|eiOD2&Y_*831!VT+L9x@}0PG)9d@(%( zI9Qa{i%71eBZS=H-`t#>oLrPV!a19ZB?(0iuzg4=z(s{zf!snfKo%)IfMSa*6PeP0 z;o)GLk<XVjWh5eOq$j?{bgnpxN21wwH6|n}|{sm`a zW#5PzuC+03C9h5h$thkY(}R79hEfja>X4ttP|6t)_jP3*Ns%xd)$l*Kn2YjN#;1y8 zy{`8Ro^pPOK;EvjlV8;;>+PogZZ7?ys zmt0*}T}}VM3FN;e#mq?aikF|Qm7B|bTZ`M;$6xr_y$vp8_v|7suq44&^TK_{D0j=} zSI!{E<-&=;6x(Ny;!{&`5w5tqIWN-P;iYk5xZ{&E2X-G;JGpbUZh*CcQB-_FLQ=X| zRFfL&S?p(>;%s*R{CQoi&AZQ@-lk`yd&eh?++4_ic0^7`iksVO{Y%H5_?YVMQ&-=# zTlL~~C*PoO+;SzBG-GK~8sq-@{LWpr*3a~JY}>nKjpq65cCheak$CBy*`d{C5w5Ry z@3{5kw*I+Q8#n$m>-00DTlZc4f`}gN6v#7pIQyIkaczwX8@xlgrnTg@^awRTggSQX1##TNUPFa{Bn; z!@JI1cEB6D_RPi2`~P9@y`!T#(*92eY%&T6fr%m~lXFfcgCrzCU<3k0&N+h+3ZRTo z&N-l*a}KTCimj~fRu;k7#^bSPW`FOy-=}U1Gw=NN?Add6&)Gk!W)N6U-MX!-TUAdz z>4WEApB`$Pm*Zz+8s%a2=z!Mh<*SeC8{B_-@BVWX0>I@JF<#aok)C!Z9yvTcybBbE zmB&sT(LRO{W6PJ2H>3r-SlRi#x~6mF=%KCKw{G3MZnuHnuERGUnp)bs;Qn=@G`HZO zCuj7}7#uouTOdgp^P{aL%PTP0?DDy@XU|=@bonx>cV9BR@W_l} zgh<}fD#^=_dH&>;)f1EkF+~Fd#F8K<&|n0;9_0rMbCbgnSm@{N?t=Otj+9~T4INCF zo_G~&D~qyIQT!(^DhzlI7Jv8}zdv@u#>_;1KeEO!`4z$ch%R6r2xfs~?>dE?!6_(0 z{t(iGP~i}Nl9HHQnEx!a9XWy`7NCdN2kq?C zr45PU!u-c~s0THK?ck(H-EbOf%pz?Ic5a0j-^LCO>j@q>EFe;`zOet(kaQ`1n`StA z(Z74budqVcLI@z{m@xlkt+iy#?`n?U6e}0>3)=qs=f6zqwC_?&XP1@X#XDFtX#u14 z1Y&t}g{Y>Zr_yxA$e{XX4^jP;o*CvpNpa*CiyvE6*C+Vjn>lLv)XP0M1JLN$;^{-T zRn&-lQ*P+kgc#14G*EfclD((J2nIzOb9FT$GwV$@ud#f1ea@g!=GSM8QdSzbWyUb8 zB7}2`u)()U+avaBsLb7cWb*JSD^||enFkZWTCGLPo~ERvV+D{(b5GA!AE-Wh*`7Jy zFIAtuNO{0FqYN&O8#>!JEG#-PMK1B$q4w>p5z5Bvlm`w}RsRBQ=OZf>o30?KU00ckkM+xm4|#i)y&D0LR8xGH+*B5r!#T<{p_deBf7KeT_;$gTKFxGFq;lD1j8V zdZgW>`9r=OIBDkC0Rz85A^&fOeK%$H+%3kipksY`Fn93U4P(FfLg~Wy1HSwcHTnk* zRxwyRQvKU)R@Sd@wy~Kq=<8wQzZ<^ZY3{d>fA!7RgD2hCJ$(4!Ic6`cL55AaFypKB zHmW-9XjcENW4?ZNPVEb|9TNr&7&`RZf!`<(A3A>O zD*c-epP9GHDjrW5@a3k(1HbrEW8%;u>O;T!a)26&A+0z@F@VooWhJ|Yef5RL+L2!m zK>2}D8e^0O4OAMf^v&#}kUs-xv@Ofx;E;W1#(eS3(b3L$Sk;e z?dsL*H*ej4WNh*rD+Fy{EzNDs6^ThPJ}!<<&JH#fW+tXUyuCycBC4EGqar}Hl_Er6 z$3!Fi7&%7ns50p1k04})$B5>C73CKKBZL4WDnf$gKPoDUon~>OhEbt^{v)$5DG@6P zYwWz-KmTd_N9GJHpv*l%tHc+i+LMyN_HA}VqSv02fH?`6lYlu1m`J`X$bp?R=%^s5 z1u!4506`{a>j#!G?4=5=o7{|_@U`_(&Bw$Vg<|JTF0_G&(uf85Ys9{b5<|JTF z0_G%OP6DP9J)8tg#6n8aLqHL{enP4qa@jZu_!H_EmA>9^60jO40do>C0tP6251xKb z0#+n<^>7j}CjnD@J|_WFR2~{-!V!dmHHywd7#{tb;_Vb2C@Q#LPBMz86aHphD4kCs zXBW~S)Zpo%Xh6C{|MhHG#lX}94k3~wqJZ6zqWI_=xQ>&6ISH7PfH?`6))qwZa}qEX zMot3eBw&Q*L7_*GuTcJzlYmJ%!9$^p1Ec_O5-=wLa}qGpe_4efBm#00FqLsZDP%fS zAPb0-fH?`6lYpBj1e59qa1t;l0do>CCjoO3uwe1zBw$Vg<|JUDa4!MNi1b;6GUjN= z_xsdX2Z3lt9sWa36-qe?n3I4x37C_B`xgjAAz=LwNT5iJqzc9iiP%pPB{9mTvEpW| zav>oSXbB>cD3%x|CozhvA@#U_1Wp3xBw$Vg<|JTF0tV`rlYlu1n6dvj2^hwH6e?q( zOmGoW#23n!aS|{m0do>C75%4%a>3M41&|f|!Cav-VShF1LSj~>o0EWvF2G5^oCJ)* zx10owFkMap<|JTF0_G%O!VI7gASVI;PZFpi{{iU|oCM5Cz?=lkLUa+O%MyQ(@I$l~ zs=!YR2>Ap4k_+c{R*94n7^%nq4GEl+fH?^mCs&qn#!0}G_scSWXCCjpb=jCleCCjfZ@6iNJ_){lOF0XYeT zUX)_00t5{3i*VN(Z0`PKc(@p?OCoei*}w zlYrsG6xJR_BjY6Ck(>m~Nx+;0%t^qU1k6dmoCM5Cz?=lkNx;P6Wm+;P0do>CCjoO3 zuwXnOP$3+J;tJUD7zLCHdH(N6ppcV@HF6vX1Z}wI6(msMa##k*$4~B9#7dI?p5&A- zEL<+{e%ssCQc+bbDyt>)g+#a<$$sqa-5);u{8rvjU0YNb5uK7>jqoD+Y)AzxBl%B% z{@T;qBWtKDt*D3(@CZrDEiNf3DaD_XVu*YG>tDb1c1oL?+8XLha?-M5V`3vya`W=@ z3knKFEp2Td|NDJyVMT3yt)vOy>B^$a*w=oJoCM5Cz?=lkNx-db@{Vdhcb8Y5!I81? zapCUK5#HCY+`D+;sZUUNYPrt^gZ2VuOXZ2=7o3x$}z)l^k3j=ZveAITLR z+EAF98ue;zscC4)y{)TFAbWNh4M9?Bt+=MCzN*YldoPjh8SGw=;43+w(4|RISs0I0?A_DRg#pw70i+ zvT9!~EqE^7ipEL6(Csuh7N3O^Cnh91^937C_BISCj}7-IBN<0N3X zoSQ%rM1Z8cx39CUCNtQ}!Q_g8uI~Pm9<6vnB(G;2vF^9;-n5k_dOKP@Jbze6>%h^A zsU5^I22}~3ZK?dt+xLA9`O)q#&2ODLuuoI_h=Fwj#Y(|XOc=7xH+{c+lw<`vTAJQC zuB)Y`srBRSta_oQImEKApWc4_y*kCq*4+5g;eDJ0%t^qU1dMPGmPJhbaTdbd)*>lM zkMehM4lQe!vv9+vCS*e@#PW_#d2K;bh{KZ$C$IQ7$tfI>sX@Zk9dF+B$ZGSG!Ef@BZcI-~a5W z&J6eSFu!_4XP>sV-qn<9aRtE)X#V&8{@XwP*)C29^m2K6?vS?jK5bn?uhL>dtrTK- zP6BS{Bw$VgPV%^Cc<9iflh5-yY5sE(a7t=gdODVej{MxjnhH(=<|JTF0&b|Q%#L<< zO{i|F=Op0B=$P0znVjq&zy0w~P6Fm6U`_(QtG{Z^im5a8QaA~ilYlu1n3I4x3AmxD zsU`3pCjoO3Fstk@U^qDmxI$cB0s~wDK};nLO>J%Rp10kd&2?p>jI8qdTEuOalYy>4 zf|BM9EmCRMhmSw?$eOClvN#EtlYlu1n3I4(GRFEqB;)$}hDJ$kMRr=K$19T?H%=V- z@j;Flt4l$CZZ2_>>+74EB=x1)3Bk@T&u?Eksi&uRB`!ZZDe`CjqA>hWoiYIXKwc zSlal<_x$>=fBwNqz?=jultV$q6IKU>l_Nlqnxwz7|ED@BLM0SV0!9cQf_SPq2{^Ay zTEj`eoCHj^a!vwXG@p}zISH7PfC=QuNx)AWJvj;Z?Yp0QI=j1itIF$3%8Tp7q8xF0 zW{|&^n+qoaa}sbrf{>GdStf%}EE%UQ)U>?1!^zgBKEy6U)F5vqXc~Q&)w-m_gT`nh z15;bu>bjORPm6$Jad|~m4P23|#HCc$TAyiceEt$A0n?)-#gvnPsk$I10b~B)=-x=_ zI}%O;<|JTF0>+kt`u##89TrMT|K%iLR41lq(!xo=Ek3|P6DRHI_3xvoB*5z z3^zzU)OL`RWb*co;^(HO+T&EmOnWS&4CRJ8iteeU`6I{wy9?Z|?bt9=Lsdn2dVFg` zLmiTQ5ho~SMpIngY4s}f*rFNJRF#y}&On>5C@m6+ii(Sw&r;sroq0&#LT|w&b(P^t z%FDvivvYH^GB^pClYl>Y=7gC^tN~5}#zF-Jzo8Wb2H{xENx*v_dt6`0Nx+Sq1k6dm zoCM5Cz?=lkNx+;0%t^qU1k6dmQdzrHEPiZN%}KzV1k6dmoCM5Cz?~oaTHkOIFed?X z5-=wL*Eb+NgyDF}$V^V4W`No=g7STj?b&yppt(xY&aP_s- zx#u=788n!JUOw-Dpbrv*<6RlMX4W8eDzi*cF$5Q5=p(Jrn16qPZqK0!4u(-UmxCkC_ zP6E#0Bw$Vg<|JT>m8q&JPxZEV_SooUWN~dZO5~T7piqv8MAWKEB40kdc1nN$?jN=s zGS8?LqlP|}>8fCXHsUIg?+cU1x6dEZ-Sfk`70b1pD=W*e$cPFAab;1Mr=ywi&5K8L zcWv9SeA%+~Ps=C>r!b$%E6Wpty`>LX8Y2>@Q>j!RkRIT_y%wS7_+L`UqALChr#s9cc_2L@|JHeTU*h5TmzUxC`YP z!EQpIt6lhjicd*B2-Ha&X%fRO1SFdi}|0gqHySJ4uo z&??L*csRYy*2WJHY*@c$HYWi~DvLP@m>6|1WTJ>wZ7oe1Oer>9r9`FseE!2ai8+ON zEkjbAqJ6ePkX4lC9ORg_ycJ0xJglR+bIazfJ9Zy9 zYvqCtp%GD3@I)>vNr~{Zx_eSjM|<~H46s}8inW8AcR=v#NXpfgwKpXDyIS8nedOT& zJzFg3eGXqk1ymTV2VV6JGJqX z1C^72qkUX#ADlX%sinPF`{;|z%*-q_nt$CtzyJ85F+0rF&f?aI{d+aF_Gte2Fgz+c z2JauqJKw!~+aXB{bhI(OZm@69-aUKv9=Yuoh`0E47|Gk;^vYUllf3QB9$wVf+PQ1b z-hD@Ix_SBf2LuK&d55gIt|-#g;?a%MhxgF>psRn&-qD?UFuAn3p)xzn+2r1(Q-^l% z*tv7h{-bv-ZC^Qic>B`iY?n6FmZgQeJic-MOCjoo= z_(H#+5-wdmo!CP}B_+9uul+ndJ-vMVNWBSyf`di|ENqia4b|e({Pcv_sEF|J@USrG z_~8g$q#>{+p=MM)0s~75b2Czt5)SyU}?tK3iCjqOCR903VIey*~ zP6Fm6U`_&N>x)btb|xE#D`t&R9icR8{13U+6|mM6iwYv)c; zS5X?SGG?L?SfGe8M2t7iFS2A)r!2SgTQ@G9JxNu0#PAU#)b|yZ6(IsJ8}gQxwjhhB zde^JkTbEBAJ4$85P$ea$6=Auwyr&@Qy}7l;%O}6(<<$e*7mZgRsWN<+@(ATQ4oPt_ z*k4*(px-umhULi%Z|_|@XPo*d<>AAXl}1iC4i1DFDzpWQdUKPPS9VS6oo&lzjT@;v zZ1^xGCG{D%>>XU(y}Vl*v4J#u*@n5=oLe<}>Nu5Q!-fn~QW-brnwj|vOIv&RiZ}@v z-m>}zP6F=VRLjfp=CdGuC=o2-LGgukpv)dJQ^E{E?Q=OS5Y;eZR+J-x7iI_w^H<1$ z)1^j#)uKKg8oPx;PDvB^9n|5s04)YO3`VFBEFJ86CWZviJEoq1R*cVp5ui!YsB|@J z7>KkFd`{Z?F?~e^{c>nY$Y*1|QDk`F9{3@OiS$dK3aS^`VP&$`#=6=%!gf}260lD| zKu~b7q@k{>{q5g>>1&raRThiVV#B2 zQUEaj87WsW{pn;vbDvuO{Kxf*^#SovEHHu&H~n&udWG9V)Yw>u_>r7~vW9xd6)6TJ zr*#QpNqI&}sMjmo7w%bD!j^!|zm9UCg(i6Zh)cuFj&RS>_hAK8$c4d(D+*H{)yam0 zjBuZIpe|$(C37R0=O}EM{rzvfD4ko7Bj8WbgY{C}sRjAeOfG~iQXgTMem7(@_aVd*Af+c1xS9i*xhK8`=b?s6yV})!p~ghc~_5?QQkN zh3Tm&37Hiwi0Qzm!)XO2y1U1p$V-Y0@$qnXcJYlX zE)jM0{_(Fre|z8C-3ecJQ+0V^VQx}bkdM2IgQLT%fSkPU4}bsXKYn@lwznNF(W=Vg z!u+(fa9=NHDA})UZNt-hKm6^VfByQuud}|cL0nl`n3c&%z=6Krp6-s0Fo5{@_w@E5 zjiQf}fN923<0RlpDJv{NL0ghqxQE(19?MzyH&~yb3jjvZc-V0rV@eytrl^sajq%Q{$LnYLCqWjiozcccFm-MxFZ zZCbTx(cA?~HeU&=$7WCae_aiUMJC7hZQrqb+op{h*K!i@g1MXo%t^q4H4i6`{>VSY z%x0C2$eOR{@JWujkF|;%UbIYMYEhg4*1G=dX;C8iX9K_(U;VlKuMVgbMfYe<&#s`A z2x>q3+JBE0c(u@>zU0=c0w(W2Inbg*5r_B~P4z938*8j$NRHJ?F#w%qXvLznQQ8}( z`@)CG>4d`WKoVL>(M2ljea%V0E*-s`1T5^WIOo!?K@l)G7*1dX zglJZvMjC3A5rzsru42W=bT##1pYPM`gQ@{Jz3*&z)`4c{U(;6z0TOyBMxbMP|MdQA z{`P-HOm8}u_jmYg{t7?D|C{r_B{8#{&2Kh0=?nWg@IwkD&|HJ(IZWO^y;%o1%^PVA zYOK&2|0Gl)|1|%Fr%cbOsqMkyJ1>Rb-e2>dJxKPTTI#QyFtPrhBydt|(C}Mn_uciv z#zt0GE@SE0{T~$h9@Nl{lYk$ZTeq|#0I#(+C-CLtAe;NfHcxM#F}Qq5N89l1nJbUX zEN$?lX!g|Q$3%pCJg~Jjy?f)L{^1i>E?l~8L>>Zql$cnY1WXGL8pZFiBLq3w;D*C% zN7EaRh~8oR>+{{0&41Pbeb50jh>pddOiSc z4*j5b5Pdl6Lfa!LB51;rfR9DiKtV2aLPypE9obDuw;&09UuZNC8-i{MaZ?kPmVax1 zb)cW8vFYE0^dE{2xSoP?K9jT0#u9+&9_WYIworDbNpv8wle= zidrJwy*=i4jG%{M0Foo3r-9_KN5DnU*413$a^$EZT`R~r2{Bs-b7AD_H#4shD?O*c1ET&{{=ZMh$JW7M<#EOHCA~3 zxc3>%l?pkj5}(A~iNP7^u4i^^dfp;q_a`4ICjryy$rj(n$BQ(+Qyn&V$dIAK7a7|) zd-(^wMjAYO{!DkQeKdQ*MCD<_h7KFC{)wfN2PM&mAxWQ}FvdWZOUU+#3zW^lRQxZRQfN2K7&V|<(j#5?`p{)DJ%EiYY+4h_SO!M)tMsGbGPHAe_QX)=u zzYKF$nAZyRScrzJ`85C8JZ9?vnHbP82)*I`-~a1x=t8Uq1;r`kpU;0Xo?%|F5lBwI zzRwyKAFL5f*J0OxCQ|&9?nKUe%3bQ8za(NSStl)!)PasUtZZFqk1l zF$>ugBrOMO1d~%AR)3Jx6E;(&t*~8Ehfi`^2(VBHrwy6B?rI~GvrFmf{$m9CV36MA z#~q)^nchN+fV7!Ztj^ZVgURhIFOse#Dbz3-Y(+&;iL#C^?}v|!v#+X4PRn%E(_VhRO9m|lix&=P%(&6m zSQeaZ^)M*h?BXu1RU00;`_`hQQ)MO0D7d_>Fvi=%#O#rcZC>(=6L+F@v?1*qxt>g zI*)Ij`*F8-tmVC%5pi*eshQH&n)F~tn}`Rl&2M?Y+ynOxseXGvjK6$|{0Qz;LRMwCZl-d;Q;PCj$(VchPo}JvJ ztGjHy){#?o?mnCZ%t^p>`ej-)&cLK&vwy=b3FVvif2LT|H}Qw5;P{G-yt%oJ

5n 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�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

5I$6SDtL*6oeQ4E>XA%?~OY>1a^V$d=hRKy)4n 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

~YLC&~&S&}sM;hBIRCHKGU@9!Z; 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�e0dsXMDsWsMbo&?M$9!12lxy_vX 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%0DVB*xBCNy@s%sWIrTQAgS|x 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�{lcRq|N>U=+kaZ0||M~kLzkKZL64zE1rbUDV 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 )*tC8=z>*-c>=JG&{{-Hzhsqv-+{bj z2sp5{if01m3=pmWgFPvRDTznMGXXpDOu#ug&0jG_5ecSUgUniT^dpi-;2lT-VkRuv ztspIDdFeh@)fshV0M2qQE4fTWHf?@@5_d%=Cfzxo>i;aKuj|A$Njj89R^Z~BiAHoJke12@R``j5HJdJJ{Iv3zKK zsIH>*gWAw%q!s4-1!PE$9_){xCFr|P@b!8cf?LME4p3i*h{?C>zw5v=0k<|}S?S-n zZEP)h|Gqg%|K=n2*vx!Ec~xCQBg(RH_7QkJyT&sCd-*kC)>4)p4xFO$?1W%9S72v2 zJ0m;7FE9jUgn+3<78zzQm5emh*B}U*LCHB>OGqF{Q4WWc4wTqeiRP2iZwP81jdYJ| zG_fgF2N2d!eyBc`@#K=HjOiyPK0A@J4rIS0e+1zGXfJ~gr5Ezji?|>cGw>>JqW|P?0Y@{L)>X z3<1tvhhLC=hQ_@AWC2Lqw+^@*VT7Ugzy6eVAdDP}GJJiXjf=B6*%J62ugHCC`ko08 zuDZ7L9@JU`$erYEZMXqJ zB!w8(4AOlmu@EN-W_9Cb$V|acGDV=-_=fKlXG{a8w~Z34)8zd zzjO#(`#E^_WI>q(*Azq=OU|@-gO-M=4%w~sIsnTFxla0g!KTR05`kW>nH=u1ArsD zzD?5YKYpF@p+(atDay&qukepg%|L=zVoG`jCvR^)ed)pF_0yH)WI)6_?YW0{P{iA} zQPHt+obvHZz?j0=u&`na!hM1RP~jIG76m4!6mD_gd`mdVV-0Djt13eg4#99_WoOgj zpRG~SSK3*T?_W)Ic2tJV%wN`lUTFWrwu+6FnBI|aSN7Na5&wk&kkSeV8uIzkzEdsE zM@HkLqTqj!v#tnQk(|_Lq56CP_*>j_}D9HJA)jbjgnr;g@>w!QEXSo z$_Fn5`&(en)AmNYJL}9n8uC#vQ=Zuq%oj&i4(`R|++T@RoUOLhN74%4Bz5>Irw53K zNe>iDc1QgkjtOw+0Wb-BhO9$>%BwDRJtwCxfd+sNl}xN|o(Y&|0_K^3(|IP~23oXe zWy0}^?7-r;@ok;m4!7?;;Xpn_SU+g-=x7#})OU8b7KfS@iVR=Zc6HL}0gEAbaa&Eb zv3+%YN_v))rrMfkJ?*%fEuyvTYMu$WAlTv6mW@0U@XgygI=Xis+`e@4rLm>0gR?80 zcw0sJ1+k{D9V}nJdShsWWPA~`{g1Q#6u{9sXDN@Sp)pTCcXtCORXlZzWFF#Q69 z>1>VDD)L3EigVI{4;CLC5fT�D{o)a2!+E2^$?y-(OWxg35ns@_%e>9HEFMFrrL0 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≪iy4ShfdjA!9`Dn%6iPdN$*8RRZm2Q^i6 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

*sF=w2HBpGeZk7rqCc^_nSBUqWZ$r2~yt{svSLb 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%0Da}uh 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

kxBCqaiS3gry*I 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

  • ;Y!Yhq>YD1=OB$;;s~z2S``L{L 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 z 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%0D9DJ9@OQIC`mLdn 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 -QNjkSyg3mQ9*io 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⁢PRQ%=TB-L+`4J=vIUFw+NNh_ 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

      CE1yI->PdU3`a>gMB)&E2b>=o;ftt8hsB`j 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 H6Gd4B8eqX$)w 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

      {## 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

      {7IVJcNYs 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

      9A`BDdpMcLT;00b(q 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

    1. ;Y!Yhq>YCcwOIm9-tE=wPd3xi1cw$OMW`;!CnCzF_5N>Jt;>L;7<~~MO zwyUXba7(9SxHvLi+|gyPbMG-nw)Tk$yhT`2-_g}p9AaA7VE78v6Y2Iq z|2{TYTvKgiS6!cyp5>^ew(e=S6r=It5C>(is;Q|wILG37P=wK~gQ^?0y>R!fLrEvb z%!lDT6R<~cguBu8RbfHK7Y?r9yl=bW#_%9>o(b5|+07HSBpS*O7UiWx1o-;;dAqxy zK8Pb_SbM_;1J;dPX-%TKs^Xk9ROpY74i63tfaygwf+C{W4I3RAfxoOMDag&vNKH+YiW z4Ej$}X@^88d}&c5O7wp+f6}_yw|j91pxP_AMUgX5Tv;pdP1RGk3b{0I#&EeAtB+g} zHu6lsyWYTq4*TNS;t`v-P5tVtaW|I?`TFZ`h72D*LSB3GgvrD9T3FiSZeum;yKhG; z{4i##)8b)}e>3#k5i|4-jTtjyk&&roo46(Q=DcsV>a88U$z;)R$cK#>GDd#>($T{w ztG_WaZId+l@0s$g!S#t>P28_IWXPyd!-fx)8#78__J%X}pBosrNh@C}4*7b=s^MRK ztu%eq$jPI=`FhC2@nc3R^Gv{?`$&X0n>HA#?om&nMPwor=jUXmr=_By4(eB2h>{$4 z>}2OoeW1BnO#)>6(P_?|!K@G5a-7pQZ%*Yvz!tN zIUgd=1k5u5lcxo)zz%}+^!C30>BkSPF}9%UW+ob0h1iN&YiE_6-~aY=Z=#I}TbyJR ze$hcn9r{0hd{<~`fF&oZYzy;{4JT8QX99+TQ#x-c^VG{Q2sjcy>%UCD+0B4V7&P)Q z{;U2&E|YTV&raPh_6NFH;G@H*(+re>uBp9nbUbm_*wrVd zP*{UxUIKpz%L?B?tT(tmOw^Gv|EpPJh{I@np88$P~#^@R3;{hQV=`eA~KiJ9e`ZThbaEO;hh zW(MF3*_QyZ*44Y|-aay6?076rETAtMrH)dE@;4{c=Zqdl0hT}4xdq=bT3t=v^_{D~ z8$mX~mmL85ATfBntKv2-_-?YyYCv2X&!GK*#BBXnRl03o`wVifQB8Z+fb*kPS6xw> zlalZH#-Wr)APo=)G1s&R@&2_@UjM&fsZx595c6#;VwV{!*S(UJ= zR@BtohHFD}b-5rtCOkO6&)?6*K;Q7Kk+G=-7GHH8YQW%X+a{_MWX49K#BZRRxv{Z{ ziHWJXB{9k33C#XNCE)V>taM~4P@1%jHPWOl%g~_;9jH)@NCdZ1Nl{K}TttwMhpV#_ zGPz6H>gSn&g*+3m#nW47R1WS~w`%1Iv{lMC0Y*;*)2>jMn=dF!bTzzxO;ZE)uve{E z4*ANpT9L8Q(Il@-PRT2XF@LIi?x@P{jcZmfU%nh&)*bQ;4GE#`wIVh+B+SY3;Z!itUINyM3X`R)*DaeOKW^O6Z%K|Q0GN=+ z&AoX0-a{}_6qlr`DX(5JM?qoAh;P6B=3B_W9Wr8++;;6tH+AtI7Z;b=tzWrv?(FGP z<%fL(ISn5*Mv-R%wzjsSZKV)w#Ei8pD=RaNRS%Ad3ZYVlt}f1kqI>|!3c*GUvf^y; zhoD0exWMBh0>KsT<@rq?X!O8j#t~@}+p zwalXmIpgMKAZgYCJs2M!-Fk>=m&CojjH8$RjOlnL-~j;Zkmb-QVl2iI+pFgf?b^9^ z_59^?6%`bwO59s6*}3_JMFP+(4}7q{d}EjD`o+tZ&zL!V zx`LvD;*1|OW0SLT@{0sS!Thf2=?#rN%1h?XpEFZw=5)pB)2A&m4v9<8$tx^kpZoh_ zU2WBsD;Lk7t)!$hLvh;lwR*0BF)5kZxp~~W7$|&wdig5lg|lYOoS`&-^ErJxuaKCe zw5+TwPTtca4m!Vg^{Qn{*X%m|$k^K1H#90fIXx>oo5_27I*AD|#LdY&GA24KG%O|| zB^|Tp=I6`g-K@?;Yg0pY8LpN21qERE7Jx*&nB=sAyE@?~ASOV%d&4tTNe#vBC>lX4 zxVyU(`T#0HnCZBn6Hg}e5UT7=t>zODeFV}PaYA-2c<-pNE4Ay z1z+jK3^EDxOu$F??pv{X4zz31r16tgO8VGm1P#V#+uJWT*FLdx!}`Sw73L~{iFdsG z3Li1ykC5$2pZ!3p!4>tro0T`sRah{en0hBpm={xz<%b1|<P)%k7ZAK17a7Rn0(Oz+XD-u(H6op z0h2MnF@eiA4{RF}G24h(SeJG-r~upI$ZlZ!3JeqMFti)CK@RHza#9p*qHO=n6VYYD#BtlK9X$g=!lUT?>u zxU;ug(oh%^=whIE`P79g_Kj4UR5l(PQhQ&2m$)`7*vr9CS6f3v^P&f)XL1ojpKvd~ zdp{s9OY(NKcz)xgy6UmBx6)t@!b}s10DY3Q54`)(-&hdiZfpGD^0A{TYNxd=8<`CU z3?hbi4fOx~YeROhqxoCC^C+;VqI&9KwumeD05Q)5EGo%%7)3`aV{`{0wV&S7_K&`L&DgCD6UCi zNZ9gdpMD@jAPZTN3~f+4_#=Mjp3J0>(|5wtGOPgCopq4OIUybHIJVJ;=KGTzdO%AZ zje2k$05AH?1WFedo(cH%^%H7pN7Xbgd7*+61v%+m>;LPofBCzlIL61t`q|}UDynKn z)Xth_Wx>GB&W5-{01`pr@v!^_^kgt z6EM#NJZi+K;lsZhEjM}gmdiSNVEQ$;LXn@AR-Or%kV82CYXKZ0oPVhR*+Q8}6z7=2 zKb`;eU>k<#5{^qy5+H1ll`#Z*C(F{NA1GG<$?fEtq#B+a#*g$BcW(TTb1f2a7VAKA z==G<@Y;xvBWyFbWJe~YZ@1gcI39M>3M_GOyK@2O{+}y?h8Gsn@)4+R)e+?^kR{810 zp%3y7MYRoW5@bT-CBlKiGXe8V!0qjDk`MIvcU5F1`=SiFo~{nh1Wdk30N_xI5V}eZ zM>Kf+c_v_WjYAp-)XYm7YinqKsVJu+kuW{l{LGz=ou58nSPPTn`5ZC zMS0nZy_f7tTkFU+sIEkiMmD}(;&(;m@bglO`(ci>oI`$>4Ztd-M~#x7SXp>{MPb1!Zg|DeTUxsTAVn3)uYaRkoIXM)wcz z-@JI%WchJp0jNkDEF!xPxU~ouiC~6(RJ;XBsiyG5jpWdnk>mN$xk?E)P#!Yp9=MZOo+1LT>2BfV_i8tC1)bW$Be!x}2*jm@oX>>X=r z>&s&kDg+tPA?`-T&vdV7pFFO4OzXt)8_x{Qt?e8@vs+gu$Q7hUcsaa&s(0h^nbRkZ zpE`B?{H+JCj8Q5Or7UZStT-XW&FaOoyEiYL*FJk*``F3Lw;#PSGPky8a#Y{X4t6p# zc&2yz#*J%NFP%Ai<@SSTuZ_&iEvXbH5&71X#JX6%dHUdi-aVb`H*Vj1^zZ=eIE67es zjtvh9^!4_1cXR~VkB>iS#*hZl-z{mbMOX}3=V{4tu~DHY*vBgIh4-KXmnYB#R}G54 zp^6sz;PMn7ABReOodlw!|FbLuc(|xXVSWyHfY8!2q)4w}I7gldcu@IS>>u7j#3dRU z;P|853fA!b6Fh)t0zR+HGXcLa0f9?nU0HT?u#c0ig@MV-``0gDJbUuw39XYSFWz}- zXa+2JXJc_jw6~L;sj1D;_|_1X=cI}cwNm|EH}+(}1UWqP=mgN>!BvEi%dPoJSYz*|!b8+#{L4+5sZF^v)c zbrpiV%+!RK@Q|QDe?Js$3J5~(G%Z|&#sO#Hg3{&GDKQ}~E-sd~hXmQmh8!_^ROW}< zyrd`(*}PPTk7okrnSi?rU*EfR9@N=@?j78UWY^^j=gyuvd+z)t+aIRq=XA7s+nPSQ ze(sExin_+Z9h+CJTDEA;Y{2nn&0nzaR4mT~3cI*b z5kiP2AQ(}VEC4uovdcPro?uWu7-=Lp^B1q+(|CN&>_vPrL493)&6o1e9T?R~Lw!p| zP9vmp%;jB@Y~+_?(K85@OXglbmAkn1Xyz&}wvG>IowQ>$-vDbwKA)hmvC2 z7SM%6OtBhJkqC002^hOrp0-Kuzvw^SNg@cKx2ma9RpC{rO#Vs#>Gs0j%Eo5#vsI65 z|5g8G;Aza!D$>1RX!)=DkD7IauErD{z6VXMtfci7QkI_09z?5v$&pjhdS}0rwUsEu zCQ{JYj)ehsp}}kof<-jN=xe{VwyvpfP4_elC=pgvR@XLQW5Z_+S3#R7%ks?)9i9mo z?=|h&Ql1GI8wVNzh1tnp0T(~=#Icgt&O#RGAR7(Cyrgb>Ki ze>(W!!$O(E|K4DgIe8{v zo(Y()Kb<@iFx4`k#xnuazJODjK}&J9U{?>a0$3yanHndjuVkx!*<|)DMEL)IVghHi zg?J|5$}-#!QKyT!AKE*5vQC^a(^@`bviz8Fa%;mga`NCsMj5UGPTnaoH8H-re){;) zJQMKrDVrXfIeYnni8wkTh3s2y{Q*pk+jV_@uxCg}C@8jKS*0-MykcEgA#ZY_)e0$w zmzS59j|zH#u+#tH7=ocd3m=3!$fc#A_|xXWY?Cf>q~OzFd4kPACJl;lJwa=U z>XvoD4WJP@gEBdjO4>WyZ=KU=V`brS3uz>)kH(XfX9DJ#fT5@@xF*+Cm7%_T7AkCH zWoJXTx&1@-m8H3esK`G6U}&V;pN}|Fb2#AWf?EUsTX|X8pU+49#&TrI56_@MUo!o& zZH8sZ)Z+^M?{YR3&jd`q_AZ_Ym}deuF?aI`3TYEJ6$LvQ1Vy=7U3l$obyZ{c+BF-_ zUA}PQwy}+~R{-RV$pKDArhzW^k6*rZLqp@xzFpf?ubktVfMMa}=46w_PKG+Uj=Asb z7c$Oahwx0mH~`>n=b3|bu(bnVe~-D6AFtXZOP^vT0(x9vTA{9#~M#PdwR1+gZt z>@8lsd~Ns^4ICimR<`y$6R=3ctZNw9FtaJdNyaxcs!bw1(?oy-5t;QczuBM3kYZ7Y zHgRu+E-QpBA_JX54BS0V+Ez!!jJsPBcEq)_@vzUBiN=_~r4py3I;~yZJQMK9Z)Y1h zdHMweNjeI)4ZW1FK3(CPuZGS$ym#7n-+eDXcH$2s#y_&KaV7oj@>@Ck){AWuhYXvz z`1GPN!%=B}_;+JREO}^TVe9HCk+y_yoM7`}*~qVl&zL`T$nc>k!#`~F53?37=9z#E zO|8hJZz-^L(@|QWbN}k)8@hMy-nnz{{)4A4-WZwyMMzeAYm2z0GATLM$Hmdf*}=-p z$nY&F-fSJ5077O~eG?*9byb2appRn!Klb);bNBG__45x53Slf~EltD(UR_yIfI|LA z{*I1}h=_=cii(boX17^Ht>ILlvcgC+$jeMiO-=%*IBV>_46g&v1T1N87B`AYbJMed z&WuWBwct_b9{7o0|K~$pQDvQ|t^s^cO;yENabbRrv8m~qSty0y*4FWtk2U3j{4(&6 zwYD`0TU%=4W0S)|BH|K~(MR0b8eH65oDmxtpH|*1?P#fOlGLYXCiuT~jEs(sPwHyW z+T#-9Xk}&wbqdRC>TYi-X>6!U^Kv!zfFCL%*5F=JxTj|T&jd_I1x^E=37Gi^$@m5< zSTSYaH8pW;+{}AO(0PU^Dn`Rgi9|IP_-8qMblg&4oKjYa zLxN`l&dDn%g8TOU&o!ZTUcuqPp;3t`VP4j+^{$^gZW$4mn39$O4^Vq&ji0-Vy=QP# zTta+=drYMFJ>4g_Zoc*j!WNL!)>)z-oa|}+=7q7fe?nSDlxIkS-&6f37x!Fq_3{sm z>_}U?_Kl(LwJTSz>)bc=NGwPXGxG6rdZ~4MpDrNgJ|=s4CSY=r6IDE2M!AL)t-3-O zrK@=qztf+o18|qDQLnbv+Q!DbhZ<@?JaN8r>cPqB4@e;qRhQeS9bxdd!N0TbxaJZ} zs=1-6C{mSY0%o;$V1dKJ1{MMkL)s$h8(?s-7zWwooPkbnFQOOZ7GO-MpQ;3MT&!f> zOL->XlSed899Xt=!L;$)Ozd2JgWK$(%BdfkX`{C_d8|BAMz}*p-L)LI_MCb?W59b%p1PmY_ zNa#>SlQuwEqxfPI!Rr{#4|S}nD(O$0^&pF2Hc}B!1_LA}qsp%lXhx`+HTsEuq;Cjr zfLbsoJBxl}j8PnKR7o)v2;rh1L`|4Qd?>{L6PA{OQxfz^lz4!@U;+V(mFHxmR2GrJ zl~zX5UMpvTP$LB&`LK0hDO;g-8ad zGn5t`XrI7n6yqh$XPlVyjizG&V^U$r=`R&n$1?rLe5?mI1N}w~`p+N{)wQh1AWgxZ zAbaVcQ?9K@^ENYhsc#!qQddJWfQnNfWAcQ!G|Kk*-OFb*5AEJ{!Z@>zWk6vYVy_Bf zfv>6I%ZE2kYaHIaMR}d7b5#{E9L0h{#uF;6Dh~H_GOEURw{4UbLr-thfhkmCF^bQ>h9HZ zT57x2g9mxp@--VaZBf5)RY&hp2^~$PWkq%m?q52qd1Tkdb*opcRNlO0>;9A4S8fp# zsQ@dzq%_ar-qq8`4(-~cyl##1#w|PcX`Z=o{r3IGY?0HkSQPLK^Y7ihW$V^$JNF$q zcIMJ`5Sc!a9hNvRc_v_5>G%(dPBdx6QXweDUsyr$FZ7=*fx!+XO!ct`K>urrr=+G% zgc6;wOkg-*f?|@hRwF9)y{(~g_{1YW0q_;%{3!WCUYD=GO<~-K?}iT_Hg<(>GHCL6 zCg9-6@Gw|kmBqL3*tkDlKXbx&Lx_s^TQravkC+M@yQdXIB?$C+Ldp1m$A4TG? zS(jnMMk+kAx3w!SEw9Mc*t&H2s_7HP4r5HeoP7AGY1dxAF)S(LnSculh;b(`=HfF+~t*d+YKGFE1 z1DV>O@lH!kNr;OIaJRQWDYDnE_4QwKJicsv77>9bCn5k45$NseU}tM>%`*XmX}FK7 zxDX*N8h-0z#IC)4pnfIVRQwLET-M(I_~FO*vXU;nz2Ieq7~^Hq4I4R&YLj&9hzZ2sIi(`QVZsa44{0rO12aCGoYz>G%Y@eSS>wvrzP7Eeu;z~vb;tJ*ZF-GDp$O)MZIgwFPBTesPU5MTla(GW#K8gF^ zx0O{C3ETVo(S;tun2wvfIf~J~cS4)fTQ{sw-g_gp_gy#1u|L2g3VCmD??6w)x$V2x z&Y3%X>i4s*#CG%&Wit7*8i}d5cc9*sX98ZhaMtu`p!)oN(uBziwGC}O1H&U@V(EkJ z?Ci~be01KTWlB@0PW^t$j8&?)UYR?3`iF!?M3EF9M|Yd=$t{Z(&YiPllg6z_FWy=? zdiVxGjPYQ)CTjZ5#_RxRdylY)5I=8kzrfIlXoP5aCSW9(4>ks-A%`JlAyB9gnF@8J z4_M8lO;9%u56>d8_?7&M&?)E>BLSiw=s<{ht_KBLNarA@G8wFr0nNc+KQtkHw zLsZH%ie>-=h)uwLrulg$V6$__w=1t*Fn8*lA3)P9H*TqCD@!7&sKDmZJCOGFvfAzq zYm{gGFn2b1deLK6L^Yf&s1iX%=sLah@2GFvv2OV?#c31e<>cfiO&WJFmuCVFkBp9C z+c#|x;A8EOv^Ce2=4GS+A(NDh5?m?CDL6E6df+%{r{e%DSoJlPaLiB&P;PEcPB!gN zSmC51be>T07_eB#uBUKuLxlYajZ_HhoxnIyE-w6zD=djvg92c|QwJjE!Gkahyyb&p z+QQg@ha>_jIn6ysDExOh&jidf0jp|W3#eg!kxDG8_O6e8AAS|ax|+RyeC7C&Lq}8( zt7%)4YYI+Wwj1}h_xA`w>|Z~+cV7L_!6Pb%RL&S?WMyS#=j4!FjKy7-?{E3y;q9|W z4;(mj=-}bA`ZzsPQq$6zT-;Jqp5txwQ1|L7)qVR8fKB6^eq?L{sS(K~jn!qj(GD+f zUpc37XxHxj2M?)VcoP_c9*K-;w7sFEAT`MP`E_lrqX$6byYHapMPpCDps=Xecy{JA z7iA@Q*}g;xag_tRcJA4K=-4$27jy`XjHV2mcAg2CRS~2j+~hjrnShyXPwA4Mn80C; zHgMYb^V(WRz{vNnL(Vmb6}uXT33(~ z?)2)~ne!KIM5yb?#xtgGNym>rz3-J&WhR6;J-@7>p{eacuoG5FjAekm`}Nmf`ddnp zBK&P1Y9CWOcKTYfgsd!pC5Q>U#Z0s~=TU)4G$2Br-Z6AeQ$3<+s28qeGY&=;gvQ0jppGI-+&;$!lW^ zTL%{pKnDQ-7FT8^hS=-t-MW5OWzW9DYG-sFzXF8W!NnZ~r^#k*NQ?A$e0}fwjq_>; zj;NiycK_)c6DvDZ0;T{uEadjG5S|Hm5J|wk)1mjO)STZDT`BF?^{4Y9l z;|E7Eq&}%4Aw6VIp!sEuw3MzE>=q&G05Q)5jCZA~rn2j2&kH;gFwX=`(fD$t0s!Zq zlA0J7pFlbYKNGGLJQHw}sH!+GGbK5=4Dm_#IV};6Qa5r}S`0L+(|M|!Mu2$ID zMh_l6F|Pzj5+-;#iaU06boT%CuYddX=Xd?>bw#1puO8gJeLJv;(zq*1akg}H_51=k ziuw0RYtx+#AKky9ecqq~Tp|D=K;GTc_xoS}_V=IP_4c-wBzTxU*1dV=q;3Ww5DN39 z($4O_f#3i5`#(Rzec6~Bs~m@GXc;1ev^wUTe5ZTClI#vHkltDovaq zCnq;SfoB3njvjLH0GK73H>%-ZT_Y?l%1et00}TLc{%A5eu5&C0uNVlsw8YrxsHjLJ z>V=1eksJ+4H?_dHqA)Ku_1uiqq=dNG*qE5;XewAqQYs4sR6UjU1#&DeJ0mqEDFI9) z^lWrsa+L4npfQ9k%gI8QBN5J1yd)D8-)&|$4{Mo;OZ@Ocndk) zB#o`PcT~5pnlJ ztbOIS-jf%v-eAo_LvZ{BYHV1)eBr{yOO~$Ly7kbxE4S|IJ$e4(6(QcK2hRiybCm<* z!R(|0;{s$9XQU0q-oZ8O5VQjU#>Zg%h+eQZD3&D_aJ9_{*w1Yu0@-NxjXYYgdT~kN zAb>Q%mvX}R!HHQ}fk%|Wkx7r>I!R3;>+AgVaw8Td!gW=(R;PidGa zbllbi@&T;KI=UNBA*ez~kulXQ7Pt4l>*;E#FBfEHSBSs^F03F7V_^dlS{hqXxZvZj zKlXyjt2{d?$Ujk7Ev%}*c%A z`}f~}{V>qgT3anhiwksda`eh&MB>?5SlP{O(qI4j>rWs0z=l;>2sE;bqocKbY!0^n z)D*1!R&n=mS+OCrvu)@6CbvS71Bqk@P?W)o(UKh1O*v* zCSckh7}GD$1f1Q`T9p&;=VYq??9R2*ni?AF>Ibzf>>QlkJZtJI3c@nl8VaI$CSXbm z$o(Y)gKXNK%`rp!66zk*RADhoJ z0T26b;B_!Kfmt+6R)tmC@()P!q3GCEWVZ&7FO0)B!Bnex1Zj1 zh?{CEOY)1dlEVXiTpgY4ZLDpqtsLBVCSYJ(8220yh$xdlb&PT6Wc=go28`k<6kJDx z9H?|w<%960bjrij=PH?^>VU>}13ZRR{sbVJ?Modi+#&_qAdv3)%)(pp31}k8#^2Kj=%lS?`=)Bsgcpy1(h}Rq9#d4 zH?pb4mDypIrWO`1T?7C1M@PL_A{1n&6;v12G`6<)cS-97xfwpDmS$#7-F?6Oy}Pu! zqeCpJsj8_ZlI6PU{KCxm5N8J)BMX<#KAs7fJ6?GvVC+(qfcR%7P%;%*;|v74JTPA* zOdYayIE?=14qr_0|H%a2nv_-XY5Gr75cVH+m9ataOu*00CGUP}jSBI3a_hd0Phw_4 zaRsU{)mBy{IvLzhIdb};kwp5gBhf25-p$hBnT5B7Ph@;XX-;y0se%5*!y4!A8zQx? zJ;TI4DJ444)6v;0DkwbE+r_}>)xB#cPMo~*@U2+dSzn!Cw6=CwQbbkAyOU%q(rxv?c^joUlg>T&~Z zUj|t{ePi|d;Z^P1I_hedu3goAVPtNF%O@F}^#!q!5gyO1t=~S@yLIN|1>KuE5A}`A zt?gksGsBc;0wxzf0~2vq2%ZVJA^Z5-EZQHWU7c)qz!%ztTEM2>*d%Hy-8BBOFUh%z z1Js4>Q3RC&D4?-gb@ojoHl7Nll9;>Ypp+%;F3owHjy%u@LKwFlnV7mTTMb*FNa8U6 z@$1)8QAs-$6k%5;HU%s>cvhO5OEu@28@_6IxLGHprc;a&Trp^{mw~9frOHox!(zQR zuQG1kKfZgvetH=SEu!EbC73pXVIkW@d)*423HXf(JWZ(H-qKo^WOMV#wsp(q&6&Sw z)wV-gmvtZM8(TWK5(_Z>hqtXdC&J0_$>Zm*4UJ6AZ5&)Zef)z$C`$!b&Q@|}HK163 zX+c(M0>FFJV&mfCScy&Q&Lpmw!YRFD=g5($F9jWp3+{fj|Y& z*!b?Afh?oE{&rzrq^s$R$5#2JWdu+Xpu!!=Frd|4sitb@*TOpJxJ| zcvT39Bf&*@Cg2F{=2%%cldxsB6x~}fNlt#OoW=_a7axEBz~Jy$k~8BA=QxhPqz4;$ zCSZ!Z)9LahlQ1(P=s>3XH62Z4tkl%j()s>nL!^PbDPR(c@_i}ee#W85dQhzrnfy!r zCs!mHz=XUe`JidQZ4fwiF$*iE!i|^x`HOG#loqm(+5Ddc?EL4MfaT=nSNX-K62L1l zB|U@A=XSBQ<;5oNHjc?99mPjpdfZzvVWQlav17+g zP`9#o_4Ex0LYXZ|8;x%-H+ai40mBgInSf!=4<7M!>8WRTUL5(%k%HAkja`Jr6a!-r z0d8L)h&C?+!uyK zX6OMFI^&su*~TOl*HjzXRoCOz>!_u+?rFCaRtz>T_)6HW#4`aG@JztBuV1@%{ie?C z+o;~HbLr*_Bk+7WxzLTbts$Rh0*03xS1~%BVF$otnhoz8REX?TYIJr(&QbpIOu#GG zzD`Ze0MV#Kl6Pg{eUhKI)_rM5SC?VmVo!C1LB zTjYihA3ymkv{4H;j32SeBQO}$h0-?j1;f61yH@G!*l)l7T4Da=AtS$^I7W_V0#-hE z?Ji1vwMk14j{fE=rOgw*9WrL*$Vp05#(g(@-1p;#E<6jlff@GyY_sDdk6xYf)zFz! zX0F@3e#yKQ%YPX4-KLATpS(7*#OW!izcFE(=EScjZry!A<*0_nu@k2ctlo7^@0mW& z1T3tCXJOEWC)=JewXrY*@`1_P;AqHj4S)qtd!-nj@j*Gn72NRlo_BrSt(Da!g7P|Y zUr@~jF5j4iSb9Ew{OMhLV@+LgQDjVNK@EHtG&gY)k^IL$e(CM&l{VIwRaPbhc!VV9 zG4dA>z?7Ci-231E{H+flbDjy9-QJ{a?LQ4j>Ram~4Bj}pmjM`rG-Fv*16wqz+O~K7 z+TS+NTR8A3qCvS-t6ut1%3z|3VlEIwhlD-TUlB*vod?^_J(a9yL|`qlz;pc zu+KDSTT6W8bRgjVhd>JC9i83Oq5t>adwPUXb{1GX6q4aO@JztWeNN^)X$j8+Tv0(W zMzN%^szI0@?iXlldi|b7SQ%+Qm*`)P_^!C8v!S54I5jNL!Oi8~xf3^x0<((>3X9-B zFD-@scm4cMQdW=}5uF?wWN-M|^wIrCFZ|LoGP8IlU_G<^k_usUT_cg>QXCoIo15xt zTJjm>SNd(I3h@I80<=nYg(AmPt;N$A06{h zUE?f9vQco~5SX3Kfj>1$o3pZ95 z*r=3}Dh7~3wFH1=NLtzW;p6+hZiz@JKs9e4|8y2&2PtY9BO0u1`Q_K2-h*maBrMEI z4E6K$h(!&6;)3jKZgj(Mzy0#lk0{eqFD%SR2=?)Cb&G<$FgKecDjfLRZ$JM8D&Dq+ zikx^rYTaF(UE)ef&X_(MfB(yGKmYiyABjk%Y2neqKAx^l&K~*2;D3X>sI6G%_};u0h!*z=V6cJ6alqCE4+zeqQeG z?yhduFJ2nFH8KSj4w(^DOk7IIm>IF50p1=S9-dCGUH}VdY=#;dwW6kGoCD(K>T*GP zOn7jBpTD1rfxh8eBV$twtS#72jZLsa+eF}LjztNO&_FkHV`CE&6H{|bT3&bp3uiZq zYRmJp(vxF@J)KdA$=V7RC|Z9YbEe}P|JB0gFUm=ciwMFJb9QpHw=ZQ2u&S0yT{WOa zM|nwMPG$<$n7@y=r-!>6m>9VpIMb_P7?%k0v1(G1V#9(_hQ!AkFfo~eKnZvzV4evW zM8nIMFJG~I#kxbDC__Wbry@2uB+SVY=z5KPTa;Ilboq*v>-LyH5z5Ldt1800?X1n7 z-M)HKb?1gPOP4NP2Kg%G-7l=IZ85y6GThO|^yPya=gz2ZRbIJtF_?N+tXREq$3qhn zb5KWD3X_r4Z*cGCg)@h?uV1lb(V`{Gmakm5@z4WQ5+n-h%3?2T<2Mg(Upaed+u9Y& zFn;Os)$2E(x%=ShOGfltnPh2fpm+NM&jg&Fk{BNo9TDR1<>unz?Cj(Om~at_h;srU7Zn#*RmzGK(cjSp|G{#Rl>y|LfD76AUwBXN z&e=m-W=$G1Z0NT`hYtC6$dKV9mn4-IGA8iKs_;9e_BwmlFO(ZGY}k-*zZvrFw?l@F z8vjy|m6=XX&MN0e&K?gmlxNC+H+1OGZ@#7RL%$oXf@)H+<-q<|S{SmOb^W$e)5 z-(oEO`*!HCk!yG+;EUG;#n6u;L7K+g1qxFqjvM*i2-GnhHEOK^6 zi&t*|Wep-=o(Y(kHhaO3+tUMTKgRvXfSJU!OMeAj9czOHoD+W016loy zoKV~bd9RG&_lqgO--+@9Fv@r)V9=X(`|966vH#G{%^McXo2f8$nu5}dsan|Asr(z5 zh~G)<-o8AwckixMOXtl{P?-9If`X#riuhu9NJ~mc-evyq-to;_H!fZ@TVcAwv}uZp z(-oED5T7h85+EUX;Di0;8@p83FJ88M#?0x{6%-W|XZ)ZUo1B%CUnC$3=66j`Z)ogM zUNU$7oS8~9rz=jMK5db4NL+eOUSSdY(B2p8YOA7x!u;7vN=h>nr%hk0=gKny^Gv|d zVB!(wm_Uhfk^M%zq}U$_O~5k&)0R4rYH&q;?`Gvqa}^fMpEzN{#EBE;#neN&5d$Tr z;ZBd|w~ub$wPE#&>57xbqfqwb$?~cK7;&iP3VC0P<(ucnwr$e9JqWLFHnz+1@V2CpSAA8}5z205LVh>}o%kmL-;PYj~e$d6A4 zDn(HMUt{CR+0QcpvxqZ`I=_4OZlH%zuF~#8ap|^ZW}J|8;DeZ$z&i#8V0_S9N%Gc4 zvR%;n-V3d?Hm+T=dgs-Yo<6Ec1pZ4nv_+hJpga7`mYu7VW+{F@e)`2INf!lHD1}hO zh7Z)eRoS(B>GGM=rv5Nt!q|z21wB--jg|nLT+A~8KTw&oV99h4g-@QOuyo&ryU&fR z99-Q!ed+z}>F(@q^U>P4VE*h`i#DiSzJ34cYZEJb7k4mzLk^o0A8Kb)Zjh^^cX)V^ zm#d3ACGdvwOu+Tfe{O%EErQ!W5Jmv=0wOU4#Hs&Q7GfZH9vXr$+z(j3VnkvG@jZmU zm$CfeN7j|$Ha^QKK0)Ap?mO#3>3pB@L6Bk+Y6SPE4lKc0_IwHv$tI>A2ndlJNL-qh z41K0i_z?-WJQFa_1gvZ6;N~3=92P}uPTJ9!;_qttg;dHnF^$zx}(zp{38@$h0iO5u1qJpu=JBO>ZWnGpfVO$-VS2@Q{k zh>VJkq18)ko$}sltKm9B6#;nQlF;Jg;}a60WOzQM%p-DIBcwv#R6`&uGczj#RLrTA zenWXVi2V_l5c93^Ou*P0$j$-Gn06idc(9r9Ou#PI&n_QRQB^ylcGfg2D=QlfOS`@2 zrw_k=Y|06DwK02eLGy@;>S2{r&m*E^VzK^6-u3?dyUvF6Ku4>$_q2~5K63al>i_r! zVikvlle}Y~Pug0S>}_N8{MH%O0|yTuIePlOo0qSDKwuD)cS>98i=$l4Ug%vpd6<~O zHO@S+b9AR3Opb~ORXO3#hEH@ZpE$Jtz=6Y>XCI?NkF$q2RarqL)W*8<^az)idN(ef zIC5bBfy2ixJbQ}{PVQcG5OlORRTZU%IJ~%XPF8w)8lnM-Ni4Y+*e%>C zkoSuaAXomAN{qnWpPC{|{soegmHB75A$BzcT|jaQXk<2&_f;UhYW$cR48_ z+Lz+N^Iz7P8$URbgj~aB$=aXfB&2i!Y?aVut^=m$nSk-G@Jztw=hQYYn?0Uq0%o{( zo(Y)Xj>NnO957ubWcv!w1T3x2i}EvjeCz5-&9gpfupiUXK!FVZ#b5sZ&wu{)V|R-% zJIcfG(X|Vww9dIkMWK#WytJKs5WoHHAAkMrM@d6PZkXMR8`@`1p1SB90wWd%Kqtw6 z`V9!Fp8E2lBp;K97f+#GbAtUG91ihLq^`Y-r$0;x z33!lu`Uige_`bVUn3wEt`|9pl&0{CdKDBXh@$?HMd3SgJz`J)nl6nAeyv!cnJa_E) z$t#8yWFPnivGPrQ0|Wh?jaB)Hezy8|E}cGg?$TppOUS)^{BUNV1k*r&e^*6Tvai|8 zhkCj?cWysM1waQF>0UmRmC)HmL8O)p&o>5U*4B=8MsLk5Z5>@byu5vUVP9~G30+c2 zrJ%GlFDcB=)6)|(lX(*a0|yPIqN#3mb7PGVRAEHp69JmfaM<_}fG*M$IJDsKAqWr1 zLNn74Wlx|;d~6IQP_Rb$$HoS%St=98NO+Qy(X9n6WF$w?E0n#!1VSM(z~zJL2OnT^ z60k+MBj7*s=`}Ud5sy4y1TMkzn~73msQE_4cDTwMJQMJ`ZD*dkm$x;N^OW`no(Y(l zSuqi%4f)PDPpE8M%rgOBJbU@>!{={|%q*;J>=-%(PUN6v)yBkr@b731GZD6EM9ITp{K{6y(LOK|6F) zQ+-uQer{noD1NG{h(rw+HIfrUIm8VWnW>>(_SUBE*0LN+>RGOkQM%^k(flG&b8~~JEGIG8*~R3c&P6RP zE#3Hn9CiW3M~BC^i5qLnGNb)n9gH5_KCgA`#L<(ksq79)@+QzNH46o)fj)NT&+gnf zqot{-siGT&2vt-Ry*@!zyR@k^FU-TqK>yyY^CvVlkE$KfcJ=l}Fsrt%wl17!0gMiIMI^r{r-`guy1DaAz<8sGbr?0sktHAyAoKv&1Bq9Q0u~yMRn~X1 z(Ofuw;;7*xh9iYxD9;3JYhz<;Yv57lEaHD>%n>tMi42ZoW@h+kQ#HuvL4(NR7|)5 z{$1t+Mrs1!kkpxBm#7Ei4fQ0YD*glgeLa$vnv%SNibgRP6qU)r6WIUb$AP{ca9x!Y zWu&DhW>vOw06)ZB+dF!CKK%O2hhC70w7|hzT#%I<6&90MgMS>kYdgDpe){9Lp9p&> zmNeH0OY^f5LIS)zcqU+;2{;E)dVt!9%Cei`-X-D|n2Hofpu?#M+et1wP;7l;)e?x0 zv57R+ibz7Sjgh<#;USD|C<6OSc!0Z^(X*$|UN~hmNvR4lVJ>KM zkyInOJUTkW*UjGc?W-qxw{GhP6~m)LmI#R(%8JtBq9Q}W{2XmfUOmyhc>bLB9c!Km zSnKu+PT}-3w(K3 z_x%3-NA~X7wR!EzWlI;#o~bl_I=alh;?-UdkP%^a=Z5x%Z3p)pIk0Whx|Pcp&Yh_^ zO=0>>r8!p|+bjLzQk?JIytMCts+!87om&MqmYV6)~c-NNAYgesYG-uANSx6R~x#+mg zV@XF2&jgIr9V+jGn=BRosfH&35P+QQoUE)Y(tm15SAjK)>uN2W4Rrcb5eB|q7y)Y zJzq8lp21zlWkGhxC_6xAxWn=%tDc(xm-)|3o`3E@mvwFe4gm4lY^Rs-#Vh###RSST z0rO12xD_%GQ*XPtxvsJ#Gr-Z&%`e2q$q^Xlz|e>Y0_YQpiP#uhYK28W)g;G8Abp5I zq!SX!x@IPNJAwUMMDuKwv1nomH#O}KtPckz`jh?-YPYPz zzw1Bk0Q3?DJN!SR|2z}0k(K2A`)U>nM_6M|e_;N5p}azO$EQi#GMo6&)eV7zBo`SmpwMY*W41$ZB82??xzDlL6> zp~SgLG@n4=BfORYo{n_VJ+9Hg2F=5<$DD=idY_w1o-(GNgydtYOC2Z-SAzN_g$2-F zXfW5fy@Ae6)}a(ofFhERmmTLe`P)8S5m*QcPIwaH@Aw}{Kg;2QFGH2=64~*Ny@#>{ z@$dZd^u=>z=Re*7bin&W*J3TXt^Qvtz@G&C8eKf-!U6h7-C^ae6g}-#>ir(7yFs4{u+yPI=>kS+i%(`2KsP z#p^Y1J$gY|q^)_cPwm{Wa`)OrOII(MuQ*j{#uUW`t9G5%dGzchRur_aHbhT#*G}by ztClUCJ9qA^*^Aa~*SL85(Q}>&n2u(yz%Ov$$$o(Wh$9{3L8Hb9*! zd;yVkE8rN2_oFTzHLMbNh>^#@$~o{%z+}w8Vu9H~Ut~o9pi7?)SZ3{Ta{BryMThG^ zHUOJ~88-ik37kp?pmBCh^A}9vurF{|!!&FP%-!@aOyCISwPtitaWG85o(=1Vdn{Qu zi!)jtx~)`l996n zayGi7v&7`>TeWH9r_6aNrRM`m0CY_qH+y?~Podl0{oCd%jhC03o6v?**fp$Vq7ZjK zdU!{dg?;F`mGkC|A2*I?0^a-F)WO3q07>{f6EM?;&tGU=lSR*N5`Z6lme5ZmB{6rC zfcTS~-7`oEG0z0t)`nY1V=EL`rvJG7)Jsa@_2$jj5qF4@cSUk4w#4Z_GQhgR^z~k7 zZ(TZ9ueq(MwUw?x6s_g-pL%)j+;eEwSX7YIYinw5rDFIsxb1QJFP8Lp@839g;dI3b ziMnhDTdQAQMVNI!{e)9Z-qGFNEHCir3oO|0!5U44Qgz!d(e1Kll~ z8U}PQfz)!+?>3LrfSg@U%%tvwo

      mrDq;SVT~8tAAg$%=A$_3+vyr7M@U9o@VGLqfv<@-sL% z^6t~;4+HJO{M11ER}ZhAzo2yWxt$YS{9uv~42-;c|9-Hmu_!Cv$3pMkwF?(7-!ids zboKHL2ACEHuCci3Js5dbBd#0q+3R)zr}?U z$9FHBK5fdBDbr-uy>g*GRX^4p(i!neUsFqM%ew6w=1h^EG)Zd4{2hkyk6qo|=@i%A zA<(<4rg&i0@)h%?CQhC(L2A~5!}@P5?VMcQ2rR0trO8G^O>y7m74xP}l$tbQg7lna z7oHhF!P$*)X<9|Cd5;uNY+JT$fwa_QjMOod}qr8WRxF0D}&c9Q!-KlR^pvvxd?jD)V6ne3E0(`M;x;YQ#bJ zCj^r(3W@0a&yBv`hT`Ik6h-RW(#$t6FXMUnIZKp6edocv*aYt1RNC^5fM)P4m=aE zgb9=gK@kQ)gp$?$fHoT_HE3eOn5eUjT}rVlbUG1>!}Bb%SRDrosrK zjioAOS|5sw#6-k!d|Fx>Ysw1piYi6TkP{U#*+1l1U`X6lm7N~$<7jK{nS(Wqs-T%1 z3i#$kqK4YSjF=!dJAK_-ckRpS{wk*>r8+k=+{@8KSNDd}l^1zJ%#IXJKFyCVn%1V~ zirnN-S2r^~O*JrlX(twf$`wVU_~!8P9i5{3itN}xcPG=wTGuaKP&#|rJv})oDKU}c zEudR!6_%t2`#D&?c%Y$j2~@puJQFa_1k5u57r_DJ`~z4pK+SQ;!46OD&tm&;!S)UX z7V5FJ-Gykrsu{Brc z#Og(JW=xivFk$li+eHl$IlH{EHC_GmfmLf}B7!{{9=6npj9Rw(6y%enp$`!U~v?HlJ^w!LGqolk~|YIAi4)Xe);L+5XeN@nrdrG3k9h$ z5pnr-h@~{ZpW~T;MgR4WFCT`w+v{o?gr)gOQBnR*E>1ReEjM2NKZ?BO?hcyZhBI5c!-ZXT8J@+le?EMQ4oCmIMUTvQ(jV7n3I+o9~l1f`&$1?#_Yd_Bf z+}7NTrh(qxfx)I^%SSiQ%bz%LNyn$GyN|U$fE2A+)JHTjErHsKXHFkGvoa{cV_6SBt+9Xz;q)B5%6)^0g#m5VR|r6G{O&U|@Y<=h!rxifOd z4<6XMVeN{gOBS!#ujQFlg$QJCf02>S{p&mvFwX>x5H`q!Ymiy1K$Tx!E?Ra40?Lqa zdngigO}OP)dVFIK%#0q~* zz}4V0H-Nncy@fx$0}0dLSA{=Kz%v2!Ou#kTu2zU( zfDH&)Wmd#A)YIA8P+gWCcIxBV}uXRF@ff*n&q*>2THvE zP(bfM3#~yd)$%g0!o_m208ob(OVud80WaYNICM zH6TJr9BxRzBR!Lzo`K~@JaF1XZ3*kcBM8dkBKY_qDn(x#>Cm1514R`843tYSApu=n z%o=4WPpWOHF-LWx%}vL334C zmX(d8cSkDR21r$S*vjyC+Q)b%;1&_>?>&Gi7#PWmO^C1xijNSG&qy82s2CD8LyjtR zl%Wroo0ywEv~u$D$R^;0E)-1CdP|oR#?reIZ*!lBkN^i83l|~Uqg(M+P~io650DS_ z+hq>bzrT;7=sXiJ1FGP_FJNd?N=Al=8-@Hs1d4DI0GbAW2c9vtr&7&YLp|LpbU$Gr zREeLEcX!eRkYg~(pMKEg;IN}|T&D0R5qB|JGUK?T_LM2=^fT|*4xX3+#p(D;=J z6y`1&w9=q+3J>_hRxfT5W8O0Jk*+6ccaNyMrP}+-nb+OD5;?^pzKRDymzCvy`@|u$_HOzN=oo=r0djUV z&jf6+Y2kNMCyt*m;oC`@jBQ+fz{DFB9YdcleZXB^4f?C*&7U%H;^KzNmUb)RgjgH_rsj78e%dtE>CNs5z@;P6TaD3Ou*vX6YF+kS6QO& z@)eq$y`9KnB8-XRc8UE1L!>{#P*-1d|GH(mtsO1M1(Q36MH<(0r~hv6?~lqZo-8$W zrfx?|Ydi6>)zSXWR+rAMLEjU*m#vyVZ`#x;3*%a`@S;eNu$Ni%082j41l-vYd1l$d znbV}FOx=I~sfi8a(eo$cM;{5c834+Z=xvjkIg4ik=9z$bCSU`@bO9XjoUYEYIDa>R zt68|st2+u;)h}(`drtna=5vilk;!RU*;!!fOASbEinO-Y*HBWn^fSG6P(l8{k@Gh+ z{i737(lcRSHPQab>ApM@@U4drH16KNbM1<%+T{xuA6Yng1%`&>c--BV;pyZ3_Suss zPoKSfWoT$<{N}}j=eBM>{vjlX3({PWA8l*pTVQzL7YX7M@001Dw^O>ANzBf+rI4=SM zs1iW|)b6A9J2N9ABNHQw^UcW}MF#*y4v;=9q}T!uZ!}m00xmn(-G`bqAqzhMJ(z&b za&sghZ8+@ADTd$74Qfol2|*IjMtv+Wzr+I?Z5>=LfOFR+ZUzfyKnc!RE}NkFFLHKT zV9#{cs5j$1g9CyaB4E-z?BdS4T2qJGMx1(` zFDYz)KG2P;@#b(qWACb^r7|?v>Saij>HSmkyAJ4k`ZqKqOhX9hxV*DC-q*{-RNuz7 zAkF;7Q-v+Yx|;Dk6R?h)j=Pr+zP9RE_t#JC-Jk2!ggCx9bK=0!1AEoeLamIisiO)H zpMQH{u%Sail%r=^kc08rQ^$_&yKv10T*>Os9Gu+o`8Q{U+ZN;n*u07LveLgGzkB=c zt17C`jh;O>0}uc%ua5Vz4vX=&yP@x7botbQ?b~)Rb?J?Sr?I9OshPqkV1voxD zr+iiE=<%aRzuzaTdg;{VM=#%4I)KTyv#})8BQ)d{&jd{I2X>h1LR?a8|2Vse9bDKL zlaoqkTU#e*|9FZtXn;(NVRu3S4s;HeEF}Mz2Ck-~8k{+@sE#h7N(+wP&CN6=T}n@r zg;M|ozYj%eGazhou<>&AACq=8keLs(B^^rWVb|j-b|JI>?k<}-glg9=b3cF{VDQ1G*II)FP`7vSmU=p71J z`@|^ExM*J;?PvGz8To}oB_ySE^pzQgrh3~N>%X-POv=cL@eWH0cy9Pi?dTnMpTO|w z-i&SAj7_xf+`4^N^O1>Ha$#nKsh^LF!KI7GwO!o3{LGH=Ou+1rii0#AM#bkro(UM~ z!)j!C5zYV0U{yRUdQU0*@=^q$S&J$?;(tmSBC-6n%!g+JhVN0E>22`f`o#;%XU`qm zy<_L z0uo3oUj;>KLQIW#h#-_GkfBo{~$M*x>pyJI* zi3;)e_H+f4uaj?3V4$$B3G$zR{QQ2nzpJILBqJd#$j{r|)!E6(!PVQ-1D7|q{)EeU zCg6_d>XPjE=!o#}U=Pc;Z_Uii%q^{{jH|YhFk~oVR$o~l$V8Wdx2uyqnwV{@t%-@c z1`4!`H#Y-5udFyXJs~OtWYexL&W?`d9M3P$1YBERmEmjg+Q860rmUe3XP3%yz;Kih zwJ43+@)-M<4{xfRmpyV=>1}oc>wrR~CCAh)tS#|3H!;xDP*yyBWZ#bM@~$;CtcJ3P zz8`!8rIFswrpAx%D=VHlh8Xg;{YI5sri96Bs**!}-OXM$ z)5GTHnSkxg4IXP;Q<2}lW7E1dYu2sbxN*zwLwaUrmSCl*7N(-9|FzD&8!EB~cWzv} zdiC1%8#ZmZ`kUFu`|*7&j3t*f#JwryOG>(_1AvU9J>!^h7Ju)aef#ro}Q zU9B5e6!vf3uzoG(-?U@bQT2Nd^q!S7g$m!-uO8mMc1hvzPVgYF->`Mpo_*(T+}6~6 z0?JyjR#y}|Jbt8p_57K`ySH!IylKbYefv*bR=ss!N3X1`1gKhN<@ruJx0Nr*9^SKK z`_>)1_Z>QZUggGJtw&FBi-FL#1h`p2FEBsP1dJRas#ka>;M-V!N=u8F{WsVst0~B@ zSuk_*xG|%?!GEJhkDX>DBsuPWVNH>a?t`nc`xeibG-mWSqeqj%*a>S>$^r8Qd38?$(89xTs{|!{WqsDwY)u2R>ok_&&HLg!wy&fy>SR_4e^ytxl<6eMg0+v&{ zdP@UrHUx@ybK|BZGIM8589xf2(CE?QCry^#Ca-u!?M?{*?_j_gipy5Y%$Y7VVH|1{ z$AA0nWa*_xPM^Dc<2G407;}nK)YdNgZswFp7^p#{S)bAD-p%9r@ScxfszY}aflorIZAhU%6;9$gh z^gf1$(bP$7VUU9Wl#LIcK7RTjhI9d_3lf#={fVU?KljwsRWu9$GKRgU0k8?-6#ybb z(~o?pbG>=;@Sa2R4+ZZ(kKh$(eohYccSDJ{W##tsOu#%7Fe^Pl& zR~`(DN`@#Jh_}&)<^yiYo(ULSpwp&LpT06IleGci_8RJb{XpyN)*U<(Fvz$X z8z}G&uYq+?6&Dd{G?7o!h9jg1<4<#UZ zCSY>|Bqtda<8hB{60CMS6EHH{T|MvKe;g4N#(CPm zeSGu6Svdt|RcjHK%xR>Lv;W=5k6)T{LY*z&=w4TpmzR^jqLd;hIuvkK{u~o1VF(C7@|%S(ARXoV&|r=Qc@-SK zJVFvY6EL+b@Jzt4|L&2$fBE$vU8Qk;ZniIOUXYVlIHPdYoXi`8?04{ok6%8wz} zIrumqBYuDqp7P@SY#1J6VM>UPYa<03xEwYL;9P=7-+SWGxMy#Y=GZ-Xr~ zq(E})>uk_I4-I-J6q*F-m{=~UaHMUYDX`=SSjR{%tj1*$#{i!r(i;r_pC(}Psz1cN zy^UZ3O)Y8ZL`O6QN7+ABBJb|ad2{W=!R-fBo_khyh&VaV1f0mewzjya@+O`Mm}dee zizNmtS|7;#a|IInCLD3B6&zLo?2=~!Mr{sY6xsUH-4pxF;mw`Pa^LS*IeY44sTndy z^6RPzjJ<@(dur^h156*CKCySr;+fJ?lcZjQN4GDTGedgvw^CA4J0kOm=P#WFS=)X53ft`; zTsXc-1}Hg`CQg|=WwldkVm$U20>^4^^^Pp)DgM9gy=8b**|sjcyK##XAcUYn8+WH0 z_dtMzCb$MjfZzcV;_mM5B*fj5s<>8Ms^Y=gx1Rl-d+s~tssQ_*eSX~U`JU(dvt~D` zP-CoFMb#Q}jydFgA0OSe6ja$LW08|pR5ytP(nNGLunw@W2Lu!}@=U-FO7^%K(hvTPfC}!)aRw<7N&>0+nAXcn-C8&n24O+9o9gZf=D15`wfHFY&8`YOmwPKb+%j*5&34+{w#>w@aI4O{^@;xXJb8z z@WZ`4+ye^P@{ykhILP)c$?t#t{g+R}V8f~@%}fgOa(8!hO)P+unVA94ud{pL?|(s! ze}7v`ZFyc=La;Xq(CvMR%qle%*=9-aPk;a8=lAcrMUBGZ%;YFP4>u=!TgPai;m0Sy zef&81)89bl)!!ki78GVAgm}5TIy+j~28D%1L`F8ZHVyQC`0KCldVAXI%7xiUu>l@L zg2pQvq1G zfniKU-;k3OD>Ok1P*RLodWq+oK#rln5zoO3(o|sed^Q+jiVR61A~#wY6NRcY<_QNT|sh)hqbZcy_@GwX=lzd=-85|h)_T$1o6z{%XyK=UOzX`y{2pe1j zZDC_;4>`atLGE0h=w)y6{Lv$W2X}Aj>OFY!{1wjx%rgP=Ou#%7aBW>Jh~2BIi<k z-0VzWJ~6oez~J#yL!;N`meviZw7_n`GXYbCj|~b0;!H^x-4s|v^#VPOC?F@sPYOCQ z6mZ&55&8s-E5#YUD3A&TL@C?|3Dro@1FNb3qJ?NA_G5aUaKbnZrX^KDtt57JPE0^! zBW(qkhkGj>kOM~@5)Oo&6+shF*%x_p6G;ihA9enN;*N&$qLS)X0I|~yxSr(T34Hg{ z&+mr^d%Ii81vy!nsd+V>sOiAFL!gpp0v`Hb|M>0W5Dp*krK$ya8A;L9;%aA)7FSpA zz~LdD33zY_m6-j6Uq{9JO(^s!0}Tj3I1BRgx>1^lD*XYZ80gOIpk83)5hzB_bYNO^ z2a^nc#Eea(wTTi56y_HQU|c|vrU47X{1jiO-U2LHj4hBNP%KA5a%w-|BKVx24JVn3 zj+sf5#nJ$f6b%?B*@)05+)qoYGT9867&3~pW4(K&zq zl0MG_{L+euet9NfGA-B~OTOWZjLa6SA8^0LMQo>HhDX`A5e3qq6S5Ey=n?znvU4nV zMh9S?2^gy-&jj345|$HZb5Hl;=ADQ39zD2o>xR`Um#EL3HB(h>?wkeJ-Fs?6k}^CW z+`hX1;Bk#(hkw|yZuPQ-b7!eRK4;!{_iZH2cJWc}FYoAR>_4n={J;;me3|;(Idf*u zo-=pet_Lr~68~U7$EUZ>YVO&4WcT)M>({Jax?sV)d1w}#yY$T6r{dlMABPuu7qxjN zV9G-9Ou*zkP!RzE9Z@{+bLF7aucsy|a_lK%AOc&~S8~p=Ck0|jf=WkDK@C@5Fmilg z%O%RS+o?&irQ(rmDU*LWUXZ^;z6|oAoy`VY?Gl;$^!fZjXBp;6fr?2y9II&^#JN}G zpmB#2&Ko0VOfno}0t*LCdLmo0~$TMy9ZJX0|NrYO?Bd4_=ep3WC~PL0;jLv z_Ca`1U|ZJP%C>GaB*Vwz<{)9;;P5+9ssqmi9KbUHQxOq6@p1(=pbJNh4mNml0?NzC zTSprK73VO+Y6c<^bvhy)Nnjr3xDhcpH-Whb6a^o#KDZhXvXcYzBS?X)ldMl#WWz4! zG~k9X1z6UI{EPjLyvHx*%S|wHSC1Uq|G%_>^nZTy)Rh;u;t#)c#zzG`yYtW~C^fgFthy0o^EK6}9;Uj-j-GpLA(njTO$|s$ z_OUlLv6IN1R5a(g`?CFcwW){{w6EMXM zC>lsc$u@7cOkkbBAWLGljMa(6pM6tW2sjP=g6cC27J&S0Z(nydz}{xjgU{AFk!-^DQm4=^a%z&B-5^7QE-z@Yl6&qewyw4@4T7L;3te@tUU zKJo}LxLZ2b3h_#ez9)x{FzE< zUyzksasP#dgPW(Xe{dLWhnx@CXt;Xu!s!ZVp^)FK|J)qqeO~_lLG<}k{uqO20w!mh z%39D-KruQR^wie_Nh^{gl)$0Dd@W-DVh|(9M-#9zfDt+Mg241){D|IB(JO`U(A6-3 z1^?LUz?La|S!{cy#eykIPt}NHu&R3ZLb4 z)W^5XzHHo7P4_q^z>zn|By=~hdqF&$@v4tq&&g?8ngE=s0;Z*hX96yXaDBC7>&+*(^iMBe zx9&UD6VD#s)N}O<3dOoyo$MDG=WB6mO-zL4dFeTfDe|O5E~gD2G=VpCMGs6o+50d zfd2lv>T+OvK6CDIXXvv z+2M0@r_9^9X~oIq3ey*CJHC4T>&(oY{K8_fxaj&4<#Ec>)*o5=-8yCU)pDc9OueW# zbJCLF*x0venLW(`2b9JxmXR~rE;nwRg7Tj*CN0^lFky{fcqFI`C0(|Q$9`+Re$IKB zQQv%{x=4BS#A!-X$G+ds|OrZ|dc0!C^P7yxC(Xb;WG#J~#i zCoV-v@d*|ppbS6dDcSLW3I*{jtx;@HPJmOu@qzMpoP0z~zCA-IXB7gU<#aiY0oW<< zB0Li?&jd`7^ml*$^Wb1@yo(*(9PC7ig%(>8DtdZZto`FJKYi+a>kO)HDA2Kx9%zHY zJ~coyeEj3rp;RX;c5||$=NAPDDZKmn=MOv+a0N^JbM^WZ_k(c*&;Xs~FmN~nJQFZh zB%TRa)CwpO7MvzXkv367M^Rj9q?N|%bFXmAKuCK47h`5S1(5J_BT9lV966!e#tO_} zdt{-c!}MA@nvxUVK0c{=9#_(nWW!&=;3&eBwCCk{-adVXyMnA73ewl0N!shHYvc7# zonRN!1oRU43;mt_X>4sRdaS8I@0(pl8YHEGaC%pJOMR7-#!=eeNaxEyXbVqtwKvxZ z-W=aGQi3xg=VIu1w!OI_EAIPsI1f`WjJ83xMbh~gA$A;(TN-k1ZF@z3r!pJ%$o+-c z84RtyGB>vnqLIN#DSVGX8*ELbuo%&L&N`VE{S9Jlpj05kGXYmu(_JC14YhYQy{UcT z#P&7wm!EVJ01laY7>Fc`#7!xlFO8nvID7iQp*_pysLb1EnpRR+Tq-OF2OMr2o(Z@Z zbe#s*D2OK&4x z)JEn)#tY{T@#)s)hWZqvo5#<6H&s^QKo=8|aUxPeL1B>3;3)dW=)C66nXlGq~a>dC6no`iD+ij&Jop@cF0GA>kz8d}<$MK!`w zVRd~iL4bg)g=YfB=fyJtizT39>+5Q%uPjWC4h;Z8myegHm9d$HrA>WC>0QvIs_b_^iT0l!1AOtkF zw6%i~zPr7?N|^mNHZm+EG{no)*vuSIVRpD@>qXeZ;YjjKz_cr4U|(hh#$>{XInpy^ z;H+c{pBHW2&_D$;6r`7s{i9$3-v(;XDNacWoSd$};F*B!p5M_qcIf*JYgVtqShMkV zPDWZP&?{_M@L0r?X9g&jEsu$uz!5xA}-&!nxrdNt=_QL3WiWwRZ~|T6X@b-W2krI z?C~EquUo!+`3lI_Y~1tG(a{-~*VV+j^Gv`3VUEwEiyHg)?>%_p$4ghP-n^qvP$Gx} zgGv+w)E&jyvEDYOrZx`NCNH017`}LA43=R|p|rTTAU`iBGc`FrBGAj#(cad^#>UnT zJf=*e1lI%FJ3Ava`E5d6RA_*YmzSrfhX+kf3g9CpR8Iu|(^8U?lM-S>gM-jw7C@78 z^5PQq0>B?eo*^|QJ}M$CEHng6wk(jxEVi<;vYNc=Y7T-+0Gd^jbGTZt)N1^VtQh+< z>o69SmDSZ?}#p?4+?{#)EBl+ysSN zRkb7+aIamUy?MhbRTTxu#)8Rr%-FHAJQMKDo3BmGkmRT=(^!}xOUY7Rn_Se zMveM*6y&2uPnaaP>*Ceh`e0KoE30(bw0gDrd^HvMvEM>Ymrt59i)R9MbabF)h1iG- z8Kg*FZdL~D9~h%ifqrma$c;XR2%p?`0{- z=4Rp{exGf5_uR2P-|tO?UV4P0LoSoIO`fO?8&)tl2Y8C8nbUSs)|| z<_~Spbv5^H{7!w*0-gz&rQ<1%$1?#_Ie$ME7qm!7>4+JxDJ>rKhs8}G@9mXx71IN! zLDdLUnb6%u*K<6_QYEHlRzUm=0rZrEUyLIpUd-wTmdhhZ$uuvAaT! zkWR;E@w<;*mDPgUo_Ft{LN7v5pU!sa(l`8}*6G}i&8s%<)6E?EFhFuxFoI@~Bi;0V zFz&*xJ?j^!tEo(zcRjIph$D`G4HMJu(EBFq6Z`hBTDt(&HFc_j^0D$^#?}lArgigO zx9vskA2x4VwnSB3RY_4%L4H+GH^qq2Lqc!_Y&&^9$<|1>{m|HO$rqCsNL!0CFJ09kiGR5=BfEv9BaHa|HY zQZpT(*P{0!>0@7LS0}E6eX{ZH!-o&=2c^ynY2lyOPWH#f`%m3u|Gn?uOJ9Vo53N%1 z@W)yQtu5=nTl>R}jKN_7bfeOEq?tJR`+-=U?LVxUGjG;31+^>j;(m%7GoU~=`+Jf3 zvE6%?ubewmWrm`njM5R|AOW@!88p52aJS8cGrKmfU#zaOV1~Scf}HGf|4!CKQe92* z_gUuGH1=#>w{hMK_4)D&iqM!BSC1nbPB@rK9g!R48NrItfB-Oz3rb}KX>NT zk-fWiAJn*DXzT0^d2}3h37VlY$l2=oO`X$P8i>#x(Y|Nl;O-k79vKsd+Z}STB+t>< z;Kl`QZH5tU@bdKlf*xpc*; zMa*6v$2eyHy+8f(aY$U3n-b;m;u`G#q8F8&Feou5|M2_ozrE`yPm2q6ethw?#_4l6 z)5TPljbty`fA6oq{qbvmQ%O=(fbGNcr!`KV)v<2G`b!=@US2Zz>o0%#dtXCdT!^3L zy>ll|XlQ8N%OpA!kQqQMdH0t;{_!8ZwW;9&UaxOyYiOL%(7YOe04TvcVtSqlxVO6| zFEz^5*x=5s^T+n?Kcb;?_o%eS z^Gv|I*DYH-Lw?F6S*5AdmYbIgFiHUeAnvVft?u~fd1l9oMY9#AOq?jID6gb`10~)# z=H}p2?JFrtYpgLnv}vL04B3g}C&((uDa=0=imV?TWRe%9WTXce?q4xSMM-ATxbZS_ zvhwo}xe`%ka40Y=ddm}|EKLl)U#y}eHw8JADRS~t=N+&Gm8OrcFE+=XreMp5*G{aN zGZh#Eg@-R+uP%ehw7&8!2O%n zE>TsMSCE}7t1|n$-RE!WKY3+pVQ=3FFAe2=9nL#eFPWpFJWWwiZQ-i@7j8dzY-nU^ z<>1&31u6jSh`GFP&z{|D)t7D9d*R-rCx)+Hn_1dBI79UU1OiY{x~hA;@G3!ovj0RIA!C`Iv; zlM}%+`i6E%R`rCkCe&|H%%`fnq#!pdGb1xKDLIAS8Wb=E1uu9eV0?J#g-x{gq6n)F zTPSWoT6QFofp>#LJ*`bV6EM#N%rgNabcDe(0asT^u?@OHY&|VKpFeu4vw7>r`HQqN zTTzchtf`&Uyd>^Pv&oD1bT)hW;>y~Eb5-UnIxc8JWdoDLvyn6f1(YVnJKDUsc7B_> z>a^*z)Gdkh8BmKTpY87MDfJAmO7%1`eQ;4@{ru@u73Eb&2STsXHUP(@Iv29`k^@)um-cVpx?FYoR3+8@Nlhpn`Yh*}fS(!Y=p0p7ouw=@apHu@vWm0T z=)E*Cx3sdR9)Z@5y!)56_HUT0GEHvMgz@7i%PP;>di%k1xX4xvajLDw=BoCAjf+)q zM1aC%1(o>+Z`^@`IpheFw00KWJHBhpyqVKLlRQOMZpOS-XRhepdkU^Tv<$R?OtdrZ z-1ZGi=Bg+wA;CFo>6#-Kuj?6r%GU%H`3Uo%>_1#{^QM(cmMr^j`MMoD4qv!_=f1(S z7cY$%%OfS^TeDvCOu(!dkI=tq`!7MoVAdGo&twC*V}J#szFx+%N%glhM#RvefE88q z;S54Gf}Dc%YHkWrpb4ZUfOP%Wa;ouThZwvBRRGfroLu@Y=t0CY0rO12t`-(H4v6sw zg`#%}E|_Gvzf)9}la0!?pa5@Quh)iVHjZxI{sI1wcfduH^tRWQ1b}KEXhfZ0n@jy zkB^V9pTEC<06}K8K>_I=tW@8N~1nURYaWXmW%vIq35}@F>q!7&l3=AlNf#e;yv_g^$_M zSPg!dbP)6S`FeTBM@!-#Y z{f~eA`tieXFOp^Tb>)JR?CiMU08e*US64^JxSZji|NI|+|LxPe{+6az>;!`RysWgi z5MLyzT;KRuw6}8#iX48&GXbLwW_W0Tnm&Ny+gMjsR?2|(2xl)xg7zAo z3HV>_pB5QLy#h2MNVpO!?r~(5z+oWirV8oag#7N4fnmXqi<@Rm;p;Lxd(N}EkPsE@8y*aZ5nn$)3J(Sbg@i>=BMHO+2SU*aDsZZVh!7?N zec>&TdV$qT%`+rtHTOjEheLj8VIBkO1xjxkx2MsqO1l-$1pI%=1jsW1x4-*cT%B8r z)JbzIuxc=Xq&YD3?qhdhMv${-K;J+A*YD!o*!Y67s@ewN-N0dz^bdXbX|N_W%*n;c zxA$-V_2;g(#>_Vf`6V?CO)YKW-T^fJcGu*`*jwA#dG){l&%gIJb&G3-`B^3Pr46l} zJ@5J@O~S&QAZvRY8;^nE-~KUBQQzC!-O^Ck&`2c9qWa>}+~g=vZ0L4geZwC<{xZ}* zI51pa-BM9q-c&0rtj)=bAQ%f<3s*l0IF0*0bPaX43agu|OHkJspOBW880hKeXJ_H! z6Cmjw8h-cjr@_wJqK?Y^qO#nygxHjLXNN#PJ8M^@0C*-~R5+50z-nqymd-N)Qvo2u zQskL{c_v_Z&e+0{-m5Q33G;G%ee;~omeZ}UvRFkY(TM-4sC z1k8on=*U6~9*g7iOu!UE1WdueyTXK|7`w2wuVu5ElmGc)W$O)I5Z+AAti%0Gw$9*YzZmrrs7EdsHkXAY$c{-Was8ne2=?*8IU*m=#90M z!z(H(Dh7fcoPYYC!h9?_4j6A>s-sR7?Rqqjs-gn|chl01%N$su+2yKb+3A1<2}y{- zJY1d$m`b;)`;q%@zWzxo3I$A27xyo6+I1!UwDXbzcGuQ+XpCG*dA^>Wj`#to0?E5R zU(cjsK!@GAaJP$P0PzB?Plm6_a zJF|sA{*i*iAk&sEFi@lY50QKV*g+*bpsXwDZvH$6r3)|tMf(2@(=S_QcqU+OJ?5E! zU*r@N6qb}qI%_hbyqumJr3KkP)jWRWhhvAfth?*&z%v2!Ouz^*=5V!nEV?K{hYre! z%TO9XpupUK1A;{dz7Mq^PzsV&BD3m&BJ`N%L&)59IPB6#xZX6dc~XfC^K6(uk6m|U zK-_`d6`O4H7te=tZUG!B*zkNnf~)bbLzI$T%`*Y>Ou!a; zw{G6Nb^ETKp8oxZcdy=lX<_H&>fuGVb60b5NurgJtDO;`LCi5Mt*mSvoLzY);FcEV zUBkhKk}j%pXwmOOarz7hn=1jtDUCbFNxDR2<^vrm z-zW93>zTVSV*e7c$BDb0{R4Ja?>qrSA1z?)d`I?QBW&y&sx#lH7|~+-LNw4%>jUhc z{5wgnxVH9{T|-N1=(9yrH_X=?>cwf0Eq^={u%sh)i=xxZ6%)T1H+zxF=y78J!#{TN zjCo6z?KZJ@^A<~54VO*Wwo~O#f0DiZ-RN(=0Y?A03Gx@WDJqZMXJ_vUgan6q<3~+a zoiSyH$Fi}Ie>-N>gxLm%r%ai!)WX^x%&?ia7k<0LVEyE+R!hf0K6b+BDe{Y!Paday z(!|2LOWYQ^cls#PTS|XYIxuVW=t+~tjvFI4Ws>Ur%{mWXm|Av8YF^D6{mu7l#{KD= zIck$8Do^_Mo6$-DL)v(OX95P_2Rkow`#-n+^Gv|`Nr_4EnMK7Vh>;6~sGs=#zdnfs zHKG<#Giqqs>dNwxVnW;#GqZE^0EOSx)%%y98>)oGmEa-k>}sp+>}W_%Opl3*OG-(H zPIq5tWLbMzPU4&7tg3cNZ%1RBxG6g~CDh#gO+rF)T7R?FUau&32OAqZXYZJzwgH|A zn4pb$CSYuS4b{RDxXEp89o#mCosaxs79%9*yRN3ZjLFetOnxwv!0)C3F}MgT%h_KU z5h+E~@jT)W3|WD?>CtOX&v3Cc+HMK_muCWg_~c~>0l^m(l?V{N<(YtaCSd6~RC*XD zdSjjmnB_3YC+8e=TAZl<21^e@m|%Nlh#`=3T&(2XOQc>m75Ff~klGqt4)0#v)7@Gt zEG=v3pf)L103^i=fydv~CF#fx1%j4_*4f)m8BLuOA7#@6PK0LyHa0S~3rxvJ<92RV zTGE>Ui)YWzYMk4=V%GF&7c8vyoO@*B8d+FH&FBpvKNA*b2b-QheD=rni>Jz}KQys* zi%dx2nSi-468p(B0V9J!panz}Rr3wQ@&VZz27&enrH@E%085}CKaZYej8PnKRCyV| zd!S0$4+I+X^N6pr4458_&lgOA3_^%05YTWzJ^==ixSII?*g%kyLjVHc0nY?X6se8< z|M>GCzkGZ*)Z11o%u9`n4EFc&^zuk70nP`+4NV{a{+B;~{`7u8(pn?TO^6B)^7r+0 zcMHh{iUZFC+}zy8GXdi+!H$Xmf4Q(2cTGlGVoW4pNP+@UCRWAS9b*3gz5_ELvYT1y zX-PmbiVP1Ugb+dp!2l0vWhM7m0h475LMd4-_F*7@(GOw?C1ud~LIFzyG5r#}2rQpf ztkDAl8<7(I@=U-~$5)e^ogC@wX#DWznWNh`fUpZhz8f^MDnNA(`>(Al%PB01^|Uk5 zzj*S{_n_(}BHs<$9LqrcN}p_9Nxo2!;LkGw|ERHJ-O3f;EnB`~^~TM6uin1*_*n&a z%LbYn-M?``OJnyY@F1^Pxo-2;?I$naxNGnPl(o25D+Ml(9$r0v>get*8`iE_y>Z+2 z9S6={ynct6NQJo5%PWf99^5#0`ta_p8#k=mxMlnI`%mdyzNPo@DZ9yOTNH#DV*Y)* zw(r=n^N0OMPwQO01tQaDv}e+02)mp(AKcmDA9 zc~hs19Wx3{!lOom3HQ6S3c!3pUQ-u)&)W6wzD-NyCX5|Bdepb0M~xaic9Oy?VP0-F zEw^=^Pdxn|X>Ob=4<_O<-;Sc|$BdtR3}{k`RpeUPS^7pAA6Y(KX3V%zxE6m#jTt*} zUF4hC7n*1aZf5TM9 zj-9Cb#MRlQtfH#AP;925@nOEMc2;I4uU{J*zvg&++4Xru1Wwpr331_p-fk|=0Ak^pfQLav`~LkqBBaH@ z--a1AG3fCq`2qaMVIlx!}*Q{8+Zuhw-mX4mm(ecUY*{~@l9~$iI>1!*D^6?0K z^EM$SI_52_?=37WE|$s}Mh~bNo9ip#by4;QJ2stmNlp~j{e1{jvHCnj*5UGUo@{j$ z-6}+NJum=jH-w|8G6ZpSD)D8$G0E5fO(jcHv(gh5q8BwlWkgJ5wUpk*Ekhah|d#pMC335l`=B<2oox@^fVZh4m~hk#6^Lk!MJycr;`SWSj{XnC|?0NCjc4; z`;A>pGtrBUT+cHB^Gv`?mdsO|Ieq%{X;T%Ims~V+_79JJ^EQ!g&Az^&!lx$|E(H^j zipsRqCx@#L&Bru5|EbQ+z}Rw;sKee){sT+2(b6a#Yzd?!2+suE*&}td+D4c_hX%yWrEkN% zObxF6c=@_3&`sfpksrl6Ja{JHhN^-ob{1CTsJr)vFQ|+RtmiCoZ7Z#shYB!jEvHp1y}2- z+Z+rMbpJOO7qtxq-aNc<#j;sSvLM1zoVhcup7N~ac_v_<3Aphy(=T}d5}pY-GT7$yf&E+NC@IRx$tkKXH}dfG_6-OE%4J7KcZ|`? zyLwmFF5j?HMNS@Eom15|8iQ#MRQKdyc6Q}GzI8=&=aPks)MO`vD^zyc?Ab|0baua3z-cQ4T-6ni8DUTROOuC1@Z^@O2>IVi?K8k|D2h~f~C zSCr#9P7W1HE_Su!HJb2FYij_gO7anLU$00Y$Vx43=>%gVK`hnRf=hcuF6k{WJGFh| zidFlrx>R(EnEW%7FPv;~$n|4KcCBB#N`1}(o(Wi1dEp}$H(=cc0tpEg9N-x1?Qm=J zlKC^`Cr_R@Syq1L()$*c*0zo=Xe$E}ZaB{b+zO1124MJSClV8&pRccvHw_B2qbCMr zjiN@B*rMpGATt?6q0v#15#eDW!ASGdXaQ%92={z7@$ltm5mRVAT2dH2?hy+W6^#T>oVjq92+UN2eQDd zj5NqNDK3XeGX7>)Vsavbr8q2>r?w`d zb_L}tzDojvD#f=?2>jjMJwqP``#YMdgt__EEh2oC)%Y3&rG)s@+6mx-pMU>p2uxm8 z`DqcMskQYWpTo-wh>5TT;QT{>{Q1`pL)|U)!p!tgo(Y&|0?t91ehSL{kfq12Ol}Tt z(k9x#t7riL7bv(y($Z2J8o}i%-IsCOVi90+K`GJ*IT`6`ShX4&pn;+#>Joz-_ib63 zK!E-m^a5m}-;Ns~Gi73+m$4olsF0U1B8Cz*4zZ2^ARuxs1mx1I*)&W6wNoH0Widv& zp2^u7NWZZeSf3ELN-;T^Ag!a+`X^PWuB7M}xhcd{i=IOasL*?$H%Va-_9mrr62k}N z6zENIy-0YI0=y`@nyC;@J8Yi@`9Wm=m|rTVrIZqCHG6sAP^IJ`8xfBWjCi{~$0Jbm_> z-V-AWBng-t===GR9yX?i270=>H*Z|kIe%U6k>P6#8(VwG8If;8d7_uS$@52#3?AIQ zrK|Vg$@5ph7_qS%k>fkBEXqoZ4)SzB`7O@`jCLTh4+ozvwjUhJ(_K$@U_D|O{IkB~a zp0J}8J3PGxTOS0aZP?zS0Q>J~6(k1vg(envfawPR0LH~u+KMS5Z*CXG=x*DvXx?n~ zwRhe&HsW$DlCXbzNxX&jf(Hkd&Q+NzBRg@TjM|M-(TJQ~-qfCX^~la8%a9(JFlOS! z39<*W>hb9d07gsl=2qdu(^n5JQ=6`+A~$~Agz-~kH-;CXWE?f)ByVbRe5QB);8K-o zic{q#OaKax!bBzGjN~LRQPInHm%X%cesE~*cQa&VCypC8cHFqhGNZS}L`KKN#zI`` z_`)b4;=w_+`BNuM88>DOee_=%%c zURdIQ?q1(4ysErT-%NMCx}x&r@n}vMJ9^BxNmIrydGgZC(#F2Np*cZw%dzA8*Up(b zMPbUM@nc7i9XC-RGBqr z{^Cu0kDtAA`@v(w*JdQIMcS)6&G69HwJTSyUBC5*BPX>kT+`Jzc>LVR1czOo37BR0 zc_!dNaYsXWQAu@cH=&Xt#Yc+=2nGiR-~IISdywIFx0DNVvNBWiYC0vTC&$_$YUt@5 z9Q^eAZ=Z%x0^ZSFUsqO=mmVMUwx|KC9g4tff#Ltl-~agK{a{~jx46BbwxT#MB`Pey zFEp8F0(NwE_XWei-~aaO$9{1;;I{=u#aVG7ULI~PPWE8w1 z7Yp*zW5a{I-922L9Gx5;+l@w&Ayorto@b>U<#~g0neu3x> zd5^XZaZ_D|u%skEBRw%TDm*yQ-`Cw80g#|j(2P+J$pBs-9kL}wIGraY#z%(*2Zx16 zPz@s#xITgY?#|9u;`uFxPHHN+fRd7ullxGkh$aG_2^c2B6%Sz(#X*`PyW|m2gb-yK zBN0HdE9mh|!1o;EOXw(u`j>W5ZE1RPd~|elsE328iIKsr%Q`ye&tKAaDJU)O?ZrY= zUy_lQ6dC&_)WgZt^yPyaSI(b1t9|arA77+{b3`N&*Ay0H$45kjdwAHIytr?0<(!Vz znKNg!w6t83d)w>!yJ|}FQv>~i+&$c_jbA)5xOMTzGvL}ic~a9jqNk^`x2wJ&^R0=s zw~L>*orTepdp9nh)zmn7@`T1I;{dFG{rye38A+bLL4YQ(u{Achb@kjS&Ev;4G*13# z;n6ED9uPN{X2pkidinUd*t~jt?*?97Q{y<#1dQb&g@WI>`EkpmMo$Fy7k;Op2b)E^BX9Dge z7kqdScc3W0qO2%AG19};H{8<>5eyU|BdgpuM5Cv>T~t$^8|Lor6A~5V;qDUjh~WiLiir~36d-~Uczkhdvz}W3>2+}_C~6-@ z42dFeD6k+8fsHPpQ@7Lgl%&TLLu@ceUi%o9N!Ou$I$0KL7V zQRMD;6$Tv~U00V&P8(+;x~R;VK_=G1H)WUi+qp(xSiNw8f~>64jarmIRN{OH{AIcxvk&HJ z>)2?moUJU69>MjoIR!e4z(rj`PqM9PCOUFqZv^D^SOk7#LXuac8cJx zmp=W~1I@@Y0ha>{q6kbyZ2jmK_lHc~WN~EK?CFYf^73m!k~2{vnwpxCox{l`9XeMZ zUfVodNlr#qR!+^(HvkY@Z{iaYlbBrGTXyu7--A_(N^( z;?&(!Womxm2+st}GXWzOK8alAvQ_DgwJw1Vql>_PY`q@Jx%vGCO~#6T}={N21wp5=9z$bCSX0Q@W@!)kJWE| zOtUk5?JVQ`oh`2DY~Q$5=iKF2);tq%HqQjyf|UdR(>BL50qdPPbLH%Y9j8ug)_r{C zwoh1eY+O8;Ix{1(+LPSeUg@7d_{7Kb%J-U@>vtSKcg@K+C_FkApIl>FWNvYc`>QkC zw%b}i)8D#z*T$9FXRg_K`UZwYVny!A46WdqfO#fh>E=(_%5F4?Dd~v25DcX zL?{6Sx8-xi1bB)KJdH{LbiAa?cJbJ6 z&DYO4FEi?!Z&Vj4kDfS9X^Py$QS&W4{6oSb#Jwdu$6PHwsiykvpT;aavTx@2@zdmG zlx9p&cw*<|9TY0Y*<$tlJ1=)CjUKDC?A+2R<3JQJZoJHd?;cx#y3}9HGXaa6bQO1= zQu;<|$DV`7PH1YL);@P|?e3cfhQ?;r4scyNJ4zgV?#@|!_u&n67Tmjk@7{xlkDk9Y zF|z_!IQh4o9o-!@Y3YeUUhW>AZVomUX69Bl_Rek|C_*MVTH{guS0~Iv_3>Mj9|!vR z`1%C|hlHXCnQ%RE9&T?%V@geVNkLwAIywX2#KpzEiH}c6NZ_i~iS@b}ohv8`5Eh}c zFFg%NgKRKo9sWBCb9g4;?$(xy!fc)ixaXJm;-*efoT-VsZ)I&mQ*&Dw4c_>VDVW4MCg^lyKudV%Z^yq#tqc{^4JSI^Q>kI@sH{qlAT)bzF*t9;_pXgClJSEXNGAX+%naO2jh(Q<)0S1dMh+Kuplh(biH^mKG51ACx3) zZbyM2MQ)INwZ`y{n6_Z>KN?9{ny7f><|8u=`TGz9AYX4!ucQi+GbX^+KmX;AUw``WuD`3XA}cl_GRWWC z!_%+04E%49^Gv`!KjXt0=|C-LJ!+UmXTjq)ghJ#>wIQy)v7Y19K zy?U&BPV>l~?He~7_pGa9Fde1L{K3T=>+f!1^6<_%%|rWkZdkv5$LlI8!V#1(d0lmC zWT3Z|>C+oppz7VRe(jodTaD1F2`qIse|<@2Qdp?1@xvSEj_=;Qaoy@wYu0XesUbLa zvVWcl7)|}A4{l%9IlODrs_&LA{cgp|)f=`Pe)Q7BoG7Sk$^slMO&;l8KYw`V`c*5I zEnB{P<=Rc#bnZWT{)!R()}+~6ni}X`=9z%AGg6b^Cd5UB2Kab+d19Gl9K_U2R8j)y ze;~tT0z51^DIqpAI4Ce6Ab@C!(Mm)eNhL%#3Waov4yMFMMTCJX9QQACHD?wRV9pFG|plOK)-R;!B#DM@|0z5-fJV-U5 z^OZt5K*Ok=2|wx^iKnDNgf=Va5=1EMuZ-kuG_+I%>uDZ4qWvU9i2AuYG*O|^>a$!_ zY`jxdcEWgo+{mobPX}IFHK7=lfoPX5Z%R6-sxnP};@ENHfnzg%u{p5yu{@LsxyeOu z_b*$dsvbZdfs0URIWvhQT%b?YCe;mQ}w3Ds5oB zm6c~|Y+So)fvW2C38O}RI|}kqqbE#~+ja5kZGC*kWo4Bvn^vz@pRcAOKNk53%rF`_ zL$i))pTDk)udocDc-K~~p07G{n%soZ;7T4dX2KL1`Sr&&f4p)NU!g!K5N2trFIH7i zlASny0#KGFO_Gscu;<9hvzKqcG{`svX;;3RJ40D+3dZCqvI@%cw;wo8Y~KPfhC+^t zOUpg;XHHX6R8X2aede6+b{*0i3@IskW{$e zL8^)%GnENpbhGC;;a3`ch$Pbt-CZ=ll;M}tVz1WUKiJE7Q!zi!1UwLI{80PA;UBhb zUc7Lws>)2&IkQ!?u&xt679eguh+WKI{kU)6?lsF7&Q?`bnW3sWYu2h{7#;@6`)waT zIJ0fXmSs!ltE#EaoH=Wj+N_O9$WN9Egh1JO|H<{5?(XB8maSMhd#;+A>MYe+vuB)2 zOh*Z_Ku8qKAKISlYVO_mo%*5$bLY%eo28~UbE#!iQg%U6sepZHJQFb33V9}A#yJYs zQ7U@JA7=mTIihQo4vtNj)x|UWha6nTBT_sv1xXK95LzCvV3%=2aW_ z>1Gan0P`gN8NhbwAUP6R?+4>9?Ao(_fx4Q?w0YMPdxwZyl@T`Ma)_I(c_v_<2^cp# z?l>g>SQwqi#A~WYXtZ&^kVVifOGW&YASC48dUm^$5@#@|py6hqI(-7Q9bx*U{45z1 zN1k5u5!#F`n zLLl+d<0($nunyErV2}xsb&~ZGoGrtVEfbQ64QzXyd4lKyWfg1|1gS>EWXH_@yE<__ zS6&A&`r7|4@4|ava$xl|eqJA_&W>ytCd)Gc1F@wWg(p%bU)EWG?YO->Ez-mM$*n5} zQ9Ki{j#*A#US57d0WJC6aArisq4qBy>zzMw@ZjOYhmM>#PDv*sv1}&q?%}j1_Mvm@YsL=}}AY~LlVdF#xOJ^(0S&$nS78)KI5gCR0YgFkayhY-Yil-QQ z2G0bH6^cGB@={o%(PyS%d`uY3RUr*dfr>G(pb^Lz6N-V?(B0FA;xgtap<08Q6qyju zbT(JyB!qf-Mpuz~%FXEZ75&imo)JfDgbDop`yq*_BqP?t z=%&u4D^4w9yg$00F@cJEfBNO)khm^4CCcN)HBHS^7rlTTLGl(({^9rEc_v_<2^hFt zdGX;CdH3`4#|Q`t4k3RX_YxjZgiB1|#o4KE6XN6Fym=EH6%!LHH3QTf5lUz+fV~8T zxhzPZmhv_+F^Ox(qg$P*V*w(Hz__3orNG%)$OfdQQEx8>mFB>qM{OJ<$l#fPv2IY| zv()~3+HxX2?w|T$-IDpUW^J@->%|HK5K$bn*z&+L0jCET?q4xSMM-87>i^|r<>w!A z_4W%03=YNa)mxqzWocsY{bCjL5F>{&1t|XW4%j-od;9qM;?UUB6m0qM+KDxDrpitN zPr%qIa&prb95yz$bN2N15u-P>JN%KZ_U=^+6eo`#KX%MG1lUyt{S2WdDtg6!Znv4~T;DbBRlcTOF`G0Gx|BBdGQV8h|`UUIScVquFN4_DI@Q zsaF6{Qh+jKX+0=%+c7p_3G;O52t>pgOo1k%mfev74Q#BKp_vvlu14_728}v#Qt0kv zGvLiqVz8QR@OTj#1T_W?5DtPk4X;3{LAq*Wbm84Kf?S(t0@gl#HLe6?@A-L>p8mn% zcYpbhfBrvz{WL78FN)`xfbZVZdkT&qH#q45L5MX2#FLUp9Xb9crZ$d_?k+qNFaeB$ z1*f<$pUiH=9|x)rTPFY?f^~w@`gOIO{EG&a0MkN43M3z~eb_O4V&?amujh;X9CXRnSkqZ zlCpA=!#wS6Y=~nRJ$mTHlLF$f4GkI zpy(Mz$ho7l-9NUtNAURQwxyuTMj4BotfIO}WH`c5(FjxEco+~+(3tgP-}=Qf73C&R znJmjQ0pB%v^6aINu?c9_7?B)))aRw<7N&>0+nAXcn-C8&n24OWFwVZ1F5X3+!Ar>J-GH4m%XWNe2fhRiOa$b4-7n3MtT9$kst(0vwT3T83u= z78YkFNBMcUIoaDfMgt8$9vf8K$HAZe_WQ@7{ti*KpfDpL#LL~)+0n{2C@d@@GP1d~ zX`uJRUw?hq+tXfGF3e7f4e%f$FFR+@Y=wk{fgw`dHSpU{!#(1bin9E)HX9A|uQ$UGg|7mY;ZmBFtjr8=g zdVKc^7{2tAO9~L;$XC9TuiC(e3jrlz18oaAlo5=ncl zFf%;J#n$kiu8!8JQ>TvU#}kf3JYkgz>v|+@6-6HaHi6`9pjf@S3L7sq zrf}^5m?R|`GE~$fRL3^ODm1>Jv8A}U5YZzX57N^pi^c|>B(On4jn3s@3E-K4>9EE# z0rO12WInaka7Rmua#LeN{Jh*GkXF$@!Q8CQ1QZ-s21d9Bt`psx;fj~J2=?8dIt^E)2040Ph`vr)DmPP~u$waZ76e2=| zBnH*@byJNWl3(!mnqcv;bf^fBAw%Mf1{KOM9OGun&%xSR8y$;KvDzBQi93@WkrAz= zT^{8ER13q(5D+Nh(uD}pQ|%26=t|QAq{Iyl#0*%1$wk1e z!nMtU%#_5~Fkc%B!{>%CJ#yM4go2D%Ljxt4#P#L*$q5Nj!9K3e=0?v9?&ujulo3t} zK4TI$R|>L{;@?EYgt$9f89md#a_Pdwdpr~HNiDsXmOZ`oZSA#1i7_EwZcY{^rcdu& z(>bdJo?=bSGglwHwCd^UYZX=I131CM+0NAJ)x%rYuAD!6R$J@r*(>*+o7v#Y?Q1Q| zNeJ|Cv9>lddTMaz=8bEY&R@82>E`|CCf4@!{q{5!#QD0}TUl5bKY#Y%?(N$*ZtC8> z_xPo$wY?K}dvw)g#|9wmVQpz<^y0bUD^qiGYhd_!c>B@)gZrx+G=kMY*vm|L8ygjY z6hLrjDB>ScF?3hLE^$jEZ-sBXij-1Ql9G}Vi71?B0!BHWSc0lOsoBy4GgxMYoj968 z(hC+wvfIyEp!sF(3>|Hh73>`V{R%0t!6w35M?E$?6L2rj1S}5Kzj5x!p6}7cxM|~t z)vHzmM_~D?jR())GI(Z$`$*Ci`{>-^J-c@9+_7cr_Dvf$Y+S#7+d-|XcODuVTd)K% z&jc*pn7LX)RxE@pKPBOLCSWW&v;{~PAKWjrVxf?!Lp&U-X&uBlAY20mtzVoshF`>z z;TR)#fADP39`kwqVuet&p>z+V7y_n2+*Bu~hC`Z%jc&F+;*x&b2jM}1ZCP(C+q$W! z8B?-B?oJ=denhDbUV(u;6EM#NEZtjK1x{--^YFQlUsZV-dF#aD!Mt`>OaQmLiLz*j z2LMQe?pSs`+(C@QM6S4+5WjYC;FV)yA9VoaV9w( zJvasWpy-@LpJZo?{$(@!|I_|imnQWOwD$%dvUYHgG*wHe$@238z@5l&0P4JVz{Am@ zCCcfIu(byi(QT+Dr*#_B)2`L_*7%~iqhmu;XSTmhSUI@E>KmI|@nu7dJ&$Jser$|7 zKv(Y5;+cSn*o!5>nc>pupJxKjKVzQ9?6<#<+gh3ZBZ$(BHq4f`imeJygGtUI4v-41 zgI!cAg{p$q`s4F&Tl{zXXF!zDXfN7&^pP=2=)WuvGzHDTc+SLbi=MuIEomw5Ar|_t z?4KGs5J0Iowb0hgsQK}>yHO2&-6-L118XhJpM-54bs-lwFEcPP;+cREOX_TlGC02b zhmA|ttXQJ1u0C)6(ha*bujoB_VPXX-&jbu_m3oQLhlIVKwr*g6vUjy0$Z24gRGZqeZ+~86L zEW*MG*-xo|OV?7Gq5Uh7^jGdRSe60X;1@Ytn`oz&u1#>UF~f))J~&2a4gdg{0%mU~ zubGpRhfcBjUMFMoZk)EccSs8&$w?~dX_d6q`2Tp+6yZv#oVo-*iw8lMmE(Qm!1q?2 z6810Tz+50_SMyB3uU5~Qp)eUJdy}TDHgWI_2#ttAkr92q^Z|>-qL)i%smV>AJZZAb z4kKF+KWd_njpO9lbUHiRqV}uJlp_?reJ`xt{6fOQBcmZ_E*2ao6gvwZtePq(FC(Y< z(#|U=6a`8=6EIoZmqB+u9Zp#gfEsalCg7195Vs&4`8yZxcCp+89O_m|LP~E>9N?S8 z<;eyM7v1gd?MBZw$zkDA`v-;(86b{^dZtd*$zuEihz^Jn2&G%+- z3&96>9oz?ZCk7-T!68_X1cyK%?(XjH?w*c!+>`EfJlM>@*n4mHxBJz(9pJs)-=DjG z-7`Zcbe(&;x%bpLb*jFq50yB4gGtT~Rg`Vt+?f1y_rh6QHD4H8**Um)2ZqP6=gW2A z+}nasuRuRA$#j<;)W&Ym-ov;VdnKhq68^%J%9tZ3|ype*$`-OG?5v>%yAOH)W@p8HTGAMH`H_H`WF*~fQ0A+ zKtwhhpyA)-bXedH5$%;%8pzGwAia{_=S~sid_eWVDD4hTP7=r{riRu|m;q6DYx1F3 zF_S|?QaYtWAS#N8YnOD{X=*)T_XOXM8@HoHSX?LRYAXshDX7R4LkC7WD&BESBPwq{I8c?s&Omz6{RYh(2vsa$k+H#4@ky<=v0L;dJ;ck|ai zS**@(yQn!g(oE&-9jjmmqh}WtfpIx|%q!OH>4S*4xWv>W-DdVeYx#)DMp}y} zkC&OTZ}AxOBEY!?*x=hml86&?WmgTj;<}IrfS5BC=aJwAO1Uz=^$mND67H#b< zDYq7Xw^MuLm~F<(hyVTW-whi%WUTCxm1BlaQG8=)(k5#1J235gy&IGMHtF!JAwx!w z9yWaF__3pBF4(I6;Dw%1o4DfTtRa8jvu^m`{yum1=uuNffA{wxlO~KEwfQ_@0FB$k zB}d16_qVy*Cw@Om4-+B7l&;t7h&jd_6Ff}q2IJ01osuK)aUn}{MBM{LoG8?{?GZO~4kvZn@ zyuZpR?}59#qvvg3cWXsev7oG$+!xX!LP(2XVXvN#AAf$^(O6wuR2UJRl3&fTcqwV3 ztc>Iz{`$4IuUFhyS6WdK@9!R*lneTa5)hjp(-`93|N7VGzAjO7GtUG}@f~p+&jj3w z^6Xu2d+XlZB#U*B|6b(%h^A>pp3*Ku%Axo_m+9bF)-MlvsdOh^?}*K<>V zCINVs>Ukz$OvlQ?2)W&Z)1T%zHFgN*nSfE-foB4aOH4^`6Sb!~oIbK|`vFk=s;Qkh zd`9W;k4u)#oqy8WGax)VP9zCX;+cSHg{Li$FecbtYN{e$om1L1Lq=vf(9X1VkedZ^ z#AIoAs;jN=czso6$wZkS5VmDRW#Q z{nIb+-*$^Z#haBF9_ZuY=46lNGL}ps&ad`Zdkc;r zIonxTn4ub}nK|R}MY4ZBu19Hao(Y&|0{)&+4UZUe5{2Mn%8Ey~v#~8IDJ#!W+PQM|y4e%Q4I4s4zMOpc=o!~vztPA3Q(C0H zcIC49GbfK8&E(W)$gmMS6Yv#{8@CJ4^78U?bFwnCvf@3B9^KW_(bIi$U-P!+ox6AM zKgi=cknV$}8`b^eV}hKO({5kyK)tQHyu#M)tClQUID5{FdCC<7Nb7BH#c6h6puewAWTSEM*uK3R z*DqPUXx7Y`Gp26|kg!lneLdJq`v=mDG}TV--*aH~l67n5%$_rA){Iq&E#$?muEO{O zJQHwFXGdpKPOz(^S44DFXh>*ud~#YwR!&Y{9%CZL6lQfMTAS*tN>TqRFFzj)-zbMw zR77%`!CjqjRWJ?VRo+KyU6~ef)Bv;5AJE_JHYb@1NL@DMNfH1D8z}nczu^2L?hu#wwp8G-iPtFCV z?qBFi86hd_Lo0+-J}AYyfX0>v8i^Z#yB-US^gBp+l?6L54LtK(FT>CxNi{^E|p?tNR=ubDk-$^=>2iBqP?$_Y?M8I_hH?`yGm z^WyZbo!gcznl)w8qzSSUCrz5PG&n6CM1VPTy;$$w9fb{>*UbkL(Bw%ICr+9?dD2Rs zh^W|vCm&p_7J+Su*2FNynJ+;2PtEUd~Ou$SdXn7#@XG;jr1PtBo&xpf5*S*$R$JP=bcsJ0=OE|P5hLAbV)Epu(HxR5R3cM_37~<*Z9A3#Y0n1;o0t68Tt^oI~qocQ@zef;k_xiEcMa5%BPn^Lsyo!qakrVPN_YAG?IbeZ|M;gwm=NIU{Q8E9 zyu5XJupEJqKgv%)_i}b(e=l7pJ zHf4voSerh)bn3)Ox#K6#z6g(sj){v;AbHojcW*oE(*hhU4YV#O96xdV_zAT~z5!I_ zGK}Psfj)6-ZIYL@;fveqaz~CHKcS%Zz}3^o&p#lL$ved@bwxZAFwX?c*#T0WW*R=I z-~XZi^!C5W*&TI;QYOZkDtGMt#4#uWetEU zHY!ZCmbyYgaY2H&v%Mq3KR7tpJ2*MJ(3yb&mr$LrrW)*3`Dt+xVPF9W2?C9ve*n9f za8KaAtpO7_m_R@j055)ATugX)cm(Z|a2F8j7ocCFz``oSGXazL6h2rGAJAcfYp9e} zS%jOHlv77QWRyCrgb$Yv4QOIY51;=0?UxVzU9G|x7sH2-pPE$wBuTZ_i7H#t+5g8M ze}4MqZGT5?VTjeMhxhN?31~v8MQ}31lPT%y`4#fd@B8}1HEB-zk00E;a8a+E@xBy6 z-rdvp+aG`ax3r$Y zL^g({hFR5MdPU{%=B3jo%Zx^cF%zaQIHGYI9Sk7H#?;uFb5Cyfy7@Dvf+l&a%=jPX zuQ_x1=DjB`-x$*N1v1graJ3zqmd%?!WfB6MvzD(re&O03ZBY5Xq5Ta>aE*-tN?W(A zUbbw-k1IFq+Hr#mX99+qMbvSH1z=^W zYivc~l8>K0^opCS%d!#!{St&#!pd^S^@Nj3Lrc4;_w#Q!0JJw$2~v{$TwH=ns<>h; zxyaXSY87?=_S>iT16{2(RRG}!I6FFc<}f1htW3=8<~H%CKR*5Zz7J$v6$L4={?5SH z+r?yq=`kf4bHBB{`>)@D@$YJC5Ef@9M)|nd+gMw;B_$-pCnP|5#F7txef~Jm-(Fk8 zGXbZ^h58YE&ehe;-NVDfld$(qEQ=SF-|DL>igMBt0m2In28&;SzkdLDSXqT;oaAw) zgm&O~&UpNyA|k@UKwB$sP1w}_kMb*|eumoL7(uEuY zlwsQBX7NnG+!91gwS!FHNKUS)2Cj-NL0F?`31V_u>>C@(CKemp(Grn<`Fyqtov#soTURb#ICt^FY1OND9>1bG|4fc^ZB~$@sorz# zJ2!7$*SMm7?%JJ)&tDswnpr?zjVD@DT^!?V@#fjXhuT`2H*Vh1di)I5g^8*8pd8P< zG&eOS#M{Xd@Y^?f`i91)=9bpBDD=V&Pz7&JQ9({hbO_c5pli`wutp&Frn(BBfTTJh zTs8%HnJKXmp&>z79Q=IY6Z{rTL#4@$%)=6_z=hlgC_c{w%rgP!W~C;@#zcnr z`}hFg00=}+Zy%C0`j98kh3fE)HI-#WdFX?~Q(Rmus6IOZA%(E7mt`5WH6f3;tf(L_ zJ2N94EiGM)sn^qsPF+-{lEC{o1(boR8!W$+w!LL|hB_Lr!*DBu=D`j%IIO+}FOWrmlSE z%o$~6W!pGOb7fasML|}Am%F!vql1a=i^tkGE}T6Bu3be%CEY-9CrH|=vQwhpn7G)w zyO>q}`bqXPJ?5Xq)0_`1QF3X6p`Dl{b`>7uN{|Il}bJ zKtE4cXEQ@PcQH7PJKwhTwl@mO>&x>C3kxHo5))&*oZQ{b4Q*XL#S&QH?>_Xj3UgaZ zvvP|v5~IT6BW*0b+|5nwTs%C*Vx9?DdTM4i0M7(WS(X%duCL>nfDJ80@7}c}zPa_t zH6|lZP*#a}6Vf(-OBHxLyMF5O3p3H%pIak?y`SEGVC|idkzZ6^gZhsZUZ=$Dr`MA>g2l^9skv-dN#G52&dd1OP4fXqRw}RSXdU4 z9p-Cb?PvGK*v#VA^?O=(&naKHdimB1BMZKPDpF{kfHu!4vJ<>Z+IS-qPfmfGJCa+-wAZU@!h>v?-_y#A3ryVfk*c;3m&zhAh=1iNr<;sILaf-$Dy?YduXox*%$&D)tIFMHsIlD~_Tc#WV~4iv zJidFwrp-Ssoxfn-oT*diuGn(w_G2B&B5lomeRl8Gwfi?NU%CFrC9|f_oilCL(sldP zG=ap2(TdYC&jd`CJ83$_$tWC2@ON?nNF5Ad<{@?-o(b6KG03^$YGhVC5{Ee^KtlO< zw|BQxIIEp=z;+^)i%_Nqo(!G|xR+-FmR5u42C;iPVp}YQ1c5;z<_esolEGR$RJw+h zL&m0yq@JouOr~#wP!!2RGAwzwA*aoldDb}J8%!zs8$4=MPnqOsoapN-T`q=ej`sBb zQ%>qmUw{!=0zFn=H+Uu9CBGhtab5tMJ(UnbS`l~j$0GZ+!3}^$Aw2xyJOk{N+ytx_ ziKqkp;Uw%+=>rCPNCyBLFMXeH8Y(GJqif-?;F*A#)09>ibGwl9YZot^AR{wLLkI{2 z!9_p_%3_uslAcTzbyMZlbEe1wBD*mxJv%oyDu>U0k(24+4TP?3xSM;+mU*fn@*qkhy#Ew}+7|H|ac37Hu3uN?;8=YHa1 zx}2#t`S`wG51nON%SOlXm_x(We9?cV$4tQ~@`gsJHo)En-~aj(nWq&(|(pJZ-{+aWb-7?>;lIuyt_u@bISRLkZ$&O<^Y%&7Cq) zcKn2$x1Z=++VM=lurI#q9rs%c>1G`=h6zsmA99KklJ3^m;gw26ej`Uk4bn@W@s;WmaePpWxTc+@^Xdz;Y1?$XI8h^(UMXJoSkf|UUDflcVXWxwhdB_r=KB6`O z%U*8yBB!hIl-a|^j!}27i48FL4`dRW4Qwum`jTIEej{gg3w^w(g-oojw#+lh5~`xv zK#@vjejrQrOY6IO+cNyEOIxf>>Uw%$MG-^@m(#Gd zbuK>jt$7)7b{ZEC>UW7dsGK9Ma}?JEhnSFO0)D#hfacR%+N!J8Z&G8AM&jTZ4 z;-S8|0TxbfrjIUe_VG2jdVI^a1G^XR^!2sUIinjM8I9Xp=wYO-sb}^`yTHfhh0=*# z`*!ZQ7#VD1p>Z!bECSEJCc#$AEXm8#Bg@CuK<(6#{X0&p8v!oBGXe8VzzwXt5TOg0 zlJiW!JQMIwn|?Z{e&N~cr_YR02%xRKwIasTA~@2+`jU=4&jef|K>ko#N^)`%@I^^U zNsLGn`#11IT-l8B5&;7!rV&ChEtPQ^(!R@`M6q+T24^8*0pLIY{)c%rAmBEC>e9|L z0rO12``%dCyNJY%&sU7xzH9p5{w8zl$01ff?kvmsBfcLq z^M|oJ9ajv4{JWvwkDQ}@jAsJ2cXW31WcDu=0jjMOWCDF04fru~jNIHkeSG}@LZ(`^ zI1e|Y=1djk7iOjcVAMny$&JZoh6gMO_N&_F?MMruk@VqyZ<*m-$S|B+${ zTu@OS(nC{I(6B=MjZAxTQ81&Q;s~Vm$Xui20RaVZ6p-%0tdFzkV#4`c6ceVx< zH5aAFM8u_*HH#%JHBF+rw2XK^1BZyHsJO(gdgTMo!48(Drsg&-p}9@n9WBL;^_8if zE=KMlVd3F1dRmEL9v=RY@rlVP$w_Ht?E|eH)s2Ovf4if z9Au;mIL`zu;+cS<;D7<}Ou+DKy!)j(#MUz?EGQ&0Avx63>b3Tb^Jgr=sS192R$F^V zXSJ`Jvz5(46@xIS= zpI$z2-Nn-{Btnw9apN2PyVtL2+|YcW@1BsK7Ha72>G)Fl%%QuEF7DpO2YDu7+J6Xn zE)5^ie$?7rRW6LYdrCpNIZ+1&m1jcgP}A6$`$$QiwlC&8ryls7HMr9eBUal*!pV!_Qi(1r6%z+urm6Y%m|h%|$T9{NO{TslU= z`CMHU|LVG&+K-cECLC^KLa7^;X99+~%uqNmkgLi|vNQ9f?rEH=31Lq`IHaY+15Y(3 zDc6UVEzY+k5<&ZL?56l0Z15U@_{+*mNluN%!4J;s4ktb#{dp$fg50dq(lUyK2!%Bw zkZ-j$R2HPgL?#zkmQn^(eokgdDI-d)Xngl+%GRthU?8k(BH2;bgZRVGM_4h!=4_49Ss)73XX zp)_;MysBD6vtdZKHB<;PVo(AkB*4|o$jI2(*u>1Df;!Z3;cip{F3ZCaJ|@V+35A%f zEG;c8h>5xq9k8(C56=W#1q?bNl*_mzMggfdYgIMnsa~dfFLiAqi)*W?LkX1$`64di znSgmFVEKJpz=OPM^@gq6b|_xbK&`*xlH!t*lF~xkhYzltJ9T2;Pn*`STf2Gtj-7{9 zFI>B=^{BX5fNE03CAs!m8fvGH?c26_(}vAI?bvhZl=`I`cOE>!6caGYz(W7$=auCT z?%uI;=dQhnPMlW1a^sHHqo)vqrm}dfp zKan-~vRIKU+M$alsY;8M&YV7p zXb@1xboA(PvJ3YgS5&>ELD~(L?!v^&KhFDM%J{KpW5&u%n6hBUVL8(ALNJCx4#cI= z{sl9pPMSDj(&T9~=Ki?*sQhX5%NjR{g%=GpySdRXkFHrTYtGz7Yjzw}ICbXid7cRv z=$Sq+2@`&rDyeY4gS8KgesDWbtg)TF#|gjb0Wv-Sq9JaBytkj#VUc!X)T>|zM0o*% z1>jwf;i5x7HEsxW>FopcE3t*)9dPBc_U^;`5ARr)UbHU2l-T!^O5cC%sH`rn?SB6r zT`)p7l`jyr_Y>7_Kk`tkovt3)w{4Hy{mi!?`yr?CIXSxXOu$M9HvhP2$-;Sa=gpoq zd-jawM!~UZ*|`OU?1{W9x_d!x?b;Pf7R;SHch0OCvo~tH1VkriWaZ>CdEY?6Yqizu zHZPk$f8LzAOSYfawe<{+PE5_r%;e-fJ?()P53XOgYUPG~YLAVqoP0teGioj`aa98_2Pk4D^Jb-@Sk1!bP*EPn~})M$$`^ z$&9d>n09*y>P!?49$K@0;p~|+CQqI)`v25m|nPB3b zAiKu99UxB1A)zlnkfL`@@!}WLM&;yM)d&t9AQZgu}{2qJ-bRZ-y z#n~xKfM)_`)%lp((w*YaMxu5qJ4BDHotS=kCg4T6xE|txM+5 z01xM>G$dsPb;H0 znyLyXPRh%jy6(^AfK<@%T_5}2e*#09>FX!g&YU=QLhd-v1Z-yG;^iL{5{^v-x45I! z+s63Wb@kKA^6=0d2i317&jbwnr67-R6QsjZ?8M+Nga~9IOHRKzG57l*mVyGBgZfXH z3l^qi`Y#>iTRA7hWJRM`CJpj$ay%sI!a_Z`4wTSH!?H&RJwS}K6^I;;2^`l$Jjeu! zYg+3|(xd#GokGgUHpeG4Q>wKzzKSgWj@p8xVEb3MF5mTQ?qG5XwsPUu&VhkmaczEb znB%MK>K89tH;C~4=z7Wl5{V=qety?0szmJ2@x@gorBfH233dWG%MlWZ-hTS@Ykx~| zVz{5pqYJ0yPpe%|5|N>YU@z&v8D z-~Z~Y&J6c;H@c^W@|*I?_fo1kG6T9PJQHwJWnoINz0SRx*VIlPI&|cOvc}WbM&>s5 z&hCH?A%(iVA~PY_PFMT(jdLdt96BzquKDDZA<5lPa2j{8qdqml&*8P!jhh#V>Ra{t zgJ*AyEs=%@d21VCU`m7CEMIBex_w?zK}qe_qZhA@z=Gu9>`Dj+3?LSj1lpPy=smf1 z{^G3{UVlI&LtPb0)90ln zL`OwNMnpt}1c!!(N!0-9^vDcGf#8y&!ki3XcoPy5|veV-gt?X!!WA z`p+`~@7u6q=?}7FN6So_Jawf(F+9_7P1EvH+F0K5&gsm~RZHef7&~f|%tYBqi!=c7 z#xXY?ceOJ=H?f9i0$#st<`mfpGGk<>&-roRx$Acyztl6dK$WqUCfe$3cqU-lcM0SL zTAPMCApE%;08B7u+rwE-CvrLhu^eLRKsf@?>n{yGu(1-xi54?XPCGx-d&=^q4(&v! zL`lk6j1cOFo)0bzkkPTfon`nV%bDcgF#W=Pf^S!YEfty2_=@cMZ{$1^aG$s;H`3Si z$!!hQQ|G)>lao_YQ*nI28UOcx{OezTeC%!!W<|Q|KfZqHtnzu6$jIoJ*f?>kH2W%*;HV2^gm_o(UMWL1_QyFnB@`65uc)-T%oRXQjt* zCgwU+R5H1A^Cvla;FN)_8GADpAz>Afc~RAm!TldSxDNFoB(ETONip90vj3A~5xEaW3!Dk>_-PKg6iXh?8SV1U1`4-8dm z$egIJ#heHALt#F6guntC86FlI8p7mMatlQR3F-!NJB*Q&o|2S+WZ%eeybp64It8r* zOb$uBnDjWk=VYd(BqqegQoNBB{;9*17SgmY=U9BvBRw@a5i+_K4Z|~=9f3*U9?^Iv zU}6HqouLB_t2xFBCDHMO7AVje<6$v5Iv^3I4P}nnn!poCw4IfBmc+$UfTxd)spj_f zj^4LDT`hHGf{d*4hFUyIo(b67*WVvB-lDedUqAG9h#E?YvJxYLJX{>??CfoZzyLpQSEJX4aL~bj=k9@rNgvQA7S$K$q{Kx=h6K1- znps-e*dYm@lQ)3qw+(JU>?@`DJQFZ&+gM`=JBfyZ0E3MXDRkzi+(E2CT$2ismBdA? zP=B}r@Okk` zLXeZQ@gvR4VEDQlm!HiJfVjWdoZ8wOYf3Ys{9Nn}AKtmBd|E|8)g>h%9%OYSZ)z5c zn}vdu0B>8f=l5=^E1x=b>g3%>sv{9eOvQrA4slaSZm7GXo-Rmr%qq?>{KQz6qK0hkL)7n5!`{or@ zMGUWW@*-U5)^-lnHFae%@#TW_s9-lkqvv<8T~Ix9>a?=TnVZk`;bXTaqTgCUjvyu6 z)Bg1{?VDHC)l|-$J$vTj?T4?7P%03WEO{p2L2n!s3lxF?9b1?qef)^wBS(yt*&L9YNkyDeNM2WG z_4LlUBg>~xoj7^?$dRbRGhx&u-Q>8~n3xz^KH7_POl`D|uK)1|8JSVThYuS*e9X8Z z+e3pwLc_u!F0gv>$}>>w$m|7^M~)pnbSUH_Mh#nL<>-P9nr8yWbcXscbB<>Mrc*u( z-D6$g+>R`uk7M>=T#xlY*c0J9ioo+sz}-DR|MmIjfu2rDyQsNZSdy0+AMEex?iZI| zjzsXX?(X;h{Oh-mecck=!j_tHD18!$dEDKcoPFYoO9b70fBx&Q&+q$sx)6zOt}ZVu z%uNal^mcQ$cd)nf&&lig_{Ts0@yolneG+(Rsw#^M^V8D8eLS6DWZPL;g{Sv@{OzBA z{rbMYtD&wDGr2G;Gc_^X*Ui!1-WGPSUwrSc|N7^jpWgO%`p2w!{^VQ z>o}%2F{%c*jjM5ar>LqpD=sQ3*vHk*#^BXc?b~;B1B>9%A%6^s>q`q$Vw|3-_!d^Wj1$>X3*i`8+8uG9)C#&(Tuv%`5F2m(d%=tg3qX-ZOnuJlxL4qVy;)M_UsU{a2s?yRLEd;<@wZ zFJ8a@?2U;9J>QPH>~J@G3u8k=-Dgj=G;iI~xPDXf-Xk476ANo@dbCxfg?ZXrTbLN> zzk2cP`Aav8UU=Y%8QexzMFquTs)!WrpBgoCKYNu4G zFY<Fth-(95>#}Dq=iafT}>-XLb zYv>dagck=U5)1S%DjYg|?BJf=ySJ}hwQA*$>lMti8yi@d0s-tao(cHL4OON62afOC zv3=vZwaXVSM6Tc5dGnT^(R?D3WV>3vxN|{8;pox*+qQ09w|dpGC5sj;Sg>&MvV9jc zpNd4CY5LkX)lQu}cwo;@ySA+Vam9+IiZVP*;c5MIdUAuPvv~9YGti?Tbg%ih)965aOz`+yBSMNT2rmJsaVdFp%B${!p_2t<~v0(u|o^GhoNp(6s zyx_kKq?rOS1FD_yFN!xEZS6!+iJ%Y{9S0-1o^;q&F#%o6|H^ zvD?&B>1zXo2bredXW*o(Av=E5O^UDK)yZshvR0(oiZhcRInJTbp3%?B&(m|4CGyQ9v#h)cT}v32xl(-u`#( zImzBOPM)3r`max-jIhY;qB3E1U1JMAN8Hu>_Crrag1@z`wVUM6|Mgp2Q%y=lR91dP zbshZml5S*Ew^w9^T9}xdJ9iEI&%Y$dR1gZXQuC_{svBE7`n$w+f}C`3o(UMGwCQQF zM}j8|mI@fHVCdk2M})D>ZejC*n-h>Ts7^fvxZy-cJ|EZ$z!P^EDy|1FnPnD zM;H??ysjs+ww81Gl@%9}wT><%BE5i&QWRMT zp1x+WbYwD!sSo>p6t%3yIzu2n$gu#@2S__`gK%}oIQd|Y!4A+YHUL8j4$j)a??`B6 zE+$C`_^Qf5xm0_B`(-<%xE&BAab#QXS< zq@;P$>v<;NdY%cGX95PY1DR#a9aWVb?x_Ft$qQ8fGcmIUzQEfrFqpF3uy+yX6zucb z%98v{_=2KYi@`CU70IU#&CR4+wKxJ4!+J;l4LjneVsVf*0M`LWz?l*CJHhe@Pj60k zb}r?$Q6JiUp`chH7&somItl?57Q%PHRtS1wJ29lP!O~>^-6EGR! zWVN*8rr-vl!2}1Cyr-|1X99kFSL=~$Vsd(BR(4KqZZ^;}xHbJvp&#CMR;77a>pr}9 z@4jh7Tyk1wW@c7a7Tvo}o(Y(WAyDI)fU#V$wFs6A9WB`EMyCYUV52i@hm01k7xS=i zLTQh|4z!-}Ou(>Bsp1LG1l-!%6ntp*3{a7dl{xsr#NHh=qg)a{<_%8Q*kW4>wbo1? zFFS6$l8(8vw;upXVKMC7Ofw6cAda?)4}Y3HZQB!5Cr=;0z|g4pWZECu#DkNKj^A~8 zK^~OdgaShG$!Qr`?0Cz%u$`7{^cnzh0lvu1&CAO#pxqz;lbet2#38wH_Wfqve;et{w2bxBI*76;C^1YIQ$UTtM5>dR+l zWMsksf^JK@u-VDjV}bWak$>3mXr$WG1qK~R&2ds7J0C?H0J!7o_z#Xj;R2Q;3lAB5 zaMalT$-S^;hGoeB^!b00vtB$CFgeqCCSc2#x+$4in4zNf;%Fb|OeftD3&CJ{I+HQHdUHea6()5XlO-xC{0#O;^ zlaS(N{_M>0)Ap7+4}aRcYx|bd%6B|MB4ZM01Fea4%1U%H@72`UD1r5WJfyULFckkW5cTelV!)H2g^o@Zc zB&)r(rM;yhF)7B|*}>7t-qO@i-@w?^!iHx8ri4PyhuG2cwy(RjqN-R>2392mTeu=Z z@Sv~-uIJ;&pWk*gR@W94MntFNS0joEj00sNm680zU%&SD^@ptH-#CCD=YBhW};#1iJImtqjGC2|>i#01U&K*9=g<&$%hZZmUlvV9Q_jIVO3 zlzb)35+VP`H*(TYdVI~WP->eAD?KPD6$ZqD9$ZsHMNy(>fQNUipuQRMFZ$n1Ay`pk zWxX&h%s0Ts zy6^?&Qw-bzpbON2{YI)qQp(1sWPF|p7)1pIcIS5;IH4dXcS`k~;?eEvmd>3qN5h9_ z0-i96X9AX54G1u@4Uo1kQY2(}RaUs}-uN7Hu6=>(5Q>L3K%NO0g@&`Us2~XJ@RD*W zF2EXB0Du9YQK%5b21-iH%83b(Vj?J5fY2$x$wky5pE3P1Dqo0Tf!Eho2nqz{RYIy$ z0*EBf1dRJt*YNY7pMU%1eSdd*ZDnCvc#xl`yNjcptt+a4#8g(+)Hi+p{qyIa-}ZF2 z)KugpfhormImGsMPT{CT#WMkehPMT01fB^Pg+IWnO>s-;Bq*(dZmIWmHAnM!YB{(XSdZ)9^JEP-CD@ktlNAGVDtn$5TP(9Pf(iR zqR%q{^Gv`f4u}MW*r+f+;v?|%B%A{D7}*4Qv>gMQ%t!_j;v<6t{r&xXp|#j>Dd&JT z;?h!1$GH+*g`im_ITimR?XJLFL^UB+ijcIsu&}7866HWyks?mJX$?ScmVkf`f+8yS zNx4Md=s(W{{QZz2!$-5#(Zq8K}CU0p=SN1j-m zYslr=2Uc82fPv;3(PR?wBI|({$Qd^;q}(q=-G|hAF71_P0tS&Vh=S{F)#VkoZeO)z z(ZbntX3SHr7(iMtfFX?y1H@u1veCGBY~S9E>zAxvG;8L}8Pm4}NVv4{dg37-NHfw@ zJGp<)fz?aat(h}>&a7E8RwcHebQ=7mL{!}Eqx(SR@Ugwyw=P{gZ|3wFGw05ku8egZ zO$hm0k*&eYvj-3ETeouYoS8GH|1fjrtXXT~ir^tFE+%=G*(0qp$e~}ce8Eg`mCl$o zYxbdk#}TUM-EJ!jtR*)wO&oHgf%Q!z=I*?ENmqF{d8^z0_j z1l-k0u?0Yl5qc(v8%yP7Os-6GpcaU9P%wjf;K?H~4_p^gwd(1CoN-%IIEBTZ;b{O= zuxn8Dq;ecpXau?*1x_0qI+5E#jmR{4CSbY?v_7Hi2NodQNS+B849G1OZ(f|@f3G9jVaM`l?kWZU7b@Ie1%P#2KcmSpx4JKcDV7)m{6c#UEHFx^-=~Ji8 zStobzC$Y9uLK4utAss!O8IznN=Y*3a|*Wk6^uNdiQF_YUw( zz};=$%0De#vS9x5ttYSEdGPEtrSQ9f@f*t*_D*O)XH!m~i-T8KSfHnivl}Jw0)QJ8 z&9+ZWG3<>fxYpKOTaufeoRkP77$vxplagtDqbZI=38)T%!m2AuP$(BEK+L9QQk}HO5aQefJj1q(Bq6R)E{Ryf+{T2b4y@!n8wDY_K{4qu zRE9>t^~^q?V)VlQQ;za@tmH0P2Q^iLOys1Lq;eFeK$Q(n{}G3vaivYFk;t%ti!Y{yEPCjO7W3ex%|OvIE#2wqT$PA$jZ(g)4@8k7okrnSf!fb-jD{wzEDhz`@c$ z>w?1Z6UUFAP4+v!PPH{_J zQKXBhj`lUxo|st?)1MEi zw?x!fTb35?{8IboWt9_04j(yw`qFa)bZ~U@r2R;Z+nQEhdOoM`d+I*DoHIcqZV{BS(XyV9fX_ z3wB)9)PDZPz|4|r1GI{>t{mF2VZjvH@#97gA0ab!?#dHaZfQYoVFBnVI<$Ko+QKsd z1DI4#CB!J=S3<`Z(1{Ge^#78^OrAxYfjHMB3)~4Sn4BFDSVdD-9GhH|6s@O$V1RvT z%sI)joOveTfF?S>m6xyzA3eYR@yDMi=HDl-NpsSF{NUz=i+bfmhEiNakoKOw-~RaX zAHTfq?QJcNcQ<)*_m;Zq-E^*0lvvyeBHZ8p`p3UM4)k?5=0v)hKGwXUa{5YmJ}Sj# zWn%kANzmW_`LF-+$9tX$*!1Ni?Yo-y?mRIxv$A(~_w@9ptc1?4-v0i+mUNFddZt!Z z4z`8{NWpP%argA{_MxLHOH6=&zCut^lA9Rn>*3+yi8=u=ZvtW9ppgR$j``-sY9Xk? z5@Mqw!o$PE!ou)M2RsZx=VQ!#00T=3b2CyAVd)ND+8PN2O~8XPVEpO!@=U<>c}}-fPX4rF-qgvm6J)0@f9*|mE|0$;mrEp>ZN{A7lTr>iAsgwzn zejs>4sA2XIQaN%i(7+dnqX}}H*a#e7DR(^+LjqPi$}mI`bV41IuS8IY@i{RYf{j2) zP*i3e>dP|$BitmGeE940$ASL#+8RM#N?fqJBPemqtU|)WA|j(;mA>ow@aLy@yRFXK&d5X+UpNv7e}@+FES7@Kn(s$jN5Ktj*0rH6VcbVj$-_ko}KJK{&mm zxPOrVhxfD;p!pJrNEOqbAV+mj4V)jCys&^u5#s;>{_^T-^r)xIKcW{P25ZWn!2tq@ zK`=?;6R4r?KNAxim~@k80;WRD1t7|$<%fl3>ne-$atg{o@l#nzY{qy{B&T%=Pq@B3 zBPGPs&dS6sivqxm=@fEwz>{kPlYD+^q`$K@yyQ1*imU%!PUPYh*=Zr}cKX`dmsHL^ z&k-_8SDpzNla^-!W*~bk9Xt~-mMJ>dQ#uli2pAB+QF7)1+yAkek&Bb|{|d?^6co~` zOIttJpsaw_u%w1^ER@*)>3SxkH5&AUEsdZTq0eCUL194?(G)ZNZ)q%y@pku%$!lrG zI!h)D>3<_JWgiRsByD3 z3Tg-C?DD$ilq<(~En9)`z{sJaMvas?oLa?JAD#)giqb;s%kJ)6@x%PdvZKe18#{j5 z%sH#~oK(Gb@9|50lWLMz3UihzxNYxoMdkCT*Q5RD*{e6?aYF}6Zp(`g_Ov(G(|N3YUrYPZljpBq8yJ~X zW6P#plV<`(OpG=17*Q=_o(ULh2be%5Jw5L~{rbKa5bzc_c#HBglOjW-bE{$Sk-HZ4 zW_TuG93RB3)s?lvqTKlKa36a|drLDjb8{;j2eU*49EI7c&%frpV0S*vvzh3Gv(BF;E zM_3G5=c!4tF_9tuK0XKl00Pm24t;37ys%IwYNEfx$7Nmp) zI@q|{nCd^&)VzE~MOjHvK|xXZj*d}>q^hY|m>U!7>uhgr_(t!^?W^jl%BN4AQc^l| zMT=(w#-2-X6$ENH7lPElW!5M1|8hx4QbP8#x|Xa1`4PAi=%AF7L-{#5Od2@Z(1io% z)A}H7KoL_w65|rvWeXUzY5gG~`=vw7LTXHmm)1^jTC$ak-oZKpIbK;N0Et@&I!$#p z*@*ZuG%zhk6j6noX9Dgjc&&B&A{6GNocz(9Th^{!y=>8fc?%XT`EmE7w7hIdtCx+* z;~VGIl}{=v9o@5i-MUrF7cQ7LfByU>OP8IENtP7Z26(=>dGpe#lX55b?c26?)#BxI z=FFQjci#MkifOTo9GM7n>AzR?0It+UUTTE@QqD&(zI| z{XqbpX98wuW`<|RIi2|wTAFc8XQy^@G~y5EOJw^wxg4lUH&LN+I9z&L+5^7;eTK&T z1%$$twyz!j8zxX`2da@nQHH_mq~EO*e`h`L+JaAzGW|kIl?FQ7)4HXlROqYlOu!tI zAeeqDZMr`6G-ZZ68}LlPdEnd1KoNK_K@TArJ*1=UF>G0RteWjfQvrt+Ck;*IXlm!~Lh6W<6;L6#@V+Do}BQ7c)H~onX4# z3`>AMp{|DXn;Q8Lr43O4G!_hyCiznhWYTkKHxe;uDLQcy_Nny2gFU1J{9p9{i@Wl_ z(SIr(fQAA7Tm9$Orsk&q(0|T2%Iudg((X>l0r@Kr*;k(9)w+O@UcUQfY2CZ*9CoRB`D=8OI_J!T4ywTVs& zglfYKAAJAoPh=yp9+0E4_qkOEsz6Hw#|6ShAo-wa04X(!0KoB&kKnH7-u?EK4MAQ6 zHvU%$Tgkd;fuzr%0h-ZcP)=)XM@LI!x3mMv+wl3EoJmC;ogKH&Yqqi211*qb^^u&U zVo{x_I8J-<63uq7fU!y{^zWDIKjIZ#p}N{S7j~{(q}|-sgjg_EP>R;#a=I9Uc$t8+`T2T7gA-Wg-iv>NP!YCIeP#wbV$FsKB6`OOOkK+BB!gdP|!gFhlGy0 zdrfQrb}3%C;|13T9r}`Ac77wLVcExvT1eY?CSaZk*u>b()jKe_t-Yx*$U!eK($(_P zYd1>`rTrT>Y(0PVlFA(;YbQ^C$QzUV9SuzaoFAOIdi$o5(y>GPcFA2kuXx?a(#bOb zOup@ng>ep^-Uhd?-M@G9#_j9p&tAB!diu-*Q+szmV)_Noda9eJhtbo=JQFaj6KJq9 zTCls4{&VK~7c+!N!_WctZ#H&cboe(pr|i_Zz4A%}EietzD@j61&aDxs9vG$F!O6L6 zkV(f$2{Rz-ZcRQED`s+BhX1kXbP&W!R1^`{F6pw<)Oy0%A@$O6J6eRrb&{^OqF|GP zdi__Zo=B$$tZ&@K?bTI=wpDe>X_*en@|&J@iy2l5R#pxBR!vQ1LD}Xn0>ce&AC=p> zOUKQpwt-MoKojG$+Y4j7-1QB0EUogAO)fo=U;jp1Gp3}xgb|z|LR1^8|48Mcv%8tG zz3m+{!yD>HpSzpC_Q_&(cH2eGxshfnXYW`AI~YB?sQB{1jkCwRV$GgDh=_|zOwAPW zOu!B4Ay#=gzLo}2?&dnD<#;Awo(Z@meot%%vk%CeprwK7zgXm`pxN5h%`*Xy`hJ0- zqld45ph%LxYv`3c#o05z``ggP#}CdJF=DFhxJf^ZobcG(+Qr)s#+2{c1-Er}O&T(6 z(h9ZZV~2nD-S@*sj2rpmBSUi=7Y~uRCG4k();g<3{eAeHCDVrtANt*Q-whk{!~A6{ zcqU+;30PQx-JaW`*-pz@pc7WOdXsE`e0+~s2OmUs32+}k=(?`LUY(cDS{)47fn zcZQo4^4?ya2^etx%IvIMa>=34&zHv3MCv4~E*IqE{^AZx#hm|~JQd7`S6NY9#NOrw-m-k*;=N|rZPPNca|>E|Cg2KTEwO#WgN~g7bFWgEWOD0_#uirm^d_T}FZmbYwL6{*!yOH)RAWbuCH$riF*fnJKGwn zN^{~u{5(N{=j!ZatgCNmWQsU!T_fDEJ>8O)dSP)^TnKoO-P~MUt#n@M85o-2P{lI= z6S*&A0Opy1m5v_VwQ1wVov+I(s2poP!|7C(Cj@!980$UJP*yy;XXnQC>o#nAMb%gf z@`)i;Se2g=>+ff#`#?iYZr|3;8`iE_w|<9h1>||8|H6ulw74KQE8Pdz&z#t?X)7`P zuG=J^3c?v;0u@#krRU^^Ihnt?djZ4$#4`b}T)BGvmhI~IA3l4@W@JU8g^`~2ol9rs zcWzj{>cbcC^zWghEUW=tY-KbT)Av~ z`03+MAEYf^5XA;`41w*>;*Xy|xKY{EkCri*0_<|szChGXJz_BZ@P`KX>xcJl`%(2_ z-uq9(kkkB}9O~}|lW!hX*|~D{()n{%+)p2P&*b<6I&eA91pJ{~=d$XWHOm&wQ$~M) znbT)&(DMvU$jHqvEMlM7(7Vz%7gw#_v}EpF^ehgHnAU%3Ro(WihR?XO4adUVkU`Er4r>Ub&h)%9~+1Xjyr~<<3 zQBc6Kc=n*QhgNHJ;swno^=~gLrBPPOv3PPEwOoFi88$dvsLQlKz#d0NV#{WrvJwm| zBGf6e^6gs^vB5p?gjD8=N7xclQ#~T)>VNPzP`e)_1tFpax_ZLjRJQMI|reK~47(K;cdxT0sl^;@k@XKItU2XNnX_0S*_BFkTPAa?M96s*q-obv6urwjm!&vY7g{wE5TM6+Z zy&jWGhKBpP8}q{bTugK>tErv7=F^1*m*gB1;K2J2@473~{9SGIwa=eYJ#*=97Q8|9 zeB_ycX{D41c_v`0duyymYwD7`%(T?hG>qiro~bo~ZuH&qI$PwDHf# z%mnNQxH;_lQYKL5cn}9Un9B2^1Z)BgDsLjx4^=N$u@=?nBFZxXw|=(&-abiFX?ldq z>pRzUg4-n{juy`Z4EyAnfW?x^2yeUBx_9nso;s;^@y-+dH|Ey%POctaRQyID0MXkp zCreY~XE!vj+|f5SF|)L>cXDy}^djsKwjh+SwIcgnkrx*l7~t>c+`J>sM3fmA3`eM^ zA~C|;NbkqRN{X@*kVBauD>rHGA!|@+dU<;zDkfva(a> zA2l?!afE${P(#-p`dIti-qrIJ#*ZF7>iZui$jHxFbmp0XiG`hu2l`ov#D#h{)V6I| zCOhT_Tt0f-MEO~3E z&mRXQT@9JhP6meV73~zj1(^tIbKKka({KOy+b=&2_jT4LdYV0c`rNvv6@~ohwN3`o z(>wgfAOHC6m-oYxrm{%;*N-3Gzt1xPd-w*2g-3{}gU;}~pFVx)@2o9K4|aV0@Y3ls z=Ptc)aPjaB3MF}e|M0u_?*~N9r8$XyR!{C|o;iE|hKY@{yN`cRm_&jAFp@aKy{&b{ zsX>m0540{`(A0WnZVNf6#?Zh4A>enz!+q6x>47{GFcK3rp!z~=lB4=Upb)DAMJ1`L zP!rKpqenQB;~1qlYG4DQs3Hb)G^j@>&JhiWJrZ3`T`ed7qJf|m)enF+jFXQPH?SH7 zI6DjiO*4$hr3Z9mM`#i#u;d6@oG?0`8eGQ?@OU2znj!2)3N!(ee`fl{KG)M+R+g16 zXzOmL*cp@aOu+QXb#_E%RP?ljJimTu)5=BDPXtfdco~J6YwsHvnVMTzqNP+@Ye(M0tLg_g&QY2oJ8sPA(c@(% z&)jxL_r)6%a|_mSs;$LJ>)fGDi9rOAqNatbq-u03}7#(ll#2Ct21f1`w1 zYpB|m&8wCyS+;z|`kgzEYTmf}Q17|E!E46yMi$_3whSsfndQWk= zp%G}_7?B)a>hn@_3)4egt#~G2q&Hc96HWpZ^hyV}>9R8j&jgIK0o<&zQanJ-!q)cg zZpq;LfxeFBDnV|3bqjdF8FgG)DOj1BTRYLXUcja97Q^p5W}X zbaaabfB)Mr?+3eE>IIqU!JeKGZ;^;52yzj6Ikt6*`v3N~-#)(U>ujtSWF>`qxVicj zG9vN(JUrR$UE<&V`0eM9Ltw+IDFqtY!`0Q^Ik6x&J2Nu_&wgij|3Cl!%g6V9Z7sDG zd1>*1o-U3Kw%$Z$m6{6U;hBKRITedLgbkG?ImuDM{yyGbUS8f5#q%ScNYqGUp!1us zzNWk|J1sFjCMqHIubB{A62zYQj&rS!+Z+vWQOiXk%&jbt{0$V_c zZWw)0r37RnD-0(k7HJr5M42aLAX*Q0h8k+0AfO_z-C~z zNae5~%+K}!rouoIn1D_;jOkNAOubS$ECu@nnz!O`Ga~1ifE$|{o1*i3I_nCOgWN0) zUp}}67BIC_r;e!GAOh~?+t5^95|z^>EQycxb1*g5)7CnF>NIG)RIVU|?%?d&(AZp+ zm{KjsiI4C$Gk>Xb99C|M5(~v_b$|MfT4#0q5la$_6I)W~_Vm&+lJ4yi{q5!X()- z2&0dYAFF7Xk(`v6mi7+{ z@ufkDS~jE#gup=`bx6Rq3LJ4S2i+s+5F3ySn@Nfg@(^(T1EP+GilUP0RwS)RK`QU* z9~l1W)4QR8p6-^4vYf2U)V!Kb4&aACCC>yr_&@*k>xV%cKHy7Lm*r(7Mf$qCINI3S z+1WaK`VS4Z{*Qn0Ou#%7@K=^iiwvV)!ReM+G-tM7+dtMDmMUQD13@sPrVY)Ji3k)W z6w=ZHDKmfeLK@7x33NsCvjw0BjHVsU^$o4vfO|7JPHYtTg%mLCUQx3kH!U*Q)6w2L zm1hFxnSf1AEo~f}-8_Bh`N8uAPMM}^K~ZjINhN=hW6@D!5ciG&=FE9JFnt18Nh@^j(4Av}|jfybGG3$$foyJImnbchEI zu!w+wdAWF$>2ZYu_Dhz0!x)kBBWb^R*Dy-cGUyzZ^Nn0BBoSV!&n zu>(JDK^@zwb$fK8TY5$KJQ3S#B(cEc%E^O=jvn}N_wMa$R<2yJeBDW#g4P!HK_P*i zt$#)1)bXP#$5jsQ-nD+!@Fl%?Ei7=uFdO~FI%>F;o`-Mm#z9yL;JBlSy!;m z&5OtO{kVPS*3FwXu35ci^~z-{R&P3d>9*eU*VrG#UD1y(9^JQl*RGvgx9!-xapR^9 z8@3--*Sh=YrJ)(~O(hLUwhuH;9zS~c@Sy|y4;)v&uJibXp^2rfqbnt-X~*dlRu`lv zMTZ8W#fZ0$4}}N)1E|d^XP~ftvj0ZdKO%(5iQo|hfDaHpS{!gW^F^C^CSXEDlG{%K zKepJ_aTt8INRnB0;oLxeKa*2D@Qa+Y>#rtgZfb7$TK+|WJ`KDF&jidf0b}##nSgP) z2lj>;mav&h6oCXna||GoE13Twbuk!#hoayk)(2Mua`I7NnaoLn^)k~MF<+98XpAV} zSUzIjU+h=vwtr>5|KHm`&jidf0pp>>7LN2@eMw4)hy9yd7d5Wlee(3l3nL3VXAj>% z2GGa$g9E3qDnBL6)5FWt!_CdZ+b=LAJTf{K_y>mZ2YD5Fd=1rQg*h2%DX8C}k&=S; ziG=W{fdeISRFt|UY+YV1BDC4rtbHgG)5ZqN!(&hFdyzb*GM++;l#vczfQ};%pA_KX zbCt*?4F3a>_rv6Zl&CMTG@9*;%>OqCE<5%vkHe z6o}|02#cwO&ya@zo zzDjajF7_0N#O>ATfwo4E9vLR&zbYm7j$7fcF}b2nHdYBE?dbC&G*@#Dr%-1*wt z&4)SIF`OK8@Jzt$m_R3WI%$xf|22~@CmcD*e^D6u{pX9h#5_Ame0@DTXGo6`442K` zfoB4iKA!0N!!rTTpCl(cVf;i{*)6Xuojtt$gF`~YXmvztw!1C-_@)I@6=WukpC}`{ z>an4++^pQ zR}Pr;;h`rkP~1lfABo!AYvTpJ4k=moPcEzPKla8iw1sty!y*cKXHimkgtLR8t$k6f zvG!xNohHwprq>~iQwvlUT1eC;I~m@6?BQx|YHz7)qJKy0>@#-<%P91}t*ol<776p? zovz=|vkh{xc&e@S_}+E(Qy!6aM*2Ag1%)N0;?A0k2oHxBuhRl-pQ))H+oN)1>-u}1 zJQFa_1WWfIT7^2e&liiT=4U=8$$6=DDX_cJQJ|4iY^+7 zAU#wQ?`izh(es6FU6}LBMU%f9J4JDVEYAeIN%Pi2r1`qUZ;y=s<}b?I6~6rr9g-#~Pn8+{gUl3} z@0VPH+}H~1PrlXJu_td%{mb`rrq0>8ee?2#t5;1MH+tK(`_JE)*<$w;HES#EI<5Fu z#hv>OtDIC*J9F;h;dOg&>AeI<$d2u!9_T)L{KCMf9Ysq=8vTR;%A&_xVw#MoefcW(dZ zw*TrX&`)s)L_iSJ|H7pxnHxYmJGbit%}vJx@)l?}XMZGK$P?fcaD1RH9GrYaOb0oD zo!A-UD>+>b2mzf0DVvJOI0FFTda7*TnSgcEqJ4cscqU*j5R0Bltab8-pMUz;ncxTm zB@`Zm;f-(^OIT;;55NC1nCf7`9!{3EB0ZMD5{pS;_|vENrMAZ80FNZLq+j8a^rAg_ z-}}L4BW?0nSxQHGDP_O9#UcPhMcu8{H%{XDM;|gm^0C|)#1s>um~nSom5GWalhc#W zE@T&TDKwr5n5_?Vu)rsS1)v=(R1>1^f3|I!u|9Ci3yS^d z;L$B=trON}M+b#ETHe;RiK?V?jFbr!mv;~J3QNk%GowOXygYO@&uN>5=9hu!p}Yba zGR)rh%X?8}NoGuZdSsZh$s5b3kDeL?Q6u<*qLMOP{^6H~NGHGWXl$pc8Bu=rZ}e_! zp0$lhO3ld1$?xix^fm-}dpP@s$0emC$9N~i`s?aEzkBCRKv+ytN?KQMg<*KQuf36h zxqWa-R!*F6L`u*L!{^ub-}3Yej*RWe+OWaMMCaCxo44;hGVw_*$&NA$@N;{me)gaa zAm#xU2h^gyOpR^404fg(@b!;QFN;cX543f%y?OkpySv9hO-Bb8pXlrokQM=1lAE4V z+Sn8moaOEE^wtRjSJ!h7%{>DWN^8M)$G{!6l_lk^8G$LezK%Ck&R9CS`NXAW2^zo@ z&JDzl>uQB%F+Sn`w|DbQz`I8vI3q_`b{KBMZ+RwQR&I*`EnDnp9sa!kqbeW9%E^b*lbLtdLGmCu}A6q$x7gp6#x(DQE zg5vBz<4Z@+U)ZpClFWifM%FIj@hQdi(q=c*Nw(%~vdQh!2llLv6B*~488J9z@<4U;Q>CLUU3zIaxnceoKEe#fBgQ- z&mV{TyHF~U92XJb?dk53^p@m|36N(3{`BEpe@|y?O>ssXc#OT=++5MA$j;H(wXV6P zrBm_=G|8aqZK^CxjSj;B*xd~p)>hWG_9X8R4Sf7C+$(NvsLV@>4E6W+baQieb22rv zu(YXb;+cTq4TCQlCu%sx6@p?k0M1BDj0z8dN9hlkSQW`>)YR3y6_gbNo{R>;N$6x0 z9vVUoanLe{X9D(hH8Xm2_ae^(yk^y^)%b7C#{CvPK0cLIHFecd{!aE*FYn(xuexW; z`V}iytb}~+rhNwX_KsE6wRJVot`3&39&2lAsP5dfX2miv^{!sMZtIUvEG(?SN>Njr z?&M%=tb6CG#?jrIS1(_>bot6vYc_5@`WT%A>A|Zh_p>)QdVK%JrK7txpn`81&jjpj zZ)i#hUD=(`10fL(AL1`)Vr3&OHqh0RBLA6ey(lEN?0{+%-dH%xE;Z z;hBKH{r21MM~z({9vdBnAV^L5{Ra--&o<9d82#P%;L(NjTQut#v%n$HKM*j4ngY$E z=dSPHGD~h8iT}z}MvWRfO!@hD^{(Yr7&^ScVOWqD&B9u9reSw>9^h( znE=I5S+22W#ge%*CXE}%OrUVipmA2FDUYKs-?Hm;m1CnH1i;~gO9nSk%z z*U@?SsF;x2#l>WruuXLLPf3ak@piVcFfn@b#?bIhF@tiKu$BO1{*?ZUj|uhXnSgmF zU=$HF)Duy0f1u%`bBB)Z*}i4*!Z|aPrq58Gt)z~1ot!(!-;10~UtKtGVDH)$3un)m zp)_sAjF~f6Cvzt{lJ{9Z(LIYA`ejS!&6qV~`t+GIXU*J{lwVj}S|$L!^1F}D*R}Vm zZeF%>)$BR5X3dy6W9IB>rxVlj3X01FM8W*N?S;15{!PmlESf(@dCshvvt~_SY95i4 zT~Jh7MohayJQMH$k<}s{N68H03Z^&%g0T(t9E~tuA!ppy6fh%jI>jLn3L~CdHl%Wn z0FRRkfl(u#TpC1yi|c`>L^^~R@BjsbytS8_WU#@SU=jN@Wd%E_5})KFOsLdG0|Rhx z?hDcwMC2=F^8L(L$_PtIhw>p(`G}A)@DgDmn?yPRN6gDJ0iQf@aP_+Ru&zmyv?)SGMe5IcM%%g-HsEP*G5n*=6tQ8xj#6HwcAc z;RDSBTUX4TK0|5pq{)i%iVAWwZ<;uG21Z0hLp(6}q4?UVeTx^(nK5nhEjzJe5^8m@$y+yr%s(bX~v3!S6LAO&jegs%b3XNg|iJj(mWF|&jhS` z`c?>615$%kRMPip=;Lo-D6@L=?8e#SM~|x>J8{{b0!QWL0=60tN`?mn5zcR(>Rvf@ z^vH3QqbeFEIeB?``2__e@5W)OsW{ly;K}_|+?^_7M3F0bz2&{R9Rci*8SM^9Zf3XOn9YASnw zo(Wh)E;-KxOnW{~L4+n?wE`RfkaDl&HxSB-`2|u1lp28P8zMS4k$*v~kdj``{t1tO zgA~Nj~_9`2Dw%0rO12WP^ViQVS*P`_u0~|G*jW*J1{Q{49qB0w4MLM-p;_?4QP;i!WB|YNKmX`Lep8j4;LmCX(EVX~Js3zU#xz@RJ=dM{4BN9S#DtwaKzoacE z-0k7%J?odun>llnRa*~}6I_Y?Rg#w6Xm5)Gt%40-!Hyb_pV>Ic$(aVaWaaNrmQfnpxjSs3GM~Y1T4$bYB{o+5*E*Ak=REgPYeanK4;TUS_E)o#nsit)!oCB+P&aW0Yo4Be*>C(m1HN!Mn^|Q zMurEYlW-`z7zKK0(*>5^S~UMEE6U~Cj3pA#pKJ(^8Fn&yV9>k|89JT`m_<(+M6pI% zVoqy9TT5LzZeDs}Gg&rT9iop;H7ikv6^Z+Y2L>gr%{&vZ$FLoDpV|? z=*aK?`1c>b|0EJt7e+Z5XkXSif8m;YL_}nCRJ4dS(fs-M-+ulu&|FoP7GUw@+69bT zZaBV&M?{EvDTeUj*Pnmt@2V5z#JIhFsHuML!g(F^*yEXi6B9bH1F%8F7p+1h!_fyE zC>>r;W*{55 z>rpe(9N<@)7-tV0^QG+zW=xqnbAdU1)M&LxWl!CbQuolRRCgm|-ODF7%$tfBn9`YY zU>NX0(}&wFPVva-De^IWqjh}iLM25xMLC6SiGmXJi7bF0h6<{PNI`E&l;yKiTUIMi zmX}eKlV9PTmqo4Q$xq{%fRW7#Rb9VvCHjLeUAAW1&Qtdvy?A40394VB!N%?i*m(xe z1WX)w#DIsu5C%$03i4nZf`P0d4t zCg9*e{ZLktpG8ceaWT zzNLkE0Q;pTC(+3OPyzZg;5yKjC&lkU10F;g;Ru)%mrKET)cu?69~H$pS>H=JI8i}H zBzu?uGq9s4TO)WTVD|Yku2%wAbhUwe0Nm6~bvzSrKnR#{!iBBP{XOsh_+_|9(q2~~ z$WDs(b8~ZXad3t-id1+#0@8ap} z=L>lU_Aqfzdu>ThdSVPndIG%6-~DwVl1A zGm7vzc?*1So(UNIw?u#o`{zh@%Ncf9fOTPH5F$moKA<9qmJkf7kk$_dS;3*ZrV997 zj>lIjXKNs7vKiO{!=a3soS7J>!o3Be96S>+n4U^Yii(Iu8G+AsVM}E}YPh?H#glv2 z)Ya8>l1mEM0T4G8n^RYJYhz_@e6XjB+2i|H)X$tddEOIrR6v81yscdWI)uzY~dPEtq(B}fIzGe4ZtV}k;dA6QGGc15dh&s4choi z$O2*C2yT*)y^te;`v5nC;*f;VFBdQ|>Uy!x;t5h-8-t_VQD~6^+I9y2{ga73Nd*bR(QtM%~W_M;L$&f89hN}Qz)2Tvw0?9OH_l< z<6ZP@$Gq9f)2B^WnyEZ*@#g)i=dV!^|BVUBQ7}?fo%Zs`wsoskt=q6|&#_bLn&{V~ z_vFQEBZ{~|feK%WQzHCaY>W+_>OIued-Ck%>o=z6mJL8!komxMFBg;)<)%gl`FOZG zIXXBvIy$+yy4AA@=x-o$4{&?rq$S3LhXe%$1_T87`}^0kW}Ivam<(0qz{^QVKy?Nk z5yC=os=(xI(6$HE23CmtY&sz@9kP9?lxG5FkyM5tpoj$$FF4%bcN}byuzw6z_$8!4 zIA;b~c&jcJ-0*|s>(j#hbsx3`Vj*EKY< zrAt?IoC->ddwLL?tuM()OA3#U4R&)dHa5_`dF|50^XD#JxS*d7&XFd-9|{YyUjsY(arBb0*&ll4Q1W z(QmNMfH8u=dbH@!iqJuDJdqfvJ*oZE7g~hr@vFF=EU&E4 zR}Xcr96EIT!2Z44H>_E?V)48=%ClxcW!?=xNl8dfjMW3}%UgCG*?;`-u5BCFtXi^Q z&dljEX3bHaf5TN$6O@$Uu6sx8;9=DhDo6M1T)$@7!Z|Z%L9RS^`9mwQ&?YXz)!?qi ziGxQ^s2hiiV4YFh4PYRXWEQtg(5^DBwcjbqL4fl z@>rw;DnI);1p<~rn3_6c3WqQ&Q%lJ zE|kc87So$>3ivd70*_eQ*@>I0#jHG=y%FTxiHuNR4-UE6+qFbE#0pv^z>l>NCoKl1 zCm*aW!SJ%Fy?sM-XST0ZNJVXRO?{)Vl>o1u@UXgC@@$Q?@9|8)+}+}tfO#fhGCp#2 zsKWwh|M_Q45rBdH_EA{@8>9e=pH`qT+um-Ie-=n`Nw3tr+4ba!A;{9wTCY0qjv2fD zvw^e236&l|qrGU`@yCY9O@CP)=q<>EJ6Pz_Wzn-YZ^SJXk^wsZ&_p!29uWwjygj|p z+T^wH$@Y5@4ZXlOx3sZ_?BJ}$^3+imba~4%J)_q-cORYIcgQdseVMB38d;hJsL-~2 zi_05VFWmq1ReeP8lVfWaZ-3~MoKqyItZi&*YZsOV7;0@>GA&&w;o{plT~PCy^~ zTZ)oGUCm$X-oE=fFe*M33VC_?SU&qn{_f|Wy36yj!<{UjJ$PW^7mf+iGjnotQNz?X zNS%T{e)`Z;Tb$u*_v-OO6YtQtq%>3^XF)FM#RTZ<|8DqQUwwA8H)@qFy~E>ieR@Vt z9?APqhJZhDaCo@CU6AN&Z|fBnmz0{8k&RC8koQoT0^S4vcqU*NI*glV0;U5IxdM28 znS;(V0bf()nSk*?5Z0D*c;Lw^Mh`x0j`SZkNy=H`N&u9AkV$Sf6GaV z%NeYOVtik&r@x&^X?FOh>`w%AiT3}m2Z|DLH3lZ3EZ^5M?t4@tF%8Hu*zZ334|Jmt zmnR39W+3^9V@rmH!R{q%QpR1+{r2luHU)XeZ2m72=9~0U#(>@f@6g%~jS)G^PDnc9 z`lSja@51|Yawa85{;uY|E|z=18??fg9@DGW%E+|8JmO^Ll!6Cbt$`4Gm6h}~f1ZO*f0%$G{U}jo$Q~x=UYKbB zLjc<(^ZYM4)8d(c=`h~cm3KCyht);0yGx${1>qz;bqfb!M z*-$C$8|=yraj5KYuxuU}5VOxx$fP_IaHhY_i?hehxY!vy-nwbm_RVM1@B2o^C8lOz z$!?5u&rfqVd%k!7z2|rI&aYgze$5iKr!Vfl42w%lK}e%0)YjeG>dBQ&fkBqnk8R$z zfA_+jK|%HgXANWG5^(d&e9iUl8CyTmD-CqiS3ACI@6H`p;vyVvZ$5~Kj>YHGnChf! zo$hbvn;+<8dhzt(eLK!*n0q-|-M0u0kA{6!CwLiWXL#F~$M`y$UDMdHX`9BytFL$_ z;B1T>78GWCu8?N}7KzGZle>HRobTyA92w6#@*7ue{B#hBeaqPk_5fp=gNS~@Xs zHZJcjOZ4|KF*C5UFV3*M`s~CyBfWcxJQFa_1nlbW<;(f?UF}U}MHw+6fq_B(-X7== z;_B|{?dKmD5=Qn*d2=vmnc2-t4j-F9PZT*stidJD=mY=7&Ph@mVOrkN* z1k8duthY0wJUkOHixCo3zpkdDoT6-PT=JJfkc7=g0FH(DP%2Pa!G1(i7}yUM`{T0H z4CvoL4L`VG9h-!OV{Hni{Fx|1GYgL6J%6?092h0%rif+8UaeqK_1QAZQDv zx2YWOE4frkaSo~1{U6|{v9xhG+m~=KMRNmFu*xFk3MC}AUoGf&jwPc_v^2|-n)JOakX<7FP=SgR_)O8 zMN5?DsyO(C#v~+*dP3E1X&u_Se#6#XM^0;8I}f$PfZ0!%-(}(C85rK>Jbmpwm2F#h z?mckmh|1}U*Ds&hb$s8pl}c0O_nO%{y4{(*Kgd$&{tIhoR~ILHYm;Z!Z=Sn+_|Ue^ zOQ$KQSXkNeOu!PBR)Tq>#EsBX)Du(K)a<2m|G2_LdE|B&s*22?4FX`1LY2|EQ}f5m zF#OfX3`~I#T!`T}6y4slZuFQh^(frsJQFb2-vh}F^b#n@=b3;hQBx!Q{dY9Fd_UOT zT+1^7hXnX~y1Tl#IJx_Jdx2!UrTy36e)%{&Ans_ZFG`OO5AyS7op9})Jv`j&sfWqC zUwI~A^4wb*tBUio(-Xse-CZ0V?CtDqZ7V@^Sy$go8EgE5PhU}1keL({2C`{)H?&K7 zTTKsOT_bLDix3?Lswzqgax=g-9US2A>*MVOCPq$!@B};)u*T`5`}UqQ&uzlthuVmg zNr^=31%Z|(ubyaMR6DkB$EJ;{?sau&h=Ps_9M5Q7c{I-i{N(0kwSzl0@l3#*w(j`x z;AxGkx9>lCMxAItrCVNJ7V=V4{ltOYJ9h5ewddgRGa6dA@9RE!4l#oQpl4Q&*W=44 z4j$Zp_~eBv*R*ckMVpalFF%XHhCyUoc= zO^yrm_u!d;xiZJkOuw}}6Y!=va-+Zh{`+sf{qEcEzWaXkcolS$N~{9*zsAPgJKXTt zim4O7|KZzjA;#dDfH$bBUAT5jK;7)h1X*ee7SB*plo>mE%-FGG#*LdOH-F!;Q|GVV zBX`~(^K$@6v`QY9NND<#`RJGOlDeeb~g4H{YoIHK@f~MBZ+jk*{Wiuw<{QML@^CvpG z2F8ZZ9^SjlGXWDkuX|_+Tylf~mkwf<8|Vk)A0sF1?t&cH?hz@zGKIdr0iFpMky@S! zm^~ST1HF>ow!#Q6H~-j#_^8OJgp`cz-2B4A;$o?s_2?0Gwh8Mi%Su@Ghq_#rmzR?q zRD=V4XlKFU^H?i9^r6E*vpZA|kQh{yy;P?~G<+CBbi1SOW3@Hxab(XdD#-|J#)9B1 zM30{Rb&ScC#5@x)v3-Jbl+k@s)f`g!uz&VD5=jTAfsiErgSZQFa2=0G@s%lvxw<@j zV)zY6K%vl2gXw`av8SgGmlHA@zhSo@FGC>B9%3rJ7?2bA;}|~t zad-BKiirsF_YVqs zEkwwN6E~Tv?A^Cw)tu={(-agYDjpLIP{TIbMd<3GZY#~RyEkoEyg+IGG|=?Q%B=A1 zq#dTVx|+?NWqSR@zAfuF&7HPj9(a18F&9K(mFPPu!2CnKeufWD?fP-!s+BXRE6T~r z%1xRibEFVeEQN(dkPmm+o4--tv2DvDo(Z_U>1|OC=ylSl!Vo+=8Q3(idtf`DL;~US zn;UB0qERkNfY2(pAfMJJT7%enPpn?F`PbJ-yYQ40LxlASgG`7@0Jsty6bW&Sa!JGn zmMCUYo$ai+h0TH>)rj~D({Cr72MQLF2G0cCjx~s~lel-VD10jcXE-fY=yX8__?Zb5 zE&A(=(<1%cJYwn?6QCIV$%uuGT`R(N++L9u?q>S*_BFkTP6@S6WDXxE?j7tG2}=_~ zJ&g6PU$}b1xfPw|N3O@aNrr~|x*PMt{aj3RE~}}XzUI?~Rh8!Fm;wjhe|Xnjnda|m zqpyAblU+K7N6iJ}MgTPH%x{sTJm2mKzg-=ILSKAW;L2UtD|wJ-v8VcqZVHVk){@geKxZ zN1vHO**#{i3huR3feIS`WdA%9@SEG`PMkP-LQTsLY%a)Pl;LiPhyVEP@Bb2&Cj@xd zzr21%MfJq-6PGOW@=(H-pARw51T2;avtol?-{{`fzCs3m{??-xMizEp3h~BsNNc&I zGQ!*LweFp}nx{^xUA*%||BbmdV-g{>5Y_;(=xvyjrK$0=8=6<{=o_05$(WOiyQddD z$OL}t?rH_1t|BikG%&&S6X!!IB(hyebif`apw=6W>xFV0R)h>weljg5^Yl)uyr z0K|hoh|=P>;nwFA~n3zP+JSn^n<;nG||6dup3}UcbeHhDd6qD*gww%%rgOx8#C^QA4ZRtMSTAHJv}h} zTHC?n?&##1fC*m!`)@=SQG_L{-wGH{XjVG6K42f^?7tao!yK6pC<#i5PUJggZc@`E zmlbCqGzmZ>aq8iT2&dE>$#4!iz5m`~2CoM|!Ru8055%?`7tN#HfJ z8SuxlHVLrvk%a*=3j1}l3V)o$Nd6VmFCr57>l(oeg-U4riR}8XHiZNwp;6yVBm^6D?kLr+h0 zSy@)Ppsl-|VrNWV^I0zL$v4$Jw0q+&jThcL6R@npV(Y~C#H8e8&W%fmc`Gb-zjIDy z>#{jhCdtXmOt7A%-8Co>Ua z>fDXD9_yP}**Upk=c1mC?XefPZ(X%;`s7J+vT{m`)~G*t@(Nb$;6ytgZCX4NFqs5d z6cPObxP&7BQGKy|C58G~*CBEb5Sb)7(LmH zaY~4eCgNzy>ER4c=|vow-!mlfsb;1H3L zoCrCmK>4DUX4=53h=v!3_w3A!w6xTQMpW#get=4Iku$`ZoXN{d3lRWBt`mg;4Gqv> zJ~yNKCHg41;D5yleMG(qs z1ye5z{j)FZt=LOvC&72%6A_ayVI;U9;h(fPJ`EClMhtUWBN;^WPUD$?2R{Ar?|=RB z;r&n#vdr~$6=fya*)f5B?x-IHTW?Ix(5Jus`=7sl=5Rd>?SSQj0=qc0@Jzrxy&_>{Syob9Yy_H%Ia<7au5;~*=H&f z{o1AT=g+C1KY#7P3ll4#MtWPzbK?EooGdL(UO&^jd+X-)E0;7iuiSe0!pPDVdqzP_b8)b>G&gyz|KjBeFV1fY9Ic5YrnM7;Cn%v-Q%`R*s##RWZ`{*IPUZ)<9(tDI6h^5gckYgaCvKX1<5 zxpNmSUUDHZqo>>{)Q@KZzIbfkkK1={-Mnezn$>GouUxib^`^s@ZtFdNjr6m)EBf)p zqx*L6+O>1*wjG-{ZrrqC!}i1KT6Z74G&E!Q6G=mo?E{UI$B!O9eCWXb1IN{`>pXs8 zXkrPR0A-+A@>*D3ke(DB8tCWkg|kNfjs+!TNwQ5Dv*lG)5FCRYB$pkj@wTl?Dj#cZ`@XhvNBSzudY0 z|4sYvOv|hOj4$MZ2lW3z!3|c-#WMlxTZ`WR+!+@U@ciy0hk(@FlJe?CY-2Umscy#F zD#tHAF%ya3_oVv8Cwti%zqIkU35ZS3d0UVkVrgu6?UmBsmt&X&LdMzOL?m zabeMs{vO6=uXS&oJ9qxZ6Vq;SZ*zTqdQNt+w@a{|QuU@_P%CZaP zS+!M>1<^sK4#Cbw7S^_RZavV|xukyi`n5ay=C(lKOM1GR3PT-Vh1tC@vU`JGMfdNW zI-zyzrjCJ`wH*i17d4k8#>V)(w6{0K5kTYoRh>Kco)}Vw01qX$aGnX6B?)1f1QceT zX?0~K6$8LN$h>ijh)4(Dk$sQszRdoa0(77N8{*0!N9 zu>XiLqKJSE>OF(43|kCga%iW;rivm1sPv*w4aBs`GMqad_^}z2y)mn!QUr)ej(4Co z9YPvX+2=2GIXLWC#Nof?v=h@GfN})v_>eb`T*%CuGjtd?&jd^dBAy92IaSm}9hZ6= zJRNu@U}UWchsQGk!-Z#{H=qE>KltaM%zDbJZC zC#QJrZFxmSS#e3d_2~MR z*^06gWn^S$z4Z1&i>=r=Q~@!$sHgn+D<9p}3W~B5CQg)5IA!PT=?fM*wAmv2mvl?2 zj7>F<@l3!x6EI={%=O1XoMrHvrKf6YhCr?b^CwuSotzXH0h6;8f&qER{~#Svmw;AN zK!`?G59XLqtbu$Kv84Gy8OT9>j1MG zyHBX@+IRZuy};O{w9IVW)4JHeRGtag^zMy^543OJy`_2K^0o74&OWko@d*x(#PL|% zk>%~@YySM{(`V21UmF@48kxR)@WS51FEEVccrsgxi(>6eaRBm%$;l}x zscD40W9_W4p#a_w=pJhOar zr66l-DuGQ!Q*vr_R*+ATeR7709f|wOeqenAa0Y3n)qn}muZOUIY>+l8^2z?0oOHNB zKtibCfKot}4J|Eyl5@sRn%#9;EiBZ>UB><|$jF)fL#;nvPr}K$YbXuDr0rN+ME#u^ z2b081j=Mv*m`>@m4f9OE8+V>QxkdZQwL4xR=tLC<`_Bx^YEN=;d8Kpd@KZ12Yd@-~ zZP=-L@w$U|Kq$IgiNuX*;km_8uCLB+-(hX}TxZ*s-J4dOJA2*6-5Y(ha8EljgWr~f zIltPu?bg%VI%ig_U%!0D$>&dQ-FNm02*w9io$M1H<85|(ZB&@~)gzm?AKX22Yjl`3 z&jidf0Ry5-(GLvTyRi~d$01m9m#9{Q+yBW~fCtTfO%_f85U32s==8AtpNt+CO6{L# z0{#oexFuWUc_v_<2{AU8WJ z69auv|3XZOk|`bBb64^*3fRw?ZKR@z$qZZPhB`T`G}Z&dk)J)8N$zU zx*UH5%ouOdMLm8Pf&g+{UX4a$7?OeaL;amK^%a7uCKmhTE=MDF_U;3pKK=Y&(%R5e zUKX2>S<-+kCcQUG8mdVC(?5R&2wU9RTv=0-65S%de*Cwix ztREf1$o|33-96ANEGaL~j0$z}^3c^hr)?IRUsi%js0u6tSRVR*c`vFg$&86lj|_7* zd1LwX(NlwjyghDayB@aXW!xYUd&Kl?X&w>8h&#w4X?WaZ>{bxV31 zg1kMPeZ%9DQj%l56Jq^!b)Mh7^Cln+WZ-FCy%mPx>AvtErB)vI0vx2G0b%|M*E& z)zjxMojS68?PBHWvu_4QCnWPsz;}SALIV`6qA*@Kca&KcHq@uQzNLC``6L^(D1k(`96qvLhdD^6VIg!cTfyo11EU-&{YpNI_;AoCZi~K!IyR ziATA)IseK1yIbXC6dbKki;L{5AghmOYr0F-ZC4Hu7N zVv5WGyg*$`*!n<~!8Lzn4Z+YB1cP<`k$wgJSqaYsT#}#9XjXY9;9owyAL-Mpw=#ZZ=onYg)IbVv z(S`%pbEE!koTL83>pT0#S5LdPY%6g7pg>&66towys~d zYSk*JY&_~484*E5($$GbNV(baOu%Q3?%lR&<9eP67{vjwtujHm0G5eJA1o3JSqx*BRS!c@o*X}P0Tqv(M}3W9R{hNR?-(3Lc(UCD?-1v7n`y$+U- z7-d5fa-q=0;f5yQnSim-)ww@)_j#BGo9})YH({pAxl1>+@rc56 zExEpW&Ab`Yr^t@^4qVCKe?MlzL^+-b*vr$yT~JmGAQ`a{gRD3o{2^KB9f&UF$uXh9 zfdPKL-=KpDSWpR|%z>Eu(DIx`Vp--*EFJ>&E+ivtn#8$DtY-|ZOu+-lN61jQZ-8_L zR~Hm0cPAFn^@N|MPAc5@m<~Z^TuKT&6Yx7UnGreOymEB!o(=03ty(a1#*FDon?qRw z6yY4Omkz(nHotdKW#5ncS1nq*diJc@GiOd;nbtuNa-IoT`dINyz_2<-HAqN2M%RI8 zAmW0(C_dl@@p&Q)3!|l693nAH&aoIWqlewYEa_EhofKT)SP(&cD;*>v9kO4kd_+Wa zi8PT^f!=K7dMq$(eAXZ>X8(+bRQeav7fyq4>9m6IOu)48zRNVeaq7VKP1_dCSiDG4 zK|xVbVPOJV!cq>Dn1*|O^zWbCy?4vH)w5ur<;@vuo$J zB@1RwR#cRi!vuzG8HP56y`i~@sB{t zB$a)%L&eumAK;mQnMDvVDR8@WBC31_wNRz&I_$Sh>Nx}=Y-_%K6Vomc+b0c zV1=byN%GECY3@t%npQDJX)8SV2){m%XcRNJMlTd3VFY2bu@Au9!Q0h7zq0 z@`?&_GjEzWcm_s9Mbr8<_@VgPseOwV&6zQ6@?x-w_NT3=@nW*P{q$^59wQY5uHH&a&kiFA;UvI;9C-t0-=IOBOXggb;fJR)jo)|AR&HOu#%7@Q-^B9#!X=fQt%I43L{g zX*~?W?y&_2$ygvPyRugysAjdBw_ zIXn}v)czT6h#d{VGQl$ex1!BC79~kbZnU?>9hE&B7b`1Gox4J(NsJYX6)`e@r=uh% z%FFob-tFrbPoE^CFm=gGv@!omURzRL6!QH1?u{$wP63tO6y*)!5_V2QjTF3^PC;p9 znaADJ+t)6gg;xI)6_w{})l;>3Lwy6@ULq_mY8&*wb#&9pWiu6JCdkMtOy3n#Pf5}W zf@n$V8fyA}@x9730rO124EGMIA>jNoGEFxXJw}1TYm!F3FI%^X>%^p8}Ze7!gLVmC=0UM7VFvEZR@sHnrc|R;^DvPv# z{rKVi`=M=UwFpjTY(71G1HVH4`^TXnabvc-$;L|N76TcSHTHg>hb1Pw(A6cSb9w1SOpL zd16W5z|ipD|NWo;>yM8-6R_2*Cwe;f9^8M14uJ3u{QLr_DxtS;aCmsABgfas*vj7C z)rn^UMgwXLA`69WeD=pt+ROX|(C}h!qO^WpEhiuG*rlBT(4m(UNDjZA4f64EC}0YV z$ah3Oa`5NeekcIq$uj{fohb*x8y3#swc-?yjGiJN(>Gejw=Pstlv9*b*p?`OgH~98 za}dB~5s`x4k|@h(r?#wCo-8k;C?~(dJ1>h0x#*zO*%6sh(bE$0{Q99yD;G_Zms8}K zfDt4#v$U~yV9|C~c9!uc(jsT|=nR)bd#mcOjj#Q3;4#se4?MRE+J zYf)4OmOr9*Dg;GON>XAX5dji#pQMD$heZ*r9}I9H0tC%p3YbQa#G_;Yhunw7F=z}l ziVHyW1EN1Bm0nN4ex3rUuK|ik8d{Wyf^MiB+r44k>IKU4cfBbF%%}#e z?IUtYm(PQHmw6`Oc{~$vsH+vv1k54$n3KTr1e7b_>JG@TPB9&Vg;i?U%;YCm|*r6QaMp^(jthyTUaZphdIEAbUwxe z>@pJ5Dhf(eS}XB4a(^T?SeliIITcc%A}F8`@ec5TM&xvcYNzBX6zFgO(li7~%a|M! z@JzsieH~5JWraKwaCTZ^JbL+rg@y7=zybjr7aG#qLh?{xnq<6WoETCm%#6O{Toe=^ zQVVjXz^-M6PbyeIIAjZ&lyeF+^@#mr3XZOrteeaqGcY+baJG1i%zzc9l3b}W%>Tvy z8E67DzI1Yt+CQ$B%E{FtUyEk~K7IP+iQ|_&{R7d+sQ`WrEspXHTC|KX+F9rHQq@lMBw8O-+JAL1v7f%bOQ^+SfHMo;!Qt!r3c# zAHO!Y<(Yu#StX1;>$m~K0>@`*33$MBGSgF&6BFV|&IY1U)m7Mdi9nj#faOu6$dqKr zfT#!jo^6VVDpMn|;^IO?k8-ooErhlE!rn(4AEgM%F{VAfk}MGROSwY`*$ehoo(Y&| z0{-s%QKQFxr=)LgW#{BtFBE7^Uaw=Ky|8c&?j*T!6DLlP<(YuXC5Q^@drd54HZQt)vaj6{*}B3AlRP} zejn)RZmB5C$;wR4tLX$Bfn^yQBs~KIAAkGx<6s}i0fqH-fQX)e9e1elps*z%->hJ&f?|=U7(@=j80;wI1)d>2hgP6z1 z+ub8Dx#F#$f9N0o`6rSG1AWMKw>MOml@+B&hXr_hxVXAFhZGj`Ou)k*fBiHpX=|)| zTV7I-nGzcb5TKiztBaF^i>HtO(D3lPj~|Cc%{&t@9j*|pMOOSu!nSh&0Go!;? z9lac_Odj96ckS#sb+uC`PoCnLfHN`xh{d2R4yCQ>{EFBUTKOXTlb@HDmy3;vHX|s& z<|w*FXJi)U=X&|For)PJW#2{=NP`8)%D8Juf#ekaVux&;2)Gy;SN+4Vcian*@09h! zygUF?jeByaU@7^cPQN5y?{ar zFWjlZEz*+ip6;3X?!E7`_9@btxpRMf|Ih9Y5Y{@UDyQ~dd+jC9vwX>-B}Qj+TF2xl(f_PLW(;mOcLO?{3^DaFi^ARoQ=ou9Gj^Zl ztP%kVJWj+&7|TSZX_mxK7-D@G%%;IuL9h&h-&ME#MY6K!NF{km^L;l zkzU8 zmi&mo;p`L_kr2Pfe#FTb8=0Mkj~Dyl+&lSaa(mP&t(Ez7ly z^fT{2IlKR`K?ZP{$}4M7BSE2=mZt2t8XH$HI-vKuDl|a%_}V4gAA2Qaio~Uonuez4 z`T}1Ao(Y&|0!Be%@d%oabO_*P9}5C-9zQc#JQFaU3sv!V_YCC5#D`l2#)S*znE9jLl6STRC}oWMRoOZYMGnbU9%x4T`wQ|4mK7p60}DHj}@B%m;zxQ zAn)t8%jl~VrN_Hk8@~ywN8JOq3Bpuk*Z1@e3Qa`=9THKLyZNi9HhCqbRG=gV&N<0( zdAqw934-zzKWn2WPYhy(*~B79gS&p9(!Tp+Z<8?6%|uUAOV=Ygl|g`rL^e0?-$E6aQ!Xfen&; z9d_K{w73==&brW7F5^H7_#a!@pOT@UzB-*K2ZTfaU}!saRrD^AhXxZyR3P!X33(d zezLFiQ&pwodruzSy8eMX&jf7#*3!c_IJ8aLR2bs?COF!|=BlBm z%`Mga8`f{ReBL;H3q-Mp-F+swvQ5JXJB zjfDx$0$-j9m^sZX)F=-t()wraIC;~+`+-&92Ky@)=i=OTa{AezcqnvuCg75C;vy_7 z$NI00H`YCW#m&p|t&_t;OVc~*M_+hZ8Tw^s=jIg@qApMrZF&CUL)%biv*%Y-UO%~W z@t9A%<+CSI2?@#RLRni)W{9)ROM~nnTRoM1I}a)!-@fviz+6i=G%7keHc2KeO$cz! z^)ipQH-32b?6nI#6Yz%3m#?ZRUq*?srM)YbcSeYtm7TxiV->Ya=MNk@aA5C_V;bj< zUU>4-#L~eH>%UH%;Smz7e@p$A#`*JV7f!33K6h5>H1wkRE5AWQ* zedpeThYvL$KY4Kd-YZipJ4Y9ix3<;i<;T6%ceK)fZD?$Q0S*vL8++!#aM=OC4;G42 zBLn^X{Czy#oSmGVUEMteK7N70ET{|5q@lL5C?_5Koe41!pb8HR3Jwhq2a_{{*}}>M z=0s(AF=)W$#s6_}@kl}>CUP{LEW5zEkii6+otZ%#pQ-qhlEOueak6P@pf+Vx_m_y# zK#cT6W+oAe)4`YvPa_gVXKeDAWWd{^%mLOvJ6O{%@rND3@fAnxmKG_u{_#6@^v0KV zY?X8`C?HOqP2LvI1T2+xMxB^5Zuv2_xnt&S+PqR_#rUZUwkxgKV3?Mc2@4>TiEb{P zJbdz$4ab*#zkc$06EM#NZ0i%1kRt)GE5&0GR&8pK{nXXi(Opv#YHi`oGXZ0VmG4|b zI~d}9=*;Nte%DuLbeBBVA(CUZ|4}-u9hkwLGHGM^%~Ry@5|SXh82cav3$e}7cGpy9 zeA1lBDTF~~aBQ#*5EdeG2X3HDrBYH~80i(_b7%K{ZHv6(a$>S$l`ItX zX+fz=ZFP$%vLNKG@*1_*$YMfBZX``jIJ@}X0!W;!;C#Q!$4}h_asrkR%OX>h4nLAs zLrYykOsuYo>LpysuEMgRI35W}r?I_RD0IDd<}CXguBXcLAu%a5SCvbmHBX-+Ij$lF zRw~cwL!qXzQKYM?{J8>$G$3T74f@RuRb_U{C+OXg&Yy$*BU@4NQ&||Lv>R`qR`};Z zXI*r#;8E09r$_EtkH4|U!gQd?#EjRkp*r)<_Sf`xxVo^uLxWZ}MtxmXX;xOQ{ISzF zrU0*)$?;+FOu#sfu<&eqOGbdj%Nxq)F5I(At!t$^Wr9S4xSVE%OcG%2_~!QcQ>S*U zoxeiGt`JBf)Wbkb;3Td~bbY1&?AC=dhmY=GK4;pzgKv`aakv%}6F8<#TUCbl>ql44 zo>4oca&YUW&C3=n+H0AdnwF83BPu}sSBu#3@-ChU7^h6Eb=r0$DdzXi-r6>I{HSF- z6ENpB;7H5amlj&RK&7a!sjTqWz2OC&mf2tlM6xA_472+5s`8Q?VIJ-5oB}Jthy6gK z4hqN!W0HD($ZQR1;1eJ_K?VYONJNj()K796bRT@*852|<9ML{gMpT3QBU zfKems?d@u7s4U2ci%u=BWEguuOF*#^xr-JO4eZOn5Mc@=r{9!+!30YB1ai$3;p@Yto>z7WS*td1#y0vRIZQrr;@CA*V_qBA3 zi^bSVic3UJTDR1ACg9R?@~X=@2rixpxTui414(tx`7Ik)&zLrT#5dmz`{o-U-3(K> z1tRjIq5@|1YHif6DJw0XJ$WoZZoa~Q0K%DIC?PovpQN%tOZ(BKV>{+e8Z+#huW&sS zhL8F_xdcotkXKYjJTiBDaB%Zd#gW5s{a?QR>Z`AZeLMcOSeTVT9|z9_?67&wnuYUc z@l3#}g#8s08RX;c;nuN}J6En=wqWjxhbg`9n4B7Va5=>F4(iIMwrpRy zc;SLsv!~BJS22jYO9@DbZq73S4;C1zty;TjDbN9C&sn_vvVnsjG!_LALLp=N?eFcC z246Y2Ztcny>-VYYnc2Gfg+(W%WS~ib$@_YFCSXvm@=U;tca|{{ccU1Asu}Sc;)0|> zhM=E#${G7HEPY8zIR}r z1bPY)8Il~vWUx2#^6vc`7A%}KZOXixah-h-qjVMV8l;*at}{P%@X+dY3ueujK55eU z$tR2ZaWQKH!u#qQkXmY--@9e=@})Bt&Hxkd_;IU!rD!OnZclpSJQHwdPn+L`9m|$3 zT=4xi)%$v{Ol+LJ{DQ+HqvP;d(Z|)%(bbq8=<4Vd9vSNI1BTGB$QUZ|P2)apd>RPD zNn4ugD~t1l85!vrnOQPCj#@V|CTza3Jbbu(5KDj6BmHvqIcX0 z_@9`1G2x(r!Lb4Wl(U3LeAyuCS%kAG%14cw1jhr|f1U}r2cI5&NZs_5WLvqFy%NSpKd%Ecsl&6EK!H1CM}-0MIqjH09@!{orf}GY||b03m#U*%Qumk(k*JW!3^J+u62+12r-roD}{h+io*~i)H<=qP^N@p(JPw%4r1fK*UOmqys`!LX$ zAM0svrgP)WsgufT8rF^6UQ$PwcMlHy^r=2O#M#nB`--ZP(n+O@y4ejJ+yiBxdOp7U z^jmeBz}C#@0nY@?GXVqXmq?;`CSYVtKPOP7t@S0DF#&F_VP)i-lk*PFF|1##Faq-E z?x-zD33bxHcTF>(xr50m*(zWEgM)qTwfU(LF8a6CuUxZhkfA1oah9?5-}&*!_kFTT z~%HHD4$Wgog!mZUiHKTD(n2|=U;y6 zuFH=P6<9vGbVga_g1UJPvb^NsGxPD&kH7uVRV|G4_cD8=rgBPI`P`#4qC>$3LF<3u zw_pDD_fAPtkid;+0_K^3ff1U+4l$`oNy$`nfI$WKm{4du6V ze?~RNv2^fEz*L9H)<4e#oFaH}Xyu$~6UTl#e8gBB5#}FtboWA&N&pd^7RQB}8ENlX zGHs&bm{G%qj{%DRyu+6E&h8$bo``~W)cKh`xp8XkoJk7bjvW5YurZ2?Qx_aFFtM_C zb@xEwU%NC&=kED^s~1cdJz~VLZ-$Ri7(Zk2nWwLe-`Y62A&4St&(*%Ex^2sH#gW5t z`G{}Fj-Rzg2s9oS0jsEtZ#+x38Q(o@WB4s<<+q37BftxR$00bS={cj{&5t z$|9Z#*x%ym{aY7KU-C^)O-)Nr$MFGY{NMiekAM92hn^NmcC?qV-tDUw&s}y$3n3cG z+B+yX^2_gk|LZRwW%cE`;SR6vYN%hhc+E95G%O-KLdHNefBXd%QoVI$g~`5eb+28- zxb5N>6cQ2|D(j*c!uy|p{Mgf0Db9>^(SLmT-1&X9T@!h!~33A zi6|w&UjOl>(`U|KdT!_B=ItLu@}8c7!FTU^WpxFaaRLk7dza6gy>Qdm%F)%!$3GaM zy&g~@4-RxSR^}!7+Z#N(u6FVA^`~amkPCeMak>Qr)8N2Bceyadk7oiVDz0*{0f_+< zPFRGfSfqFY21*aXfyb(6!ADAiX96xS1I8n4M|+#UrK_3ibDgK^TefYQzxZ5QBZEmM zIV_8;BiTY2?P_oQ>gBa{i{?(7vskIH4j{*f@-U3Z_BvldL0q&g>XzIZAE&)>Vt9Po6YRaon`UYtB8=eQiQ~y%1x5V{Qj2 zt>3s3_~6Tyui3U!GI!yd2hbzW`iE;`Rl}t{x$r#)%DE5f08GAE~kOXm3;u+zN*m4|jPJxkmlDwoC z7Y=RUtW^uW8;y;1c&EhV3o3&@#a*4Xg@x%!1r6vyVL+(pcNt>x#R~4sF+ROx)5_Hc zc_!d7iV71J8ifRfgocG7Oo8K}K#)_Du6J<5lIar^M~@k;pfGunj)N1hZhe3%iZi0X zHp1QJ&X%P-6L1R81Wa+KA)ZdLnCocZ+8PkB;yQDP>60@eJQFYoNP)nQO}_74Z+A;w znK&!EyrC9_gXJtqRF6bWW2;Qo^TVf)ePHq`%T5jsNRoiyt{h%iVF6Au4J}ex-!H%Z z^sZ0ZP$f=F32=80Eg?8*_;8}e`ue6;Su5AV908YIQSJ;|Gz+uNHZ;yRqsj7=*Xz(=xBO>;>wQprV>%Om&+Rit@~Hb^Gv`-Z;VafT3FfGIZ$UxHOQgS zcZzd$L2g=XSb&cgs%tS20rje+&J`$-OA3o!kS9!wj|vY90Zn*-e?UM0N8?LMAh4&t zvjUMYEh#=GDk360EHpF(ggr#%NrSoqi;>*R7YRY)8y_1J6&Vo$InM;lxVky&AAArv z8=zC69DWKOC-zT{C>&=%)_ci52_(-dumr)jKnEH+1aSiz)Lu+9h~*LrgV6rVF@a+v z1kxD}IRN*g*I-QHg#}I6-U;--v9YDGFwWO2fM){cnSh-g?CtFA?H!z)U8=yHO=QK0 zyJ5P4+aohME)s2m{(io`zCJ!aRlsGyz=We2B`yS!YgTGvY!t2jkl^5;s;X*|)4;4Q z6%*EjC_5u1F`m;Q!yuPdz=AM#`$C=xm;w%ftH7$J@ZC_26RWcPGi1Rt0oz7q_W$tf z-~age!$5aKT_dJpVYV))qGYG5tUM&wu^n6KIgDYbmf(Sd^0#5$x^chz1mED?8ti{(-*#{@c&*`*8Sx zFHv47OpOooc6GA1vbM3Yc69gY?`!<8zv1}M-dbH*D=88sMn?KMxj5NaT3T7z+By$QCR}|+J3R5D2eBGU0 z9PMoFY;Bx81Oo#+6EKHjL;yt$#~5c$W_4H%A{Rj$!Z$*gk8mES{Ft^;7NH{m6e=QM z#RBwQj#fgkRh|hLBp{t#9rcB2iE$Buo))Grp1*kIlG)UbdU95CLF@vus^aW~n3zyM z4@Y|w{b$p|{;tOM4t zsh-I?JG=Y3+iJw3{3Qd9 z6Mi7c;j1EZhQt<*r3JY{2GpC9l9DW+)YL9U=s<9>kXa)paFp=n2!%qrJ(prbg07yu z2Hl12?S*LmmFM1|M*B9~lW+tc%JuO~z!hA6amajdkE^wjmG3r`=r|TI`OwLMj1J*B zvV3T3z4kV%IFb`UvfTJUOco2}|1Glq2vu`mCMQ%#xpAWA0%p|Ee;lsw?A_MhK_`@< zH-O22J5c|Cv?uX}k3b-+tCUg0A@@FU6DfyUiOLpBtsq~Yru5j-CMgv)%TuXzbw^ik z|3Gb$4bKED0Piu?Aocg+Q>)D`DH5f`g}69+2Dw_Wafda5YGgR zq7f?JuNP^&&Hb14kEM^KIjA<#XjP#!LqAL||GfTD`_DZ}Wc^;O)wBQC^^fc6K!tm> zHfUZow*J@k4+}#I_%u5Gj+)!pwAYolcaeKDWB{l;0Rw7}4!hXeG=$nki5okJ(~LN2 zF-QpIrdR_F6IrLAlx)BhT8aZa%o8x@Yx@<+G>G z*?j#;TYH-I?Y(~XB8KqMv5AI&OZso$+GiEP3d{YN38os($FQ{G6ICSLr zj=eh$?Afw<`>GW)r_Y$XXv=xc=cw>+j(BqX^07mkcOKupe&eRCOXkg=JA2BMImoHu{j#@(vd9_qa`dJAbY_`OkF=+y!cFaxGr8}amoEcM4~cmuV8rR)_q3J8d080g+`p>{-mQe>v@F0*<>2{r z_4N-7y!+78Ac_xiHq+O-b6?*tJSGVWLSZ(nZx6`_fBYyd5@v)rm_L2=$XF198>FOV zW@Vv8vK#(;e?MSaJ0*Fk-Zrmw9vgcGMaL(D$|fChGy(Jhw`pK-V6eL?Bf``Am9DvG zNDQt|0iQ9+2?qjX#eD#9X%@$M+gf`BN5>~6gUmQbMAo0$6!04OgGf?SXdvk%c*V`JB8@&6F1=CTjv1%!>mL#k6%!lBGXXQ_l}%p;07|1BpeiCLNt`G!BQXZbyn(%s=6b2q4|Ve9vcFF2dx zY7E?jx+}gA$$#XTfO#fho(Y)x>`>1G9Dh1J4-H!8SbKE91&9NoVY5Z^d4I&;&?iHY zexQx9%APOQ7c(=+I$>?n^88k+)07G)>1%VWD zyf-R(TxE~EfPM`uw?a(h>g&&yW^9faZ2P|t1+FR-<`w7$EqEi2Hjw8hT6uD7=x zD4clz>{Z)kwRP@(^{shX368g}95n7m*q*8-=>BvV;F*AvT}_|uJMiGyJ?#rC*R5Z( zR8{Z!{TIQ}afwKph=QzLJuP&vZ1VFrzj1u?wgbBt?ezDzeRb9#GCCHYO`*4$_JcQ; zy4nSP_AgaW?Ao_;$Cc<%d+S?|LL;K^`P3viXj!KC*m!6AIhd%OKC*wu8Fe!cdy9u} zgF+%OAIoDs-ejbDTA4+9+nZif-?3?%y4uy(JQHvRMkWgiW3G}@!IcrCIRyE`oNR88 z_d)BI>+VDHkX0f>g*+Uj86l*2H3sN=*?0LDt^$Z>0w&|q)KnIdWA!pP()9jOr7gQ& zdHU5hP!$#GGhrX3g>gP!#-^`qZ1YmhuRc{?XQcfgj%NbaI;jO95j?(%822}N_U_NM zDuW$goH)Gez^?7rQ$wtbE?;+c^Mw6w%?~ng$cuFJEDm%qI(78m!5wEV+kh+i`ZEV7 zci88K%rM)$Tz?yr7%!_=XOy;X+C!x@#lP z1k5u5-`B(TPYw+8C$RswRETT3`YKH}O$csy^Rl+5n`|bWA#xttJ7p5dYpd#pq=09O zCvBYnu&)!RLAtuGmV$xOiW;$Bnzo8f==DXjhbzurcjBg`5q-`Qlv>E79Sz2Nw^_c_ zS~g;mnbx97iVD;BEgEfAgmP|iacLRP1l(>jZ^T!lXM8tir_1tT|M{PP`O7z7jhwB0 zY|NOE%S_F!;YX(3TlANm+8ai1d%J8n!Q# zW8&eNUw{4Ww>%SY9J+%dse_AW0xsj3fO#h1Hej;iduaiNn+Zx}NKSoE;7O(ruBoA-C|MBX?HezyZ-$(^J=&ts zmh_wJwG$x8FnB4bj*f*p+w&Gnw>z49j@_#9DwAufOaQ+1ewAS5CrEIKJQTwrUc zedqF7>&W<|)bz~kHfcv!wZEsEqjyMjd}2bRXKa*@mgckj_Y8f5BjXd3+q#MkLQ=eK zjb54A1|+6uMtg@Q`ad^#cJ07zcR@f{RA>5z4MxVAw{PCM^WcfGS5kgPxT&we<@LF< zhcsQ>y?oyuRE_X3d1K|_7ZBj@>+KVfQW&1->Syg>ed~mttE(H&1dLCRn|`RfWP>79 zJQFawj)9_6#KQH6*3)3E>L+5HTjelvtLm-UoOPbVz~o`7KSVaXB0tN0ka6i zMXpDPa4k^KOt+w@8)9oLjz^&aw*C>q#A0Ul0dkZE!8kDEEBCs;SpRf?RJC?U8ztg` zqUshILY!Z&e}vayzj-ELOGjrX2U|8@es3-vyKr&Cl1T~+ zpBPy>g~TN0RUvDI4-HQq{L>`~#&=F1+`D?pXvI^{L`7(qt|hi_e7H1)cqU+qv4h9E zGJe~V5tC{Dvw$}3fG|8f6EFZkin#0t0*zqO1=~RpNIk)}SVpXs3_^%0hygU5lTCm@ zBrdO@Mrax^OLPrE-~gzQC_`~2F@aLfgD?+RqXbjPXH370$`_*QhGt-8@Jzs=LB8Ie zuFg*WS!pTBcwTi4KmHC7p$`K+(%Q z)Kui9fGJ1d>E`6*=o%Rv86kmfX!-58Pe2LkX>X}5%}$OC_QUO+Fnj_7cqU*;Wlcj< za~t+Yo(Y(-gu?4a^DxKbE9RMiiT}7NAJ|&~mIhC5sVVK-vT6OA)oa)7aHt?q7+5x# zysV6b5Kmi!C%4a@*s*a7G5xOHsGME`ATN?v7G>s&B3!MEcqZUo8&dU-{b=>o%^OI!;018(jOBzaR?$CS-+$*B%lR zH+Z zVBull;l1m&)YUq>edpHY%jVAjSLyVbGiS}*6rY`&S5PR%Lmm9!c;oIqrOnG%u9`h} z)~p#bXUv@a-RZa#lpq(1>A}BidVW{+z^3mPE?zKq&fHluXU&?v%q%oMBS%zF$Uc$x zMVcB)Yt}4ZJb%udIkRU@pS3~TJt#IcD?3-j_Bdpg2ngklerOBuUsS=M>zabikxL+56WU#?nO^N-QZotalNIt}b`WZ=! z{Xj7f6n7!w0ThkTWT%X)lj0%J;i4i;PQRwXDKZf$a5vzt2V0{2JwT5%=noWR0Oi2# zIpG%?tS+9Vb?AETUnf^$?*>gf^aA*I;J0`}_~(Cq3fvp!nSc>_!RCsG)8Ark^zzKE zo!gc!oH={IK{yK(y9(xvktpE`BQqzRLkY8cyl2Sr51g2|UY;lA9brxq;(6VbG3 zQ>M;dt8`!A(%Cy86jZ+?#T4P0fVl;ONIZq%sg{?@d~0ilm_BJ)(t_ghbWDycUoDtG zNx}wOZ!C(58&E=kZ5NTVPDRkkh-9 zuV~VduYbH-TDw^BwCd%*q+OeSWOtO({dp!}i_2$sZ`!bA;j{(cfu>hcVTE@qYa%Hx z@52~OH@Ts_f6Mw!^S)a+A3VL#m={@vBO7o`(VEuZB`|oTvTM)ARV!yspEyntJYAC% zj^?7qGdEWR`9Pbknc=w|+qNv`nSh&XOGKHeDapyyVVIhll1lcCrZ`JuQGI@0bp--5 ztU#6uX_@_^CBU&uGVlnV37E4dSl%s2(9=Kyk-lPSbKi&3Ce*_{G$7;#mHU$d{FI^V z=?-K#Xy7|;Lgs`j3h1_EGDrwkIavqn3DaPXQ1fRd(Dt^bs=VYdfs0#YB|IT1%0h^# z_cIe{yR^ADImE?8@6I)Co(cG*x^bpZ2nQgCthE%AyEZQX-L(%dojP*l*s-I>FBv3a zg`{y!gur&FF3a&T)z!RpQR&d3Bgc*_Up9zBn<17FT`y~_D$R{?dj0U`Wz}Q*_8&fa zOy#OkP$)E#l1ScBU!0#7Z2R(##<^2RK;(Pq=;>=_-u}Vi(QyfE&uK0cCJO9dU%#Mo z^2olu2M!-QbK43uVZmWhF*Gqc+Dp=+yse&IJEx+2>;P_X?3|{xlZQ`WNO&|?!`YY` z;BNiwrrO!l#}Dk@cSQN}3rl-<$ipHrndlCszV>gQ-&Q|!PWjM*BgfA_G6ljYn9RZ> zF|F9-651MQ-@1JMyz-F~$`IQEVbnh;BrJl>LrgfC$l2i8Q{8(P&Zytfw{>>&68Pcv z;Sre0^b&AI8BYp-?4?Of4JkEM$jRpm(-48fqf1UmNJvaV zH3MFsP1X7uv=){S0$8?Cn3W~W#0QW@?Kjky1DBujw;aAa#q)S3V6-goOu(3#?E`=P z^xNNLMX|nawl8j+IjN+4Liv(8EpH53f4x6``1C_lPK3Lih0fK}Cr&CIKY8(GB=rC% zvh~^h{{6eI`ivlF8xt*!Q^!vnKYl_@*FOle7({@$zH_j@y|p&Q$IkTSeRZWHM~|O4 zrS`-F5LNf29wKL8Y^=mT#cVSxN-j2;UhonAe< zdsB^P0_K^3VKCtru+Ap(I%o~ZDpI_jT|a;R{I$1vZ2u?c1ocXsxT7gE#O3knz3Z3G zpE+}rMN=nTPX{TU30T(NB}z=?nSgh$SvqIh>x0U>qP}Az%p!3}P3fK@XkzZ2t!n2#5lb!Qhe*M}_{FKg{q67n`2YU;p})OK6wNaMKX~-;si~!{6P$E` zFODCGfde3@za`V#=#7P~t+RtEI7C5Y4ytosKlm5$Z~!#ajXgwMQX)zY_xJYp2FEbD zH^FdlFere9zJ# zfd(#z6+pTjIN*82(V2pgoCFv&+Q4uah7q6}NQh4ehX7$FsGL)hn>ZAaR@U*})L6@q zgsFZG<#R*?)d(U-)af(ij|LR(vf5sr37Eqe1P}u|U*Qv!hCIEZdG(Zv>M_+L%9h2A zfKZ`YL!Jqkx<6VY+|on*EY2K0v~|wJ35tq}6K1T?cX4(16!^045NWvns|OFSty{5i z)ilL%W5y^$<>W`NCC%g zbN8C+j}@2Ir5_BVZU{P6bW^Cpi|7&C6_ECX23hw+adhn<7MH-qTo+wN>Do z-)jHp%%L?iCQleQX0+m1#br(@331q8;9$12HhV|pbrkBJ*uD%@*@|O$Cg5Y2Z{B~b z{p{r{ea7-eYp5~ZP;JkC#4fL2y{LZu_5*FbXRq`PjO5J`gv?VVOv=jTnSiM^kY@r$ zsFe{37NDPrx@Vdw4%?121Hgxgwi6Hl3<#+A7NGg^@eM%l5?tu^{_fV=qRfnh$RJ;VyQiDs3u6mgCwFgwH{>mFpW8c|CHa{t zago760lpq)hNf2VPux7bywR>kFVNO5t1r$?ONfpR3-Sa5nytN~lZ#y503VzhiNP=r z_dX#UOrM?}9z^xw?JXe4tR^TRMvn$5o(Y(Gcz7n@%1SblIH(m6Q!l7^ftgreo|P6R zaJ1!_fI&2@dh&{yrLCQ#b9GH!SzKbdI5Q^H)70#R=1q+YXHTCwcmC|%7si&h4o;xi zt*sU3iqj$mPKM95@7_>XJAd}##j{uL>*$+VJ2<1Yv4+Ts6GJ_0UcGpH@A?&uOII|` zT)6R2PamgzM<%bWt;!B@v3T=B`{CWYw{KlnzjX7V&I?0R3rlOr8If;wah#jA(Q_Rg zZLJ4)?mpDgd;S_2BNkReaXoLf31BoE!PE;0#W=rEpgKhgh!Q0Ti+)lBl~n#oixPO898p}k2Gf$i63+ze z7?_*a`@>)V{BhpXc3MU(Ey*v{zQ4oCiFaW-_5^;Whc4|so zL}-wokGH2YFbD;{0gR(yaG(cF#1+sGWv8da$3=$)`uQONfD#CFld#T`zP@gRY5@LL zR+I-FbkQXsK9`U{90SBK(APt3mVme*4T{fYWnyGxwu51!w+~9)L`FloKmhn6_yeGE zDlrri4*?3~dyry49j>$mz9i)lh^C(DU};fBEy<`(7cjusNdZwn#X?XiB=lUWF~R~t zDuHJLCQHH@2!;Y&PbDZ+g51?1YpJWMZj^#`kI9jHCD(&8M3Bnr#97H;z_z#bOsXUj zQP?Pw*CIW{GXd9SrN+B@`g(h~T38xr-?^@KT2)C&Sy|kRI*tiu1IC z#cSP1x75#_RaI8vnSfy)0F%u2ENqDc)C>2QX9A|GE*Lvng{{rZZt+aOSTHEvJGyi8 znl-DIE}TDi{=&uI@7B%8%jsor`E%#Zo40t$(u;Aa zokb2of|qyiUOjzM>EynB+t#dHv~2e5xwGfYows1&lGo9l=^p7puODk(IehrU!2|oY zZ&LDgT&dd(I0;4*quWQN7;hBK{@!y|hSrO4WMP-uey2ch9vf8`*-hJ$?ND8!bu=DKv z{eOOKYpO|$ipkEesIEi2zOx67ztW2AaBFibE4S{!|N5h|PAZd#v(xjd3aT4hI|jPj z>%_U4zUI~z7A`&gKmV<#q^h%1+E86tT|*?xwN-fqSqY)8PIjhNZe9KF-v8Lw-P_Y& zRo+liUR);;=Snh#!2tpfH%n7TuXYqLb-iorlQxRW>&x?jSrHwRoE+!l>g8o+>fj-0 z?}P{b{$p>eMATB6Eh@@Nj)_Q&wzu)|vNCr>3V>$I@I^s{U>(5Nm=r08X99)`0vn2I%%+lUzhT}PUsjQH~puWRG5=PK9bC7@l!*?_J-n)UWy&j zjc70^WeTsBoL*#UtY5FY{XuATmlRh}6#{%P#7&!#1{tLQ zS_I%9rCCrF+LZlPW8>;Y2lQT7g$C#zU%O=cW3Plvk+@VskaqP2z6RH~EuKAN4bKEj zHT?7kkF6>v(#81M)0c+ErsgPLME)ZnIF!0nvA9jUO%M@Mstwd3y`S^#Upr3b*p_@Txra4$-}1xcX5T}~Hr8u)?e?*9!F z=w}7`_xu|sQ28BjJz%=j`wQtQ$Lvj|$>yl=G$`v9OR{(HhlUxod zuta+M`^@yPB(c~iUMfXJ56=XQ0CQK*KvVd~cU@H(K6VB=j~+d?h)MvBu27hrolOzc zF3{}qOu&F+;F*A7tYE6h7Ey6G{*e731H!FC3Y9PoCMPYH{To`O_|6OqJF5I2>z`o{ zAv{mk;s03woNa1u`j_>ONPlZ4gHmGV6tDf|`X>X?)oEt{mlQ^o&Pw1UkgtErFm^Py zH&u9FJn;s!Kyo=nJMflidq*d2a!6LD`>n%!-nO>WGk}azl#nxVXIJrC6BFg>2vS*nc8tS~o7&d? z4sZ4DUe|eW$j^Y?R)V2?vse5)Xc0*S$kuOe@cCXwe_pJ=hZBI zO>gd2R@$}y^wkG`QSr%X8Q5Gaqx_Q6e5{_IJ$}Z?=9SLYO}n;lK6CD&cUW{>5^bP0 z(XQD%6R_DcJw4zW>l+vt7@53y^xW1>;1?Vc2D~bnv>{IvWozZ-Y-3|b7y#&vr0@ZO z2!ZJs4hjylHRAltxXAGE$nemRAhf@xe)N zkrdZ;cDEIUnitd?>jOQJP7jFL(C32>sJhD3p{fq2UgvYl8=v>I<7&7;2*$AB2G0bX zAME&g=eFB=cQnteSik=J8K<7<-hSxl!x?CRl7J^YC9HP;r3QX^4V=ah0l3k;_9OQ%T$7mISzfe)4t69EIk z>JP(lOkpQ%iVoJ*RF)T`^FK2!B`F~e_5lS(un;T)$>ur?1hoi@McKgm2Yvvd9Hyi& zIgac|%yY0A#1%yaiWyX)n30akLwWR?okS`8M+0YLI^MDpIZgor7J|c9eC91JQj#~+ z<9Ae{NCR+cRK`dOh?r*M^bG}8fd?UeO-D5<{K0XnO(s!CoEmbvCtXUv=8k$4Zj|;# zYIZU?dpBH>0e)<6tEEcHo|eQt@f}Q#tJsA&Llai1%c%#g-91*$lr~&HVeXc1`?x*u>>(%f<`` z%Kz{YV@H0kYiech?k#I?iP$>9?$yds|2cg2;%Q$G{{}$*!$yBMZ|U-VM%GU5@Go90 zAGv+kwEy@Ig?rzB4JO~O(StBfWBY{3!wyOv}`iT-ED=I05xbMIJ@k@WVthrg**ie$2ksTiw zADt%3%g-+W&OPcUKK;*!+QN$3hT3}YJvCJp3FE{4o#WCnvV?%bZ)@xP?T6|zab78S z$XeT)B&{vg32`amp`ZaxfsVARHKeGyC^If9A-$}*y|bmJNmiGUl^9^+92FCjklbB= z?tojUvyFuXmQ%Q>si&i*xUs%6UEpr!MNpA(Z?uvlyuAaX6O&Vc>X1<;9c=BWZY(Sn z2V41shK9bh2nh<0%@l)2jnX!fvhwziKeRS>RhOlNT6qMAJmZ;wc_v^Al{ZsFoq~6i zon~b~)d0CEVc|U1!$;dKq^xC?U`Kc+V4ewB+aj;HTvAm_M4-r8UYP|ThcobPw}_^G>1tiX)9Z{*6t*CmIyp`|V%CRSHP^%Ab6 zU(zuPsbVH1oyPWNq0sf-nX}v#v>ml2yuPw9N@+LVKE|I4lo@KKe^hgQ zb$aBU^|Y|)3Z{dvOpM?-9O~-KJKJB=-?>~B`-%R-))^LBRcTgME)<6bQ-D{@I zC>7@+(9W$+rbU0FCCugdD**J)e)Gi_wuGtF0BL>#{NdI&cNtxY0YD+12^b_c;$pY~ zm^N)y8Q!lST{(M3?Uc&Ft(!J4Td-)aC3f44ED+V8f~-aCczM@>6Q`7vPG7jBa&-IJ zC3B|FzU3DYn~702d?F2!Qu?e!yAl2K~ z4{u$+Ve77=r`4}rfZ7q1*-sz8>#c*kUr3wd^tBI8Zri$Z-@(I2Po7r0p>bx{iT&GF zPMb1*pQ*LI%e~nL{LM8VKIfT$ajYbQ8#u#=hSg>3KD@tmoWca09bq=uz`2nDYC@sD zw$4NI;fV=j$K&+Kd}O%}DPV;`q0H#K%7W1fL&wX{au^00wbi0Kd)JK^NvB$_ABEFk zXnV8*c_!fed}0nnr=gfJMsd7R#YF%i1Iiexlfah1GXZCVNfBf4_g{Yc5mdZw_2oGU zD5>>ycXf*|DJsa#W=x-rzy9{iPaoe6bhp)%q({Vr_QlS=0TuUw`@W z{Xk!5lSC{`iVX2XyPca$Tt0vvA+D}_|Hp5?{P1D0r@gU4oE5_}0lT{(&hB7h`qtd4 zvbMgyN!H#s*w@w806Kz%umFLlhr5TH>ste3Q!|UIYQQ!j4b$7x*-|el&Q1se53;AH zyNB(o*KbTr&2gwgX9N^F+M8=71(|Vl0Qd6pcF})@T0k?4N}dUr1Kj}6H%1x0@l3$$ z)~?^CUyee~f_%3Ac_v^x^Vd3eFRLr<+_Yu|n0!~RUcGMX9^JQZEiqv#Bq&iA{IDxXomcI$3I0ajjqz9=VK6#M$< z>iIKg&sn&7$I(-#&tANI{nnlPkQe0T<%{4dXJ;o0%yc!iUcE7R`uM^92M;whA3w?C z6v)+PZ3C%^@zH^vj#hvoGc+_XFw8@k8ZSZD3wb7BqK)O5fM?H~Ielev3q^6OtH9(t z*yCsLYT!v{htH9&Q%3M&jT3=b5C(Ou5&&bTeh6*A?2#Mwsotm^T(IW?%Pr`36qLEvGVt!* zyTM+1U@Yp3LM7yha2Y2b{2(nYFO+l+4uTbyJ}Q#8Hp<2Q?uCXAhU zT--~5Z8Qbw#rvf^6R^(71xvo41)}iDlV+?qboKE|QyV9DPj5eZzP&wNJ#D_{wk}yb zf8Me!CvQA_^4ySG_&t66gCNJ=ix0J{DL2^N*(V|*Sm5sFNe#Rxz>SGz^8#eks6hka zAWHFgCg8RfK%=9noce+5zLKX#s5}n?Nf_=2T0Tgn4l(t@Z-M*6axwUksgk+-LQXO& z#^b&-6>8_>ngrR4uo6GqhcZ3vPCZ66bDV`1I!46UR;{9aq+{MOX+9t{B&Mbo6x$ z^om0r4fV9Hs2n?b;^Z-&37BUBM)(VCK|}QwToD9T<4`pOY96RKLcTuPZ@HiURQOU( zvZ3`azX^$luIGfb#@YI3ukugwe_j9dDpXHFw`J!htOG(!kal!&OyJaqi-Q#1n7eCh zeMx3afSYSr8OPd=%i)U2#T{MU9km51p-%euu4x7|BdyFiS{xIotZQ(vue~-uHNr*z zw)&N8b`3K2@;JsZ!_@il$M=1*%B;jtmzOtGRZnZUQDF=2-@wV=efsqCKud9QWPrV{ z#u?=^YPVBl+ z1l%dD5GI8>8ff3YbLr%PL&ufXA3W7Z3A2-%Cjh7M1Uu@}qXL`_weH-#LR8-uZa*Ou zATWhM-UZg6bf)CCW;I9n4MMJiU4O%DtCw zfD;0u5hqu751OYm@uh8z__&IN(LsK`J_4i=F$BJT{^YNd3Qh(Mb;Jaomyr}36CE8D z6%`g59v(r3R~V={wEU!dRL*#3r62#Oh&yoZQ;};w$~ba51gEFMj*Q zkK8-|QvB!oXKLK_LsyWH8+ca#TTVjiCcr9Z`#)*m<^Np&sPS)YXlQQhL?njOz}x%0 z{&^-~*RwlUE}lIeGCh#9zc5He?%fS>#j&=QcKtmoy zC1b{2|K|qH+lmX4*7=ZOfQ-U^QglQT91<~gR#ebcL!%Avt_E8w0G03- z5o7r$Igqj{i@+t7l3Pc6FG4v4g@npex*T5gKyP11V_juVtfzZYH87i)yo|})JG%#d z{Q1WZy&Y}UX%P;u3|xzwS)4~wjx!NpaJoN!`u*3RJ`QxZ!p}C<(R*fDfvya=;AO-? z)!8-h*T4S$>8E!C9kqpFw)#4cA3h9fss$rTc?tHG&hFlyA^+t=e}8*ThO4pOle-#M z-jq`mskjJopcDQ2*Wds4)4RUD*5X7j^QW5k)Gugea!^t2?Oi?ngTMaqw}1RF*x%Eb z8|`7C_u$U?GuI>Yc_v^`jbYCK(Ei}SKzF$?#Sbv#+L}BQFwX=`zBSngo(Y(_VX=`V z^?9!M&Y#@6eD0J<_29xe&rVN@V;(-p!o$N!jzNhXDgJW*r@$hD>I9L6lS~CtF(e<_|GPSC3k%be3L09$*hp);il}yn} zHp1QJ&X%R~r;i&wdemrzanqMQHZ?QH85r3pz@-H}(^gkMv2e!B$>8}OIa*=D%(V|u zzi0N=9Ej^pjV;2*SI-^VICt6<#cxNB7%^I5^2}}bw4NIpo4tjn1_09ri|gkPZ(1^K zlESx87(IU4{3EySL%{@cxPy(YxsQ}~ubnr23TTqYC@6k6Z}r)0cON|kS07ei6Uanc zBh_|nTsn8!(g+Mx0^!riet@h*c@w`^Xubm{W%SFGQ;lV<`J7vu^v z(^Hel_;9g)xU0;kC%2O*{>11g&P@LXmWdk(I1?8+_15xCz;NDqCg8}3sAy2tH@)xu z`1_~#ecdg!<%PMaG5&7Oj`p^1Eqwz6gF`~<8|!*Hc_v`^QFNxnwu4Un3; z5JAMzWT6nE!#YITrlJHsLPlzG5`q21#MIU{Q2QS>0@9%uHz-3~7f2rIDJbTRgPc>K zngAdLq3#3V{zc;aTp^%7l9Q8&NEH;CWJc@gOp7xq$>C)n0GOJRTvY|GQ)nRFj320K z2>@nAC~*Lwh%k$oB&lZ*qXu#k;|)*@kyOY^42emD;DtH+Krg`!b}a+Elq;ZestjRD zjt5jMW^!g>=qGjuW?-PfZh>DyDz`%Nmq29;@sKhF5`Sg_rI(;LNwrz*P15}#=d2vH zP7d9Hy#_r8+@BLO9j1VXhH0|LNE=I1%dsbxylr1;-j!+_e!%r=tsg4JlQ4Zpj6 zTT_G20;dzm@l0WS>zZ@44lkQKZPHkUQKQDrx>Zm+Bxje`HK$!azH907$%l1tDoU!MHp=(csMR;}BxZSQfFbC+-2)zsE~u5W}1 zkEH=~URIu$7%FhGdh<$8`>~d`?$a0gh9+j_)!4FW`AEv~apsG%k|O-Q+;}EnN;$%G zU|Xbu9y-Mj<@NA01(gUkfoB4SKiAdM`{N(K{7AKjQdu)%NO{7<&_ID#Ktg^wN=eIl zdOrO9k6(Z2@9D%NY^f;+KST<;h`l^r-TV@YOT<0>zyIS8BoBJKk%?}uE-x$;r9=e# zdb&9|JMm1w9s-b-efar@fsUq{%95h|oV3KKFcbm0xHvmGfXl1=&eHUcSyQ&gKR$^|bG3Ts(W`^l23pRfAw~Cv>(|<)ppi-4^@6Ih3QCbq8wkLj(A~{50fSSfwp=XAN=uB52n|LGz%L*G@sH4Onw3b6 zV+nCd=`vEn;XWQ%b37ApYYUa*$v~|!w6d8sfHmTfS9m7i&h7$3t@~GaCg4*?kM7^L zWy{)CE0-=_IDh{91&fyM(|GVqChN*D*1oHD`sBd_d$#V{yzcwu%a<%#vSi8fReRL$ z>b#`&7od4d?fCvZ+jnl=ylLZ_)oWI-T)txUrX!c`Xg|}({?OhQp`&(e|L$G8c5dCa zWAnz1n>K9Ne&pQs`%hjNn6d;@M|HgQBlS}!jvYC2_~3zqC(hl_)Ol`TY;JAuOc^Gc zvpf?px%q5srzjcDr4(yJz!Cd9Ryf<`nF2*RDU`s;c_!dCGz$^XF-=q&tPqNtEu=k( zFMI?7SzV4b@PR_3Q$FZf%l{n~zWPz$aOG zRslYd`bMCbVt^aBr*GiBG&j}P-c``WGXZz?(dfVyUQwJC=>=LaQF$c0NS4k zm^irBY(pbQ09-_Epk z+e_`kh#^s;4yE{*E8J)UR}piIDNu)7ARY)g&jd`fo(2m=;1MBP!i-dFvJgBQUk=is zCQF_PSnU1$_UUUp6EG{##}vVVv%V}lG1%SB!`;n=5W1;CDk2K?58UyVrVZ9MlAxKX z1hm6OVj{y&<<7`BTO-3nG@q!zM|uq~`xzMwK9rq|XfL*Y4CsS|8Nti zBZ3@+KOKla*C$)OsQahq{P`VJ4()n)>Cel1sDZH-c2F*7`|D=~`TB25%Hq~PaRjiz z)<29exBj!wnh2To1%88kkOI0fkniJ}fG1C1d0kH?iE_|5c=*J=?VC32RXKV=g2)QYuBw@ID5wIMTc+dv`Slj zb+2AfyP$FC$nhO}cO2NWW%c$|D`rlgF?Z3H^P11Gdo@QqIez)rq0Kvw?_R%g)7B;P z=FgoyWy+l8n@``@dqrKOts=vVd$+9FzhT*mb>Aa~Y@FOA+p3iS4t*4kvddna~nT)Ak$;$>@h9fOOn zXJ7^%1A-~0KUgI^6EHcC7@SFf(V}P|76e>R_J#SNoB|z+Acl1!$!Fh`Yzeyu{Xji@ z|AqE%mmG<2qdz*xjZYFw~TDl&|shPs;oLrG8 z2W~0P1l&z0g#Lbf0g-fT6G~|)h06vuJ^*!e4}nGW04@ZZ969M!ZUZJ)DVSi<1p>4Q zjBa+)BaS@s<7ubG(GDjFVxjG3VhY3)mJrElGp6-MYYL--kU~g~S73WRfqFB^7hmXd z5Q`z+NH_RbIjuXaB&;3ET0>knbSYb3kg^S*u#mCIVIQuc^-bf?5I>#rF<3G{-o(p5 z*0IjzYFrY4)Z}vRRq2I?G~_qWYpqaJ_;%!|Z^w*N_w^4Uh>W;|BpD(UI09F@ z+g($dr-%yOk)y_p-EHpc;7tl@6k z54bbvg|JfqI6r5ZPXc&Z| zeB+=0@f+AStOq5=eOW&p!NbKj==Vn8x)q{vRT? zk`2-VNgePVgqKGNCwvo6WUG&jrF&N9y5 z*+TpB-W|IyYhHhCZDs2d6cN=c>a2)#H;qW}ak&20*FjhH@V2eHv~FBie_-k484w1! zFf+`eW?x?DsI(BHk(oHRuTb3|V!olP#5>}+T2LzelyZK1(&h2}*v@TuM z*0^x-v5lKwC^7wt`nq#`1N?a=U_#~K8mx?N?5@~k+rK;$Q4L!Yy=3mswb=b=Oc2`48BWOVRB6Ln#O7Z3Gh9ZTtBa)?NZO`Li8 zcqZWO`_7-+b?2$}U7s)z4JLr8Gdm)uE5*(2^}{PC41G+se^OQ5wogg(hLdklcyuhD zTuVk|erb&R>x+B#+FCz-u9UIgy-mvrZ4GfFKGw;q0t>&44Egsyyb?f%s`wt!f zz5Bk-T_X!SCsz+In$ErLrDe%hZ(Qx(ynbtDj%Eo01P5nVo(Y&|0v3t;4S# zf1bZ`_BT_%oi#&t>eoCI@U2IH`s(egJ~{oXKP}#)@bx!9NK#xpM`rRQnQvt#tiA%d zsSP%dLYs?I&*{$j(}X2+mTcd%^M~&@ZuoB6kF@7t}RRY&mqx;3blT93b!ME_3v`zj)RC$GWI2(0in(r~mlL3nLRVE36Q-dG+v2 zz}Te8JxI}1oZVRf5D7$7P{{cZCByGWhkELpssy#Ixa*m6YMfMT)Cpo1MNl^ePyo0P(iAdNWD6eep7WWbu0w<^aA9<5l zzq@loZC>3_R@1oal-1UQ1Rzu?(cSCnC^<{q5NhvgdQ1J>xxJg0ty6KTXo2ZKHO(m9 zByn4sr_r0|x*8XbpFF&F@!X|HO*6`PCg4?yGAkaN*t$g~rj<6Kl9_6m35%+}I@Rp< z`J)Fnemhd{p-44BrjkZbg(o)Vlt}#GlSv)<_8*s^^pDs{TZ+Xig+gA zI?D5?Z~y)Gzx?v^`w>wa$YRqXgZ#XG5~>8m^vg2=w|8{?<+ooyjt%#9cQ%z|CPsp4 z*VDtz&CSu(%geK=4dBi1{_^R=yP^IbVSQ;+Bt0D}nVKbaXRaWPSm;b8=!Kwh8`@zn2H=Ar%#CR znx#y!@4niy zUp=~U`TVKFhtw_eTPcSN@(NBr8=3^c)@HAt-qBP&eR%JV?Mj}FjSQxvobiMI*eC*hf6Xz~n)7H6l4=^LoUXqTmhLWJt+*ofLQ&SrUYZIgA zXfI#AHfA)w)B${<*lzQ(c_v_3ps-Ac@IX*eiN%9c@#c=|;0LN_Ppcb-2vBB4brdM1 z_$+TNHQqm8X3FGAlP1pG_%IWAX*?5fWPEH4oFMg;5A>XTpY2?tF!`GaUw`%0*J$5N zm^fvHQ*dB#WmQdmk=7~o8%K66l$%E4KeH|qc_v_`3zv0nSCo@(@=U;tXODh>(3FT3 zS++9eZjK^zS}NrJfK2DXLFxeC0FL*NLp*{EYsR_Ch5%_QcRfypOwI|v=m9c5M&2mu zrSZqc{<*{8;4pE!;m`>Q$ZpYLj2brt?l>}vqE4!(#V6p(W$nYyAAkP9x**wm1iY*e z<9ey|<0naDb4}~e$B*bj>~aKOAQp`g)$Z8n*oS7%8z&C!{z>Uk!TV2Rkkj~_9NpiI zr0SkJyKnu*HOrT*dyqN&p2_hAx^X$g?Jk#<&+Xc?e&veg3m45>qE`P7qa#yW*ulQL z*je}5sY3_1ZCSZt#e(_s=gr+2-cM&eoZ9K`?{Y2gYo0y)(~%7;H*Z|DkY@sR^bC$p zNX^VGC@kdO`0xNR0Y>?F1jZ*N#ze;?rDf&j7Zw+nmP+MAcwMMq>*Se$DQ6q8ICx^( z;j9skGoexjEgGv=kZL!}#%JpOnXZ&bdeK-%=nveckdI3lW2%&Alhc26;I1D)6*V>1 zm%9t4@<4ysXPoeh9+a0(D+td7ObaH@1l&K=8?3Q+&FU4)f7q>x(m-W=wO1m|*lH0CX zDjl~Bkam*a5ZgKQ8f6}IHMK8Rh+g=9X}gCU`brB68pe<&)sy-%Io=LYDbuRKdQjOE zT|m8QeBex>(K0gLgDny0AY#`;1yEl#G)Q&>8v@Y=j4(bv3n8f836y#UC1%*e&CMM(AkG!+hxVG!a`Xx)3Dkv(0WomHa()F9JKsSY) zXwO&?lVo&kP}EWo8Q^C2@T#ind2K&pk0d!`0__`q|KXjeCL_?@?$sR)6{QPT?&S>N zSyL$!0rN=Sz5h5SEKBlrwtRBq!nw1`npf?GTrj7N*4)8&W552^UKr_aYi@8&RY~cr z(xs<76R>|sM09*2J%34Gb#}bJ-7{@973EV$emZpQl-fgkHy>mu$0ShXv9Dj473yvO z{HEr`^QVs-ICMf;>!q!;H{{VMGNd;wsR?qndU5OW1vO=O=uWHaS@2B2JQFaPK~nwa znSk4K!`&Us^{<{gedhG(Gn!9B!U^S)Y6K+x??(H2S~CNkEMDEataRe!=`-gv9|J`z zG%P%V$p`wn+bR>hZHx?VYMefH;?!x?%THX~0i+We#^gZ9YAlNNG<$ykhWe@FCr+F` zf9092qpPQ1U@$3XzgXB>n;Yl#+Tf11`k52QPn^DR{iQiNc=!eo%2vOmv#}yO%FRgc z&P~m;$Bvyiqo(`(ttB!2`XO}yUyx@4hDiW}9w9(3a>g?O!_Gm*ij)al(wP_O@#y@) zt*e(USg^yUv!4thR;uYuQ?7{7;7SEltblt<&K9bWG&FYJ~%kpA;Os^l> zvvt)xMHz)Tt6%nDfIs97WtAmi&ovHgU%&KQIhmQ?F5VU?VaF`gNI?O51m!gqUiZ%L z*}P^Up#EphTD)AR39k!T9L+TU+e=G2M*?r1+OdA^f>|;k!cv&GKdyHb>ip)>mGc+Wue@+_^YRY~ zC;8CO*t_@dhsAB>dC37bPw#47xTtZ{%#Q4X5a5&pUoOYX?lpWv7V0RB`uw2 zmiCbIOu#wF(3Ap$z^8}AT_pC^6G8zp4;a-Kho%9i0*faBr4Rvi;v=O-XoXDe$t}ph ze^`sK;g41Gz?CM23(Ah!UwBIgaDv{{A*Ffo2LiMaW~MBu4g69hdj@7n*AwKd^oO zD!bY+EIyFF;VMoV>3$#MaZ&^Tm^Amv`;n zv23MUw(zqY>!MhaVN;Oc>1<~7N_)%qOXe{&7Y z+c^tXShmyfH8oWBB$AYShS#QhnwaWeRo=F24t!v9FH|Cj0XYsVy|ypSE33c6&-|^< znIFHOJ4fZBlQ79%IjnI@C8ZIj8;7F64V8 zh#`zX0}!R)XNW*Qb1(a}Ik!v+x$P}IUR0c-wr_|$1-o$Hq_>)g6;U3H8WAZtoDbBE`0*sysJ2IyBJF&)3(-n;K=@BT)lg zT3e7}TUl9Nl%1Lw2a>^OK(qswVmy&bA^P8KUk)E2uRxgsaA#<*Q)y;4enmiLQ zmqdsMLsbu4!&@fn1J=Q3CeU#?VU7Wrkeq_3a3UN?4$Ph87yyMZfP?Al?F94>xT#wk z@h&OFxExYK;KwFE@_u-*yRBA`Us%`CidRY0aTVq5m;k~a0GE9F?dK6NdDRwXM1-a{ zG=Y2$Rv4HBJK8$BMdFd)|N1LR3_6+w*_ol<-ci*ACylvMf{D}FBOdzeUw`}fZm_4N zNsyBg?&aYgP+SQil18y6clGxD_AkHv@^KVwSoP)EDPdm7uXjx@0@Gu57A8oKXz1^M z`}O1d!Oo6`s)CHfU~e~PCwt$_^z^j!bcCDw`hWiW@1Nd{iCS9(rP--bejaX4_O_1E zJQJ|Le*n#%P6ox_gPGA(Us;@+kqiZoiiiN&XIMDnnWZu*o(Y(YZX7>&Cg8$WqJm&K z%)~T|%>gTXYg1WHLYS8myyUl?tD672oR*aOqTFadS2F{H>*|+Y7B^sZq1032Nk$b- zS9?cIQF^4Om(|nz+G=WQ4^zvEQ0tMK!n9WQPa2*uK=eb6E{ky=NaL0HiY^fxe%hQPS60T@vHxVQLIgBK7m<&ncg| z>Kzyy8Xnow+R_?Z*x%Dwlp5k;ZTwR2mgad?RTY(!JQJ{&w~wzMYH#4jrSouO16J(v z(t_-i_!yoEm{M*g>_!RC9zygi3hm zdaitxvvJ7vz>FM}BcTik9k74W{*1fhvso%_fWohH! z;@;FQ(3!pUq1l~nD->o=pZpDk-%OY^ZN|jahDK(VHug=;?TM;Co>e-wWwGK6`5DtD zPyA-$q^ZbKI)3kok-3!}-gj$j#7z~|^|R(E%$Pc5`lQKIXUNZ8bwEw$;WHB}dl*WM zjqSA$_pSYIsiNGpnKNg|&Y8bx!%t^5ZtC$&zzOkqel(w{(hidvYC~M%75EQK(R4xp zS~)R^;~`QB9t!vfKE9GnPneIW1)NBpJLcab8-^N{3ggU37{J8&!5!Qw+fUlK9t7b} z)-ulo+&?`0@wdNx908d~H@tq8Wd)fDF-axOSnVhx%QFEB|Ks0&`!Lei)7;qFP+5`| z7Z>d2;pSj#YiH-^?Cv`@I`sE{{q@73xT~e1wxXmoCoaUx11!Gwb~t|{PiJiO{m0*b zdEYPUY_6{=ttiNh4G;2m_i%M`baHfXFa7= z(bQ1aymaYRW`2KPYoEBjxF|OPCsGd&Ta#Ch474>bt6jW!5$6P#)P9}`xL?v%6zA(^ zZ)IU&{NlO({kwN{Z{4}C_tePL+TMv}llS-5=f(!W?O|yVv5KtPaFF#v_#XzAdYfCq+hUtPPba^}?8 zGiQ$-*uQnd4=a~1Te@=J1K&Jy0rE`1MLrI%9$Z!DnSdz@LEt}4@FY}}78ey36%-V} z>SjSUq%z@*i{mN<-;wCYxtGezpk&ggaBrb%dDr_Vho@r|N7F~3<>8O6BBb zSXJvFCdbOijX;udW2|p9=8d~Q&jkGb!!IL)!$YG@bsg1pRc#G|;)c9}h|mBZFIx*& zzdmpp54`Uk5eWr#?R8~HY)nYZ$Vd+K^z*Z`aPbM~>mNY{_Rqt}AL_0tEUC=TNQ_NO zaCQjvv$J;f_76aYFy$bP4&$x07FJi5WF|*?xcY{B+Btf7BMBLAgC#NXOu$@rPJ0_* z+S*tMo?K*AG-H{1!@y?=JQQvm*S>N6{;~tD4}YjHF@Zyc*$~nZ{@>Amo(b5(LHyxE zcZSK`r#{K~rGnbVR>U%~3U>*4CSWTEWGM#oOu$TeSy?SJl9}1354tpTa1Wc9~ zoGcx}CZ%O}E&h4`hmAv}2k6mNvir;vWAI2)+4N@vgH={Do-?uA%4cui_H|TA2vCF_ znaIW>N{YpY<%i^DJl^& z>KwdQ?7o;a*qfPq4$ir1#IGtUKHG z^NLDJ3iAt!N>Ky|_k?6XY;9$!yK|xZbe;)#;hf#iY&>!HkBCW3%c9MUn|`clQ`lA- z=}*;701#qGVeIhAy0D#=c1VOGE6ahQQc_x4R!+M={*O(Pc3I5%PRcn!ju&F-)Swzv z<-tvRrX1vdB1?e$Yjt%n0LTUyR}7|IxCt%&tWJh$1Abr>?vJDv+yIdCOu&CE9n3c2 znSf>GSv*56ZsF*ln0+)y+wWj7;r1X#gxDY&`?hdbv3&R}^RSxYts(gCSdhGyf{ijj!2?9G_R;1Id1OkV;0l-5kFZzS#O7dJG#wrlQ44}HE; zar?3dBmFoHvgNOpzO<;mMG%~ApyCjv^ZlYpvWvEyx!E8@opS?HEyN;8huOj1wy*Tp zOjfki|6Wm6X5OLir`uK5pnw4+Ej$x&pTpA0Ur(R^-Hd%6YbXBs&tH8t;p-`j3{K6M zF=dT~wLO?&v+sWY)jor5(|22~nFRU7Dc{VHTe)ufq}eJa7S_Gu&d?)szBau*>rb|FpD}fZ z7S9Bno}Lb^8{hxA?H`ph@PM-LM0QqI=AW=xqcJl88#hy0a!pel0xpb(dfLsIjX`z< zOa#s>KpcW405SQv7@AEuLS#04DW}WvC7{L_fM)`hbXN)68*>7@E&bq!ic2=t&xrN+ z4@*eP$jZ*j%&ir@>yb1ID{2H0c7ah*QLk(w!()>21Ss*KunoxJ`hNb@BOGY1&5W}1 ziHLm8GXe8Vz!WiN9(4v6KqaTpL8?Tpt)@??{Epf46q2H9JXDrL!6}Sbjq9i}vz@e? z+xDRUQvIfZxfSAn*MG>_`aw!g>QC;=FZG`;ClPWOafE5@A}En@IhL0?Tn@3gv!lK; zBOu&AC`HiT1vz(ncu?u!ArcE4+Z%FYL&BY{Z|mE|)X+JG3jM$ahRa361MNH$FoK4Z zRM}8dRw>L1PRsXqzIpb7wX=s`LVAv%xt*H=^m>67(Owbf7a4f_z+s*VczpWP9H+(( z!zc&s?rLw&iTi0Q&3}BxR74v3!W5^c+0mSLd(Ugg*R^W^ti zxp)Uh_PWm7eE;n3ANL(P3IwY2nm4Xq*nj5m?)7uOl|N)*@9c4R(UB1AhYw!Zy1Ki$ zINF*$yP>Oo^~CYrJJ)=taMsGke))dmH>P%hX@#Yw#rZiIDe(ao&tGULYwlXVV9vK% z7FLHfpV+uY7S~cSdNat+1f{vbrdLjBT-wGn0jn&ZE;D|-{A{GM3@-Tan@esV+%kCz zO@DU&ly;z8AEskXjVZfVO`eV8Cn*SywCF(V1BsCg)Q$t7&w*_(*#VUuQdxiy>jcZP zftn#mP*hNgO_5HnXwnEG9XpXghWb$EtfSkG_h9BVEdsOx@Jzt0P`R*1%5>V$(AeD3 z1*{A~xuCA80W}bnU}!5LIgA0c7BR@TdOI3{)0B`^)yOdR%8CoBYd9`n;m1!OMu)^5 z4TAi%sG!hX7Geh}Y7HYAtndB{a4z2s^>s9q7oUz1*@{Gu#mf&X9E88esoaW z*$fb(sIVY^Z%=nOHy2NTUmuW+^Gv|l!(oT^cGL^Iu)2j)2 z_hbYJJ6dW>3vyAV;O_|_CPxPcdwZIHjp)Fv_71EnRTV|qDRB`XoA&f@cXh316R@#` zuqv?VAP7)il%JKF5EUB4GXWz@hUq>%W1b1v?D4Jhs>k+j-Mn$b#*G^{ZBvU+PD})K zb3=V*R!Lcs?Td$6=guDfaqE^18#bWJ_EY}RQBkzK)+Hk#WJgS)n_TeogKgawUGj1Ws@z;~lDq=@(LB0&k)0<>pp8q2hGHl{tQ9`fcEufOjmBn>=B{ zgs;B-=4*iMOrCx=J3S@27PeMOIrp=UFe)zPC#&un2FR&QPDl)WxSn}O$K&Z}`F@1)N{Oo0W zkMm5x6tWuyD=t|0h|rBSBL3rNIFIl)? z;lg=qETd9#i%QBX7!&V@%7<5#Hf>tFa@pd=ix(}Jw{V+*cX(1(eqnJ5laIbDf2+A+ z^N!U^mo8bfc;y}~W0!!aBqTr-6mat4VNt}jqgytwU$^y;rlBRz1kC&tVDjx408Iil z;?krdJp!vmrXm#-F{a;veklnr@pY0MI!&=Sq%!hMz;McdQ=DsKA9`!*DjFmxBw^gB zcv|rIbV--K(f18bn)`Nb+;Q|y_Q?AoVl)PqH(WN5qkiPwaGchG!`qgxSUC6Fr8krN zM~GXMg+sA&LfmG3?&z_NTb3`JKTlCne)id_QTC1yhr!K9#zeMP)er94xpwvZ74v6- zhEr~1kcfQ5@bc1^*7XhlZ%Y zi`v-x_wU~gON|$LoN&H&k^Zsv?xTqGzyIAkDcvy1dxTQ)=!XUewI8?ru;rj`*6=8; zqK)%@(C!|OIInK$=41%;Wj zP78(!unlY`O?2;3k&V{H13R{@S}}L|ccAH&m09QC!-`1i>PY@B$NYx!;ayvIEd6f9 zGVt`G$5If5)exd1#vdIBFxFGq&oco>c)JJ2#zq8qd-);*Hv$Q`iAl_I#omX#5rAvG zU9Ht6d0Ckm85x;bXqj2qI){nu8cAw+-INs8TwjeOT1t?mL|WQcnaNA*J(X@^-)v!(9N9_%iC6<8FQ=YeEFjNDK?rVK%%&x$-?*Nfg$#_I zX9A`Oi8TKY8i|rkaE7ymka7xpyE?}MD1hj1D$R%v@bHRjge4>bRWD1Omd01G9d}h_ zM0%JT-qtqYnSjq;Hp_!`U075^E13wByR|eF)wK_$x|xVO~RwlBR!qu zlJ=^y>zt+)EIeX&J!6U~{UAScj znlPRT7(`E$f`^p{sdWeof}Vo#vuWkQ`c#;oUqJagXq1{qSwF;0Rg26X_{Px*L=l{n za(f8ehjK*f2{V{5veBo!lne$6L$dq_X_%A!8TX}YNruKg`r?li3}ub`jDrcLXeTTb zH0cO`Tu*8$4TVCRxei}m56wm+D`wox!4Q;Ofx-UJf8;2LBmN=;-1ON^t!sB*nVMNx+c~J=iTrm7$zJUA#Yz|YSgEg&d31e(io}&0pneqpPiYJo}Q70LZB2{^4kx!lp7|@*z?;n|9tW+>3lUpBHk|G25YbU1?rS?~o zeNbGG_YE_qrCT$(tZ)Vb9|wTMS>Udrp2=wwrV<;@wMfJntOE^1Yu|WdHaVT5+G#Q4 zY6!*+blUc55Lo5NGURd~2_}JU@U;1(NfCu9%?hg>z9~! z;R`vCvKlK(^0P9F+pt=~frcc>`Z_u^p!M|)jSY`Tgl&yQNxt6c&A@D8@>(YE>mMBZ zJ^g3Ubu1(8FyOW2GQ>%8=o#Q3DTQt5&U9yJ`16l?RVs zyd@*ui8<3)*$K}CJdO?y`U%QhA{@d3wldL~9pyXN*J=OfnSkdXOKBrV2@YFi`rwS5 z_}s<(md4qGJ66q+pDCj_|8PljJ%O=vJS&n$XZsL~$ET0)S-W($oXiZlISY-!1&TD} z3g*A)%QE*U^tp57z}D4^AY|8Z~$}m-O`O0 zweRRXdu?Jt1#Io0;*8VWyM6VNxwB`<$tf&Yv-$MZn-2^?!%QjZQ{&Rd=dm?h&8`Zwu+SrKfE2^@bORqYbV9;^E$ z{s4}~551^hPueIXO-4Ct??}!T5y7~SezOiZ6o5qc^8jDU$pZ-|CRq9~0%h@Id`>R? z7W4~l5O^pl1&}cblAMYr5D}sBMI0^&!<=IZqz+6@Vn`U5ApD?oaA02&mK0si#9S8| zfr_ADMv?jo%E#rDqzy+4IR$wpV4exs#|JE*bP%A~!!rR>T_MXzp))_#4obCOAYc-r zlO69sx=YH-v{x!8C8ZAV3c&q913>+`4)~B%kXvS?x1a@Nd<7x#xc<``MMexpU~3e8 z2_|O>&K8gH5!gK-ufX_!=s$yJB#soyLgw@z*GuKtC7}E$v>>Jxsl1|GdV7dEJKGwo zN{h>Dg&mO77r^i&r*#S9_PYG+=m1woYhRuT_`K@bYjB}Exw<#EwACi3)d}(vqkJta zUp~BfRpa9M3u@{Y@4PgFkKL`gxuvyLP%Oxf3vhe;!r;!0%bMyJFI~EL?cS3&mi8|0 zC~X97O`9Ms%E!Uz<)gbg*REcFQj* za`VBHmv1d>Z0#WjPcL3+RkD}8$%`jX4D|2czVkre@CB?3Ya6?9Io^3qNltQfkf(!{ znTZK}{8l!04o)rrdf^5@Y^}Poyf`~4IyBJF$J+}H9#EbMn2MXo=ZCWcowJa+f+_$& zjY$=pr1Uu{3H9TdfTzw}s9WC37E!DpZ24&G%GNo(fA!kgvNNYlK#sc1@th`tfd?2Z zTO5Ug#}{-?tX()qVXo}tNmC}zkl7IqrdLFPvPs_7=J@={n$a`RTDCeqQb_&Q4Cw&Mt259!=!3V>?Pc zg6eo-c^S!ZkzpYr!9hVmfq{WdjFXeXsw(nw zveOIdd$@i6B~J3T}44wN;H+Y+S#MT)zv$2bcAOD9v(ra<{%}G z5=d4X0{uwhK@Ja63=4>d09pD&2r-=@Drygm zEG7L-om~wj$uS{bZcY{^rqAx(xU8Xe;rw}3)r&g%MphEZfUvcuFflU7!`aT%3Mi>J zw6ADrsHrQS&!rao<<*Z~O^s2h4i>kkRJjh1HuKKL-UV?a!Bhwx|; z%Z$M6+PP!LPaXZ~z=1uR)~{dp!+5WiNXPBJQHwWVAt4h z;=25Da6N)Np8=BjM-4xIq9C`oqaD951 zlZ%sY|G)m%Uwb=Svf~pA%j%okIy%MuLt|rOqWZ!Zduuy8ufcc!@%R2Vk+?xnm{ZnN z-Yo2qj1Bg+35xTAtnF=VJcdU9@^3@cP5n3>H8(c55Xo|DQ)zjAYLq87bUUwsQJx8y z+gg!LNxKGxm=J)1zX1O0igKg?(HWLyzKi=PO$c%%`j88n0(D5mAJ4FZt&iDHathL8 z2s#5G2N)gbyoxj-?D0a-g|i?S>rhpRkC0!?L`<=GCSc0_?_|YXC0DJA>FINipZ-9; z0n`qFYE#(JS+6wzO|(=_??)=8r%bP@OZ0NHp)>dP#;HE(O^h2`Jsl6Po7w*}6F8Nc zp<#&r;FH!44t;HPeXKm2-H6?SyL zJ32PE_2l~7gjIn%tf>V{0D-n5X}!0jz~1D}eVz%J9wmWUbT^fyg?Tx?y`_2i`n{)y zPhXhuOu!U|!%V{ZLXU{%F#h!A?h6Z+5)`=w2pupzxiU!Fe7Sr8$QVdJhQKl>5@GvB zJZ@ax^m+eg_n=xPOk{5R<^~v__DuCKz5oC1|1IO=GyVK*ARys`?$Mi`U&qELjsVuE z2bEKC`d@g_91a-T5C=Fh_a#t`fq-5|XZ3FRXTcozp#yviUir`Oh0I; z;CkQ>w8{N1OrW1TkVoQwVFHzofa?)F?D~Q!l$lYasjLTncGFhP-4{yC+L^JGcei|_fBW8>;F!d8bSNk& z#PT^r@^`=dEUGNXjdZborl)5X5QzaYv-9%v#bWUA64Vc1TKgMHv-}-iKY3*48=jCt zb^1AwX0Fy6Z$zPWIx&NNnKc7B81e5ld!we4Wt9n2wX3+RljUheM>_? z{=e%#Veg<}fd5bZryg|mKj}Xll|6X`g+%wDZ$MTvcW<(8$XDO*WNc3Q&&kOYAUR3< zB*MPVdjCsjOhF4Im6I>xvv@coGC$8-_xMj%J$>x{Fe6|rLC&u3AE>f2H&>n~KWF*t zKDxgU2}>}OxX~q&;c}lx$M>&XEH5X!BCS_QaUcdjOp6cx-#=*Q8m+bI`{nX7GCULT z(O1@Pej#CC(u>2}q*nkR8LIs%^fxNX%FUEjHL~*x3I)?%Y%yO+jb%MgB|R1b}30*2X7F|dwy_UPCeK~^`63p{T$ zaW8z6)Zw$79wQzmJy0y!k~Te#32=OQ;f@zvA9NVadOg6d=j1dj4FDf19mWTH3od5$ zv${xjG9eK;FeLqzi5YzZLxHc1j3o>Uu^rdb#gd-pn)bnw-uy79nr?zjbe`P5p{C*$?O^gMuc)ZFth}$MJ}b)0>BXCj zAp2*kN~aH=J^AC-``!+(jk612fES8IRY}2K1)f&X4sUKLU(r$9x<^In(ES&89>=C< z<>lvz`-GVxneDOm_C|NqHEn||ZXQrp+JE@`_4~o`DH+*>^3xa}oSq$M_u}H|3vLcZ zPk!97f6vYfY7hLQ6Oz-j@Z4JxJPR{CEuJ4ba{u{V1C8}twr*OjYWU*b%Ltwcm}dfJ z=Edh9L>dN51IAbpc5l+3&m#PRQ)c=^)Tq-zrbvfWOcK&lZjI>e9ZEEiaB}V%DH4!a z)J3bzP*2vels+cMW%xg4G3Vv0j87Hy54zshetSxO@5H`S+BC8@rUox%yUxxtN?gdGzSs3tA4~O4fPq;^vL#-;o#X zSXvz7V4mn_XLLd7$L&8}xqS7-+vhK=>|Me1+f$z$U>}v>?{wYB?XAYi{o8pa;9TPP zB$UI`%nnf8NtCpU8qUU~zAQzK>i_{W!SNGH z$}<5YPm^Z?cJqdP@pA2yJ^SbW=}$6ufA|JWzTcn*LGJ1vh1nC2+S$9p&B`+YcZ<5~ zGcuEdyxcuJ-5hKz%*;XY=IrL-^ z=ix38^EK61p>8)fGc}okki;h>Bqkks`Wo0Mk) zE=);INysiKEh{T8uMi-A;S+J~gMdtFOPirBmFNo1YeH?jE0* zn3^%zu6D#L%H6@n#?IM0rlfO-X96BU8~f{Dhld*yT^qzfsRwHaDWHA${nwFnCo5)DvOVdK4t>;N?9-?B<@TmzbxL=x@f{2{ z$pGMi2j7phncN|ZmCK>$2B$+Rz7R6M#Uf$d&2!{9LzWJ={b6S$D-}LlY>J}JTC=m( zOb#oBEL5(sFM~gJcQh6imcS9+^+ys9{WH*+Gfq32>jY)wPMzFw~Wk?&ISR9Aar zMZD4h3RF>_@kWs&q<}%wcqU-VVaJlk7CTypu{4nnl_HO*mFEycAZHNj-0BRc8#^{& zE=o}VY0(w+iX|dpgP^>!xm(;z$8JtefC9{a+t-~NYV+!bvYN(Sr>wRfWDud2nC@Oz zhtN;2xFOWu)%2G7xpR9rFI%VLR6&Kt9MA_){CdT0X`V)Jp6hB{IDYc*+QoC19yQG< zD=sY;kp5%2?QP2Sf30`z;swofDo20Zv2)Gx?+@B$WMT6LQ4R9tx&^LU`;VMCr=)aV zMLjm=GIZ*D1R{-7u$e;nC05Ynbb zTOfS4h%94n#J{`yS!q#W0sRd-DWU$*&#FrN z0bL*mTS76IbP3G|$%NI_aAyOYXB?gb{A&O!FDfKJ5E9oB|DUv+nO#E((10=o+y`Ar zOn{`-_)buK02R^Ks3>Dhzl;kNBAyAjPYABr#HjEfe_v4Xh7evfo>yDPFaP@cUw{2L zHY93otjLXv3=Qz}_HcFaNlQyjZftC6@BIC5zyJQr`{9A^mip4nxTuf-UoST|SI@YF zxLDBCws-&SZ@>Ne@%>O=H_GiY;v#}Eyc=3zSZHWNb35d}|H?A~i@KU>1-VJFDDe*s z@iH|wGqqZZ*gY**8eUw`H5D#T9_w5=f9BAS+qZ1qv}4cSeaAJf-n^&(6o`1VwN;n6>Fa7< zICW_Ej_q4_{J8h0W9Khlzy09xGur7WgVUgxT3PM zv7X{!u+};4=9z#&(@R9XlN8=Iz)&j3!q?DPu5X}s<<#D#iZdoo_M&{Q4;j^9 zYJt4IF;>so_5RVFt7WH5ocPVxUxCW^n~BrpUkeKIbAfkO+vsWN>GwpHX9AupH}R{l zzW(YfL>Z>dSa4ST%FR3Qi@;PYyRmW8viU#}o$?L1k|#``!DI%M9M(XyMX&(yhLX1vr}t5w3HTil zg2p~Hd)_#4X!lP_j|$#@8snLOc_v`Ol5D8QOyC+&rdW7@dH0!%o=SNclPl{0epy61 zD40P#;KxAN4_p`HmxGRwrLQ9zLFF|hM{8>XLe=>ATzWfPX5=U#InM;#+XI?LJRv+h z>GCC|ZR{UJVguCz;P2y)c=X-7ccbV@#EzhTWK5qt6ELVu`%Lv7oZGr%^HMMYDT0Z3 zmZIXUb;0q86a&Fy9Uim0bnVBZ8<(wEK5Mq(Y;;kaEq5#+EG{V}J%b+nNUPq}y>b5N>eWjjpEKuMMTOa`ubMgghsVYzC6jny zV5Iojx$oD2iD>TJZ|5x9taR^~cO(!(3yR#igu8c$r0iXhGh(&BJ z00#;i9@L8RI-96IzoLS6&GL$JnktOj9n2J{us0X}l(MklJ*`noUW=s;7J;(w&y46x_FjZbpv2&Df6i%b7>Y81c3`0$33 zpPpv|9$>Gdw+HigMA~>J;HC3sp$16?WW(~y0#j4d(=)S)Nmye3SYLJXwvCGy%mK}? z9Jq!R3l5>ok-Iw z5QLHDSzt9oY-gMa6m|=X;Gj39K(I!G{UN83SrmxV4tE&8 zo((G>lrSrGKIQUo!(MkqCjeh*CA=%sJ?X#N~ z&zw4=bXxhUBRp4hHpX+4NJb=M!-6Q+w}$%HR8E~dbM_R^1nl7Mi{&RK4%3QFt^!A6 z16?h3b>$Ohl-2bt9Nl~ZLQsSlOVf@`+!A-==g*$r)wpo^_8Ujk9|Z)55s)&=4#LC- zYdnHq75Q;ts7;KBjEV+JSbRca63t%Pn+ZLmg_uCg3bKjj6D>70H7y-VhT*AX9+6Xy z36uhBRDzTTDrTMu_)GognSkHkR##R&r>v?I05%syNGfPy9Q&8w{`PO;%A_DK$Co!Q zoK;djqkP4>pa2GLVIk@N@Gl>K`_x$!>+NLocw+sf2*Ld@cWI& zwC?IZefh@J%E6H!gjkwj%=M#(4&**#s2 z^H+jEB%7U+0}5oE@&ER3|MZO*C`|A(GrV>ElA4xxLPAn%afY z?>~#%>xyGscqU+ffByjB1n^A2h;d+FX4^4m4pJ>DU&Bpmzlj__g(!LFp;fb*}@;)VWXpde8;M}iZavCVY>X> zWhZp+p@TW(*baCmV0sEIl+Id9DNW_YdD)riDE3W=!{_Z3>4y@xAWarUyevJwytp72 zj>OayIvFq(Z)-zB0;`+hDEQDL59vOn|D3NM!&9mrju1Q(FwX?+Wbd1qo}QMTj%agV z|IdH_{nNWKQEQ8!G&?oQ&x2J{<(WV97H9ll{Om0rO12*2XXOZfSy}OGV|R znw^WAr;mSgYh77PUT=F@Vtjy;xv9Y&9SxQ9pz-3FfWtyV;N#;mRn)X@+<9p5 z^u-$!M&#Q7V)xp*(zK`mH#<`!LxV^922Y>8eDl`a(z+R$7NkBr6EL~>sIkBuWigYp zPbft80(}ZEApK&EVuuU`oDL^&AX3H;`!(Ky2BCs*swszrawO;%rCNM34B~rOljw7T zA>ewjUeY@u@Pfk)C#Eb&N`oXh2H|c*V8T2T@Tb52```caacr=oO^BIXQCN_Z5f|c% zAeD=QtwU(q$Y1{P@BjMS`w>Z5WlLRKOJymrsKAKo3Z`p&8;6j@(NF*NAOH9bG|0`Z zz>#aLs4Pm4jqrDKb#!#Jw{r@L9330^&wt~YfJaA$1~B^oanaIPTUpM4_6TP$uMg!l zJQFY}<(CTtQUi#31xy7@U+D+h7y3^yE?7O$7?j(E%+jkTUgWKd1+{ z3?b=?X9Di;Y8>pXFE30F^b2zLaJM#oWoU5w>ZOa|+Er0eHI4vxLVs^lQFfAvwYQ6( zx1GfsLp|N=8mh`FD(95X8wY?rVQ{c5KP$!4H^|?|)5g}=;IA$|{#EJo?3@ zL*f>m37D8UDDl4*FnrnRX(=hdnoB{BAEqPCNNkCS(NkQjp%y`Y7~eU_(Ew5>rZXDb zGO^vUz!sc*l@%ZgM#Op^+}(XbqJli!eFB0}DHuly`U6yG1pB?arJ*7(HzzYWE*223lp>v$4opx$ z%%jjyLQtUH9c_&@mE|SHMS#-IhlQUPWXUrD3;bW)IKZqnf_817t*J0GFSpd!E!4)@F2KY3wVRQ# z&b6yr*RS7yZ4HFTzJ}W9qSz2~r%+cDD_i@!xAgQMUQxSxL;LP4OXTR{4!y0#;m)rk z9A20>ynU*B^}&4=Wu05P4~;Br9Z*9<8rW8r93SWR($UfUnZdow8rL7*z5mqM!q(9h zrZ4QTc(c@-GD_LYpbdz)3+lZ z1h+?WmI_@&J_q>mc_v^AK)?w$G*+CL5@Q#Z989VrtiA(4z$n^{{tvUBtE z$p@qa*@A4nrOOFp>7BseIv^$@#KpnJqk(8Gsb&N71$z_8M+Tj8M;c3VQatU=OrrsF z+Xkcw!c=3|4-LO7uqYXmG?c`9TN^!dD6Os`8&d#;JCft_K5v1zud6OI*xuyvW8dRAE|2=L{Ca!jXdwK*@jq%=C-SxSVGK=KP&J6L4u6 z=_fQ;Y;9$!yK|xZ^r_Ql%v@|46PKKpk)Dy2Lqva=Rf7#LoFeqs$;wQdGIiPvxywNz zkvQYxh?_3%CHX+Jx0ANgQdwl^PMJDm<^gAKbYMm7-1U;a-UOu`kWZa5Wy-V}TdZ7s z34|v;J^^w}V5mUn>rIQllb=3iDsHgJ#KAKllsZ6;2Dw<=YP5R6LfPrlr%j)^?~N@W zKu|;<3#MO^V~n1j&ZuJx=gH2PK0}6Q0wz-ec|(6N`H~Nx8u1ihb>WD7|A(AoF<^@e zM)>l2WY{}1x|&)v+W#e3^2LYL+z%{UyhKq>ZkBd6AlG3<(jLyP7l{W$6n9#j zUb|?H0?!1@GXb~JL4iYo89!doZ;BFe;245r9sG}$8R>DOy^RYo5>*OIH?@6gP#*03O7+bCL;Ln#ONer|*VT)PjmN}kNq5n=%?xz#FAR1u z*F1mX@ZJlTEq$DA9$1A(#$rC!CHa`#wb?Y<+?vqIyN0 z6_M_y5eYsH*Wda&=&Byxwsn`*jqBeeagmrK{Q+7cM@waq|m}Bza$Vj&Fd!<#R*B zXU|`~F*Y_fF@LG|!qF=rID+Ju^c|%o@s4(W?hX!4gaLr!e{cUFpaDhGzmFO0oh^d0 zyyUo;n7Ejz$ndc6NI)FId`e6vtz)M}gceZTpNGudv=s6#BqM(yEj@!A2vUq7${s-6 zAGLi%;hB?@i7{5S(F2Ou++p@ED!)7h>D4cjZ0uK zI_Ll<(8jtdD8DrMKN$cHv_fzcflU9&aY#&{g?T&^@I6B*CmA_2VUtL@>jfq0d(;ZeR9b1ZMy;Z{VSUgQrha-y#UkHc)Yh z()oVTB-uq<&fIJe@=U;oOze3kV5Yuhjz0Gnue$$O7nKEikM#8PA3u3vWMXDz=j2Me za}Uo1jAH`qB&>zKB6!AG7y{KFFh=S)pfJhs`_ZAE`lc#DEm)P{`{alOQES8&xZzKq zet9nuHn&z*#3yB!HN!hXmJE?(ko@Pr|7B!!q)*sZQ(vDJ<`et9Qu>uHTMHF5W?X=rY1@8rmEnRgke+mgZG#(Lj%mAl`! zcY__+jd#Z!+3fT;2tWAX=r1EZ@7hBh?Cn8w3nvE7jpPX@hTMMUDgX3azgV2(>1>Zz zj)NgHgUQ2*Vh4Jf+k02M}1{RK)8QUil80-F)5KKG#n=lh=q;q z4Y{!);m+2#_3dJ6Nc*X#5&DnIMZ*K_WtElLJQFa*hHJ_yg;~LA`Tou~&t9;0_V7zc z&k-~O_?c_)9XB?#SH$^62Hrk!*ubW=s*afKh#VIuH#!D(b+$Hlm&BDvS}AYRd<|a_ zgtW?%AxpLBF&{wDQL|4uznrg|A)P|jE*YX+J#RW+7Kd0=9=<6_3_a$I!Dw_<4SfFj4+(#2M1{tNt?5>oUb1}MnbxtHi$tn zz13V@DTvZmKP5m$2B#N?LDTp z_vd*Frq5Ki@d}KHiI;Q)s$IFTcf+dH8@B9MKXv{Dy6s!Cc>W}XEhcuZzQJwwlUCeR z-ne1&j@^6rE30c>Jacr*p`9C-OdPMU!`RZ+>H4%?ex};Do|xM^I@np88$P;t`S_WA zdpE9I_>-ctiJ9f>EqX5uEW8u4QMjF%mJ}E1W&HTb302MYOQuX1uVrkqQ}cnDeQ<7t zkitE}%Ch48bYFwh2Tq(^J%6m+ocsFb4#Ck0`C$8`X@IY`wz{e;-temW?(NIQ|2XFG zl}RPOOn50F9|C&ig_Eiuy@T zjmE(b&g)KE3=Kz$2vG3wp;!;R&=TH6*0n562-VN@7PFxtW%uWqfHM<9eEicHB11ly zj497XP}TC=#}97?x}}YR!mPwlKTnTXq+pd4WM?xP;;M#EpMLxB?sc!YUQn2k5bWdO z>J|lgVQx0d(-92(`RUgWpyF+7sLY8+O0B!AvrAkV$r%%%==VQ9{rc{8e^*;=Sz35> zu#cy!le0&D37C>07uI(D>-SF|-t_l&Gzp5c5+j0rQEunr6k7maFT^$VZ~pq@)BCsh zcsvtuTxg)TyQ>rY>~==RCZ-m`x`u`(iL`g1x3jIWx;!^N)Zfe9&DG7t*+kFK_>~#r zu=OIO3HNk&v@{4xv*Sa-gY53^>SnF`+`!1#6uEHqA{=p$2+>@JFE=(cz}v&a!_(=7 z?n}BJ5CXN0P0iQ`cqZUVvbgC#qVeT;kcwa>+k_VL^6rQM9MUlN+a$ z_itOXVmai?R;<0Ak&={%FGL{7%`YxbkVQ4I%e>ffIkOYf>@$ru0QnSh538$IC+BIa<#mR6c9Ub$w;1bI2RABe{H z+i$)FmG20-Ip=Q?6E}FcQ&rclTsC|1)~V zukBD-w`j@IX)~ryojhgolxaWlOu&Rb#xnsE4Xdm%##9iKB2dJVYRTyVCSH0QY_6=a zzSM*MBd>>;UC#->>Opzwlvyioqw7&T@ujgLkoC@qc_v^Kr*wKeyLEW$j`b^-O`S3h zOrDD4#>uM`!-&J?3Yz4;7EAqSN4IR=xM0qdaZ2Cq>jSCn)Ua#ClRMx)=L{=;WUa;ym*9APo1^IWi) z`<=!|i5(iPA{du$Bc|~=1*hvlCd&$}(3R_epff%sEe|w4K{$Y~np!vAi#~^xX97l! zXKrpD9!`Io^{bZ}zieDTk7ojIt}DySNJ&mgN=gO| z4k#|6gj|e*5Zs`c7<(~qX#`x)rPtOWN9KRZQ9#HbcPWj(wwi5+OmjHZpf~{PS6F&J zMFMyxU@pFb7-w^7Qm~WJ!>i}-g|t%H4f0m-s5o(FZ?~kOFecE&;NHcP=Pua;-4t@7 zXGQ)2G6?(nyTr9w!CnrA+Go_%)X#e$uZ77QNiOYq{boR1p5*Ol@$A}(BPvHv-$?7k zcS+b!h-^#R2VTGJ7Zt>~+rD~m@#tY?Rn0S&A}*LyPnUNM^#A&?Av@U7-00p}0PHEN zoYcu~q)2VG5MpWfyVoE8s7dv*ex-l ^KZc+UY1ZA%9??||U2C`87P!zoJfceQ+c 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

      HuU3=`(^}7#1EJe9$ayBEN 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%0DIB0U|4MvSS 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%0DV9ahD8x8)_FKnyD?kz4N0CHC+Q&US@XHP%> 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%0Aa>n?P^ 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�c;pdwF?y+C4?8pQ#1%R2s#t=+N2G(ts~F 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

      ;)jX+q zi=esmF3tU{59`;|)({^Z^F&Gc0*+)y!F==04BpY++|`ITCVD7Yq8POQ7*2 zV2b4BNx&5q^i&9|{4MPbZ>TCN?pnWSwUSK{%AtzKKZzR>ob_Hm(^5No^!UM*^JgtQ zY?xG#n_noP@sB67qbA++)xAsS&Z;Xa9p1iW>x#uc?>EP4o1U4ISBN67Z327EJ%>&y z%FCZoyP$M@=lW&yXV25}4UdWENx-)eO-Us#=r)pL2E`VWdCzP~$rW&zlNfV2EVxt&k32&S^_8A)uBk4Sk^Bi8M?hiHwo6IMK+c_jPJy1JU*Zb&4Q@Kxr(~u`WBbY3_eE!+&j=4yV|}@S zs?y>~lC(b+eFRYt@Q%^yYx8dI-!NW+cD3JkVAb|0Qm&>tZpSj71e`?$1+kKr5H6AZ zz%QsmQiN83DuP+W>nsNCjhVZ^1Es`5DpLZ^5+G$c*~mmB7#whQRDpyVq7PXQR){p4 zA`xLI11J#MEl~ukDFYuF^sLlj9^~Z}?69ip+NRdV`bt5eprWRV&QMfT$mE>3P6+r` zM^klSdTdlmNj1yCN39f|1k96wn_-3>=G;0gn>MW9xZ^cd zV=XLT{NtK})VKhDbNxqJ>hcG+ZP~bP?fMP7>?$G8$ION)FEc$p*xg$H(T#Jbc5U9a zVeQ(r>o+T;m4Zn}^6KJ@+`MpS3j>{tIQ{mGBwx3Ar*$zZy%J-&x*%In6wQ->c@i*C zpo#G@(Gem3UT!Wf&dyFwv=C#7Mp17p$EfRsW02=-Quw#jdQ}R^&9#N=@1=rTDTO>Do##D*XU?qDaYU`O1iRY!B5ktPDYzFSyFoXqFqPj&uUz~Sp;Gz ztvzr(5>iF74bLP!ovUb;T#WUy5 zYhKm5c?WWUet8n`AQ4xGh_DN6C!ne1Z6sG?Cwq?*e%Aw8{R~dn*#Y_BFxb3b8#@K~ zohUB=r3@tBA;5OgVVD~0Mda4OAtLocufsb4fU@@C)5lLASeHRGa9JVdNx&l`@B7T3 zJUF*=_x6=57Rk+(n>~BZoVjzh#KAvVSR}wr9rEZ?R7tEbAckb*J??U3zVPGj@-^hnzos06Qps;k& z{Q2|e&6z!S(?i!lo&?O3fO!%yC6^3$nqO4izisQv<#J2pWMyQerPlg%B1oHZNNB~M9ZTevErncGR_5mzl!T=ib0Q7*dc4$D+9VrYQaFL^vXJzQtn8dzI=;*Bp0?t~E$bJ~pCvnu&OdFM z>}uc0=(xm`G~~7n3|pMPwEghfMN1aTPMOK#JPDX50aFMI6_4Rbz!*PhG`_?n zU-IrSH<0Kj3l~HrAWs6O3W(h!?>`PV7sR;RzI%M_tm0_}^^2CxEW-gg7MQI1Muva; z(wH6WX#V!$C1rW})AHw^WH;3_1%N^-`#-(^^0(SlFY9*(chwYm67Z1|Dms=9ZXo@J zMNz^?7f%AF&^+Y*q5LPpVpxf4vTMLD!2*0(RZmvtn0cLOufdIkXf(M5Hff#XjcO~w!f^`XdRt18qXO;koY=3u z|Hy?DDLHzm<0k!AeE99thmN{}_%I*KTgUcm?>%(XyauPtq~N6gT_4{6@prpe5EbBM zcKyiSJ=)s3*VDw5<5XTo`rrG69X9BLP%8B)KPZASr6BfhDa%84r zzW?q0n=W~qI4#oQk)dk|vW>)5m7)p&b^->cYHWz<}xf^UuFNet6X@uPq92SP_@&&%Kc z{^!5mz3l01%#HT2xO?T|!To2V3b4dy3uN++uAbgM{^MW&KQcJ|!^|n;HD~QEs-udWigHGQAglB9DU6G@ zwJpXAyreBGT-aU^VSaD#%Eg*v)Kx~QsW0#p zq*Eam`Dr{8Fm_tOyO;gQGXWEkAf={oG~eJGr+jBZ1fpRB`QNua8_3OMGl9X(yyt6hdNYNF**EtYMh%7HglwSBhmmybWBjmct(_t zKi7xr0NX1vS}=hp6*iy-g+c6Bi=aV$ za+xB>c;C+}7B1d=#-X&87!ffZTOp-fE)6)RwR8RQ9~RHhoWAa9ejVgsZSRxI+q|w{ zIdO8!&#M<`j2k^lV@rG;Zt$~QCXc!AU~=J*)~_pOk5eC^GFoF}p16{TRydv&d6m6& zfa$HBTi48+Iz~-pxZ1c$hP9w{FDWjf>q}&*CN9|?dYjfSn=@Hmb;R%yBgX70DlbL` zU=HN1t&$Lnm#LGDBJOl%$FD4*|lcwgfXL$#-ge+a>ldJ;Lxz}aGnX6UP-*Qjpv{Rs;>B0sz%Ks}%mT3X;}b0DgKdkznrya;kF&``F^omgR0U0F_rKa@n=Cx;TT z4mhD;i7RjnxGZBfo~anug_2%inxT8J{cHxBniP=S7=~O(Y{pdnt1Kj9auO4&D(k>y zEK>*gIYqcFHkx(eun)1X!5m8c$9Sb2ngZnqcxw$_QVPewZX z`x@IMjWuOiF@f$*rnj%2)ZKq@&ms4;q(o$cle`IZOU)u-TCksk<-_ZGM|Jn@+oyFc z8Ze*H(R6>pD!HtwG%v!-#mEq(L(r1 z6~fGzFi%snhu6-XICNm&e%*ry^d1^p+B!Iahqty?m@7<+@^O0l;HKW$qel)NIDGiP z$;-E&m{~hG!)vXfTTBe|uzB?G#-%eSPaHdWV*jDDSMNT-F5i*KYip~sLtQM49^SmF zr+4A}nWM+fUA_JAsi}pfHRNJ^qBY`@I5+EO4{qPSsek37-c|j(4<0`=HMg+plk-f# z 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�elW+y8Ynp8L1F0G%7u)1D1D=V?%oAV0SVLXml{2vvv32++~xt<+<( zx{|s?k#yviVCpp#Y;vrj-V&&v)m#LD%^?}e)N5SN@p^#J@kqdxm13aNM&Zyn1a+1USgR*MSWfP#OxrD?DX43ilhxcy) zwcA=-lA96{=;NJ$D*UqIygcsemX9BQf6pTUw>Jnf6T^c6WDFo;XGb?5FHf+R){fr+ zBs@AK>TItsOp6Nx(6E~;fQ0Rw+}+)PQ{37y_WL`$+`g{1nxgbr;23+ly1KZy*x5Qb zxzvH))%y-G$%B0WA}&CQv7fi6n=58mSz6iH5xrA5^!CkYzo@ODGB+_I#Lvsa)z!__ z(Ztl;!n&@hrKMdc8Xg*eX;>F#E3U~Fn;Sr5&q4JN=4mT##o&r6EH z<9T^`c-rae8JU<`AWyZqtrHV^MIB9mc}|E3_Vf1k_HotMeMQ&T0jL>ilaK>E5^yzH z-1Mi4jB7Nx0E{A}G!w-&7)&bTJ7%y1V@x1A{e}RqVGnSFo58I>f}956kRieFX*9AX zgR}&@!}v9|^^Mh;ewIdh1`e_1O$~GpKyeBLR^|W~;U>*sWM*=4AU~+7DXfUwCaXy$L&rWP*WhHmUQ5-;r7e*)1Q8bKG zO6EGg(kbSVfWMQt$s+;ZdSz&gAV+1H@`g1l7tNhMapE^$iN`nJ{qWN))u+#1!Uij^ zHea)8>)Kh85)$8jP4vGIl<$ucOV#cZ5;p)zGi0}ITEArO+*uR8{`#x0LI3)j2@|L6 zS5?2G2{h%hvPwr3M=xDGf41azUxCge0f+c`I6FF^5sNL*Jc>$6N{R~#^7Hd@(bq3M zH909UJ~rIX8)}fdo1nA^j_#6@B7lD6<>lvQVM1zJY6|)(1_k*0`g}z|zQFoJf-)Nf z$cJ27a+Ffi4MX|`bniGE=@0>y0vS*|1Jg!$Bw)OPvEihfr_SzOyMEP@g=_Ao4ZR-X z3VXUcfwT*9i=(paxgERLE?>H2{(?CR6>7#%){9K-wpJbqn4p5kN?u)Fw{hFbMT^kC zVEJwp14rNR_|(kY++3oLj0_F+gkC+mY2(^8n-5*qF|%_Eh=@%}%R-JGBM%St_x86J zgnPRB#l*)&MnuLZr)TBl6%-T|iRpt>XC>@vZ>g_D_hpLy03jS8;$=i92%iT5RDBWT2gEQttH?DV_CQ`^6Tox$#ig7 z@Bx7^E{=vIejW+9hvASiBv9@PLpDUhKCC>jqkUZua937vSl1eXrxaooF@m*_|pf|{@nX@ou`qYJwo&Ca* zWShc1w~?aj=a2G8z~DesImZ=`Fc!ii0rN<}!^0yzmMRzbZ`-nB>FgyxOG-&ikyzu? z#fnI(t4aKfWtv=UKyKfGt?Sm#n=?&v z3UIonOPnkyDl9B0CNHH5I!$7qor_O!cvLLO zztNTlDo1y&Su|(vY~l}6)22$!yJ>9i5fC03Ma*;fO_7@1;T6jl&i#4D3@``al*vCa zv~~3k2nib+#uE&U6g)Y*WW_J@XUzipn7iiKwTCZFZJj;5d;$naeTYW_=5%fnh&&Q7 zj|40$bN*H^R|7&J+TMY8BX2(dq0I8t(;FAhoH`?OT2|E#?pY|f0v5p??j0Qxggd>` zd306o)X6hvPn}gZ&W3iKm!D7cp1xjTQ&EtOuJ-*a=T4kBb?W5lD+VZ#Nzcg4V)UNQ zhN^r&Q*F(gmt>9|J8|l?tcpQQLUJl>Numqe;Chd9*1La0MgG*G!^cmalDlRY5{?-u zDeV3&<;5AHb}w(MDx5oU;Lx#SC(o;y`2>bW#wH{|Wft{zl;$S;I_Rk@$(=oM=-`p# zr!L&GcE^N>m^jM+=@nIE#Q0c0Ra20YJ#_>NoKn!V0ZKq{SY#|k*hGD8=|LVg&u(14 zc>eT}{fAD-s=Tmr@Blp`8fS)9sPuO*e|}5(f`aU^BPUKPJ}^axQLvxLXo^td9g1@8 z476^lC@K4NfYeLEL`j0Ir%$SB;MZUN(g zM*^nLf(xc{*%TD=1jFLSr%))~77M0wA=6J}ddF}maGnQl0@8~~v%XlPUt-H6coIP^vw5;;Ir~0PW4$kggC^x~*;gNtD z!XFYq>S_=i?%{(o59qktu>cOAT1Qx234Cec12Tscmal&zmMOSz^l6Is2gCQw+2m zm-p5+)C~OQbM4UP)hm9MoIFut+Vq)gOv+d7*|1?43v{_f7mPC)*Hi;J_1o4W^fqh^^qEp4rh4K-Dj#aT%)QBjc* z5n(|=LBS#HVjNC1^=N5qs0DgNDd3Rc#ZO8~h>nhqAt{M%N-jlgn0l7?US6D^lbMm8 zk&>8{OfnV|sNjxziXd?aAv$D&6&K{?WM`(OGCJfacmA7k4~pm2)>Hsx0tqtN-(Ulw zFc)9h4^%5aU|DcYQV%aoWWPq567(F>bXaq3xIoziqC>8K8kiyWqB7t_cKxReCy0Go z1b%NM0b>DX;S!SP_}xj5BDWt^39u?>Twhc3>EZAosOoQR#F0WJG#-()S@?pEPFZzj zJQ8rA<Ve-*Y=7eqShYN#qJT~c!k508k7j1u+}{ryK|NDVbtm8SZeYpY$txaA7{ zIV?O}*iSx$H%O2g?5-1JN4x4jR8dg8q@?NO?h_Om7A^!1^3dqmukYRrcGVW91v%(H zymJ16;+5z2&QS3~h(0(tI`;bYkgypEIKGzJcT_H1RJvho?d0a|7l=OPZ~()1JKEn? zSCkUyVDLcw@+B4Zr)D;w18NN6D!72hMn?y#bJGIQhFptB0;UcH2;hSUn(uxpkyBMNWjPw68ARf4zzMJb9?^esq&6p+ZHcZ$Y?`85}^emM;HcRZ>nW( ztebO&+XKF>Hm-n>>Kf8W>+uG$nOG!?f zGIfPjLL81aj|7a(4{3p{Ssn=(N#YRxDEuiS{y@h+KUXZ@N#CXRmL&hV5GXQ#ppI00 zrXzU46cO=+nmR@o3xCdzXDw17y|F?~b!|P=oeIcrPPQEc+%RM`Wk4!pVfo@z9t}nrQeE-*wiUCdOH9OsNm8>HpSXD!6HGwIc4+G=cp$TX z!TTQnwp^i^&U1(hL?!&LcqK<~DywuR3l-l~*I)VWyErBJ|+Swx<{`kjl zuZMeD>jfEUK^`9A72tljytoj(9NW8ugMa+-;qBN!S7SYr@I%~PU3?1|ka%7$4q!*O z=)+$>ynj0aFs_=CjKpAf}|Z# zkO-QR!Xp8Mc7abWM z8X6K3930X_y9>JmmOQo%#0t#wthD6B__&yu=;){@+AhSQV1q+)B5YGrQCgIjm7bb{ zIq`9EO-&HGP*Ug*=vbf%pL<~*S?|e737~TmpvFM3k^o0@|1uc(xmg*==1XZH{59f% zSRHZ|HaVl0mO%T@PESikr>_Q-LEzb_tsv(fC@U)~<;(*@lEfWopzk*$;{ho9o~DqN zFixf!V&?$^v1BR)=lqz~h`SG40r~r^1vy&+bauqJDcmhk zGJqNaa&Xi2+ySJzDBOd4NGktl4=JXz)6Ghw#1BbT1}Fi*o5cE`&p+2Z5igO`gXsKY zeK8&H5O0x30=}xqBLN#48Jn70THD$?((wV%ZzG&?oy0VIiMfdI^rI2R@WlNK!);dvx4CzuG8 z@POXVm9Mh%4-SA20;{7^p&CaV6CgiHK4WleFa`j}A&QQe04J`?$m|Plf(K+p4b@6m@oh&*JV({_*sq|j`P9(d28vQ$T}zeb-qk-Znl3r< zr=KQInKgI8x&vpGZamP@Gqz|TdR=Yd(>;q9NYD9s&g^;8i&tzvBBP{6PW)HKM6X5I zt2*_?$z7Y)t=qI^*TK_r3M$t%G_|y!>l-#gVqyYH%BqW!!+o8tjdXQ*Bw*I77z#1u zA!oR=8fdYV&lNL+%5t4qKp$qfcplAxGQ^@Ltl2sF^_h?ViwQIZy~x2E0Ea;?1{U`x zPn&o(b|(%`gXpY14E?~y_(BN0;N;?0AhW@x58xME8UkWFIG70kQUh?fnUUCw7zT?m zaQ_m}3F#M5mxDtiW1}NO!p?^B!s6<-9of zl#~b$D+_=i1~Sa54B+9S86`#exjESwS=l0NJ(lMzmXxbEv0B0n&8->PMdk|6_t7sC8`$Y7b z1A9m&I`%B+)cqgoFZ52>_go*o^z?K{JaR6O$bMJY_bO!-@Kuuq64n!4+GjcF6 z7lK&9W!fj|{|cF`1CV@uMT9att~ zyQr&gPfs7K(E39D1L_3K5|_7wF6tW`WIcY_tK?J!gcYy=f)*&&0sHJH7TL#D_TX0d zN2Bo&aoZL~>+2gB9_Vfq6c*Q`=!i8f;^;gQFpmU`UDex=X!Ah%+?i7+P8>gazYrV8yH*IIJnTRrajx$Qk|cc7!?wL79(EX-sB$i^A8LT#V(}-O2IJ%z99Ss@DRdr z7Z)F&fRV`ZW+(@UELdt@3A40>dGFHGFjBca%@hN&q7geoWR1>$L4IC77yvC#1F1|{ zf!%{vfs2jY%O%AihzIpAfGR0{Dae$C`4qJiQ z9-p7!3pzL9iv?h+H+)I|Jb^w9JS2|R?)!l>tBaz zQiAOr?Y;W`_J989Zg0$piOValX=rY37xoQ~j*j-!4r97*ZQ05^U|`jioBeIEFG+UT`lySbq&<7s;XSO zc25s|zI#QrRT247fhP7rPKM@IHg|44c%*qnLG`-YotI|F(ZwCQn+if4^g?Z)8`{3o zzNvcuo}8@ut(%&d`=lu573ueq&tF9xg zjd4^xdi>0x-P^Vtlsl=Us&?bvRrv$!*Q{PJOM1Kd<8D!g&8>q6eoj7#l)CHMy zXOHgRxM}Uu1#=fHJAUH{P&E9tuPI$tQayI!^qzxzjvUyre)qaH^XAN5xNL`_=5t7| zj;P0{RZbn-zW4P0&0DwaT(M~J!UZ#DO0V92{;rO07plm*3SV71xMRcNEvwdS`epgN z+0qMU&0DeY(B*r`#D_)+3%4;`OXkqQZ7VmfUAc7W(nX6`ZQU=gc3r2PZ`4B49y&i!NjgiIG4-9vZ1IwlIBY?dT-g)tr$ zx=(G3(A<<-0HDVf(Q&!RLm(7&RHp^l7(RY%5TBb@$eKTulyKLN);SFPI@F#U?QWu@ z`AFL{H9Z>@zy*bc`3S!t%YL*y^4Hh>^;v%Q22UP5cxV}ul%56iGcPZXT|YcJGWce& zyDGuk((uV$4Nc%~p+|BKTvYjZKmAbAM_<1kY%NR-aWT_>bo;J;KxAABCSV1a*MmeK zd;e=sS#DOCqs7w)4~%`ous~Wyc1{jzmc#_&HfrF8q0}^@xl?uoMk0%gM7#{4URzXNnMG6TU@NdD%M4e=4&M+1L zPA@)pF5L#74;H`*C{&<9y=QQI;O(QxHu>VPQN9R5!IOr=#UlZesX`u09toJvKmP9< zuy%@2*|2Phl!U~zo3%)Rpf<&2ToEA1L%E8|mI~_@%#cKi?3SqP{6ctW< z`-K+fW;eIbmzwnBq{%-?n?**WL?0)BE8-W425O(%hdx>}MPlNFA16+hRQ3-Hi;9Vh zPe@7;cGLa)8$9gQWEM?9hVFzPC;znH!2=VbV`Agt+4`8$9V@d9^dBcom@skjCUZxx zfY8Y3n3!15;iJV7Xy=iDnX8?7@+n!3OI3mWf&a-5H|~h12|N-oWz|r65ZL9v4bn)+ zVM?`2U(%UwGC&NJCP2EjbzsK$#VkzP+Zj73og&1CKNoRUJ2ZH_7_slSxzt0TZq6T0ly55HobdNX2(8V zuYjgjD0p=UapL^Ji4Mm1FJ4sRk$|^u z*{*W!vaAYHjIA8p0Q8*|=5B2t==4zT@)gA+$BrC1xaX9r!bzpaFHNi*f#loMEXeW< z3)R1=d{b3Xkw*e%Dm9XCxbx2xYw~K~4^zR(;Dd{!vxnshAjOtby(tF|qLLgSn7|_e zm+?rz-yOBKae@QQcF_-CPn!GlSc>2InSZS#XrdQ&&@7#^_Xnj=`nq zQFMXg3)E_(vK(TVTxq1CQwktfWhHAA>TjSzHu??cOQYOAN&!GM9+Kyg34mHU9tju; zB5!_ch;Z}`iwcW~O-YaRwR@#?TjionbYe<+W_Dh8Pj7!ipqIOoPZ-+TCq;Y3$M`+c ze0KNFEB{b{fv0x&mm7qo`Pdoin%M;=_1fE&et44d((kVdGpuJL(wU2*wTiJze~FgjJ9Kmd}X3L>kyIZylk z-JOyWQ$gnzpd~O9!F7^fP0gN~_s>lINeY{j>B!i}Lew>2HWcqa_FXT3$vCLQsD|6N*r$ z#F}xSvoLdaA#y$5o&!BZ&}cY6j~apyxw;1Ka5lIG09cnAk>(L(C>&*k1WNecsOY8& zeDwFC3B`q=S1~YNkQ!P$TAFGEC4%buTIy3$T3W*B+_F3p@aSMqQ(b9RbXbtDw}-2f zqh~VO$kf#}wzPlz^W(?&uZQ|O8*7TvqQilO>+bCAFE}WuwxI>|kH5WpJu)EVk$|(}qr!p%g96=+42(@o%`B|3`RbdX zf>X1`)*3-hLJV5`hIm?;nVFlLTUgmp8W*sJSvVWzy;Vgp;S<7q+|Y>0&K3p~AyL<1 z0`bLGD*r7n&Cf`T4)yo;aC3EWa;jjvzpjzBnn25fs`8Tjob;sF@F0IbA9O3CC=wk2 zbR$8-pSs#|z+2~Jr>7=Fh5_f(-w$bGVyJAC{h{$4kO9#g6fK4l(a9(*B$!$#;Ly@w zDNo#Y2J}luzl2ULYq|jlU<@7!_~e1TTQ+Unyh|UYnqYA(Pp`fhy|;p_3?ARSEOThb zw#^&XZ``!Uu?F-aa5lWaoUEiUFFS+Bw=SO9vvtR&_3PJf+$x({0R~6(y0Ywo!YDUu zLrqn=lLvNgCi;f0yY0%*%b6OY)D`CmO5=R2pWjtJd-A~6jT=B;zj50ge9S5MKx%6X ziUgG@9>$MvotHnhXYu7;33NfffBYI6*dSP+A)pJdib7y%ZU>*sWM*?19g5LV* z;!`F@5^st>wtD&8*%A}K`}T()zWw(52~xMKYKdM-ytTF=UvbCQ^>b%Sf%e_E-+uG$ zcRUjC_Y>#vNWeEW0J2+*0hrywc)gSB7tdQDy>$JaljqJ~M6>dnx9@@u6y9Pg>Cekc z_BGSie57k+@buxmyZ7#EYCe2SFus^Ts&;O67P|WAw-MxeBVl_4jRkrTf5+be0LsRjU*G=v zhE0JBdKkE@Ame&5_3b+VZd5i6zI}@+SOMAKWVIrDot7Vc)8Kaf#GzdWWFF?eem4p_ ztu%!UoCmoJuM# z&ipM}9wG7RIe7(zjLstgW4}>*3hZcdnNZ6R=BH?GMs*1{#B|!Tb}9dOja71B>lGT2~EuxQWI0xbQF=)lco$hq*e&=3doDN=;B;1)#}4Timo zLI^f6gUz8CV){6Vn@DTG>%xj5FoC-sP~+lfz+~<&><&U62^hR;w8u(S@!*c_t5?ol zI(HgiI3?Hn_YhJFYDnnAAImVhA$N55wp~l-u2>HGv}se9#iJ!GS!UQC!~Nbb@1NU$ z2!_S{c{70IId#SiNf`la#aagXNT-eA%M1JV?pnEY-i&F$0>uK;R)lA`+n&x9EB`@y zD8=K^>at?|a3K209VyDzV{UY+65x@5!MXZTVuwL*ienpSc!sw{9Hgh4I2(=tV{w?u zO`vcxJij0l9o1Q&GcGwez_<&d7X9qw=49Chq% z4Q)<3i ztZ;xOLXkI_tv=Rda`w>SHR~46nf>$BsXt9SEf_+IG2GOR^>lL{37AI$X4{hO=q?u0 zhkU0}#99*1Y;b3Tq9Le2BnCpAavJN$`41D`B(t$;5oIAdYt=*RiU;u@7ES4d=0NH1 zXyw!VwKy(eYUK4VVc2Oh2a}3LPqF_E~OKTic4?`HiIcbgcQoHix&O$MX3?KuI|xw z(1d!Bc!H2IP}n#!qC6etsbQ`rI=9ud!n=CKidH*^1c*A;L19Zte2BY|*7Zx*ZaB43 z#*6rR+^u(HbfBj(H_X@BSW{JA{=AwumS=RT5fF-oUcVXZsZ8~Av3{wcBqwv>%H7O< zdV0KNM7D&zW3S(iwiU;FIhZ}Ue&O6%*~_XnZA^!2L4O@|U~K znX@vNwDVfIzUuVv9{lz7hd&!KeC^B(?Mf}r?UxSI>#Jv7BD`iI(=TBjuCBj44T2MfV?Ii2^cSB^v{p~`0u{jln`Hc z9trp?pvccC+7@$E_S(fP>)$wXnB2!0hq$a~q`r^EKi7Z@-&7Wy-82rwmN2 z9o#%T$s^Sh@=@(WLOjm>SH-Jt^tMFm`kNU{ZJWbXuv%?q0iCYT|^6-+%wZq$x8N@40?Y3rN3Kwn!H2 z>=NavBl5{30Rtt2BKkZMFrDmXLhGohCZmC=7K5Wh!@X_Ib@}mL9w`lk+C=m!Mi=!B zjK2T<{oA45?uLvgM_mKAa%3C9&r@3sHJ{q_{QBW$Kp!6;UCSsAPia0)f?Bs!I(!z?FU68hka7>*x+ypUT&NtRtKH?h49o08guQ+ga> zeP?C10mz&bWw;HHMMREA>?_j$*$f^Dn3_Oh^!9{3y{dWboSghA`4h5M~8fx-; zHZGqhHA!Ofk3UMzUV7n~9wxZBWACDOZAZal`QuxbE?pog@e{_ZMO$w@d1-8E>*z`~ zFmQQw#9ZFJbKSBzGp0*Uk(|AJgTe!CJ)B~DN08xAN3<(MX7kpyD^{#nwR*#@y>j;- zKYwLvVQpv6yzNvm!y^F$vW(ONat{!H;E{koA^i$@B;Y6y+uJ)tRz1p z2|%F{;bEa6!GQr(&w$a2vb!eu-hlpES_~W^pnxL(Ix;eX(UJZO7Df6bAc6!~qaZtj z<@&@%vx*y%?FcF&R~RZKOOIzz0;wrUiMW!20rVJ99a!fv4nh+%vNO|DSt0@R^<#PJ z%a5>eWjQ)X3IwPq&d&T6&&~}voPm+YGJeU4z+n0?Yn+TpT;qOMy9f^@p@g$XAUbM}*uRnC_)=g5D{y$F z-1UqM8NjdvQQnAuB=?Zer08FtAZHXaF>;Cgu^ zV2+oz0QHq1k%`H{0v7`jgVx^OTvuLHP*T+f#0x-25>hFnv$ATygIDKdMEE+{S$O49 z{WWvMf{q2idfM8W>WedDgWc_QwQk&YC~x>zI)RJVRrJGXNRVG2)zUjg>iZK_1SgPwrn;xS)7W$pdgy zutAC54!EU`T0usLzoXTQ2O7!>=g*%%s~JmuBx33L1a-Zl_KLztZ&xFOM|XK7U}Hlb z2^a-I*#80nbpvBI7G)hk!V=Igr7qF&pd5NeCIgH|0_Kr`C;jjZ>Lb4SmPY~(3=Hu1 z_xJPjt4GWogZbsrq6ek3g*oZT@i9@%LeQ9ZNeh!rTr1;nv0^4G0Jxaw$?}c?=T2hKXg^2&bLV%l_n$^MbhiYGoV@vJh z%kT&FXILcdyH8<#YNtSh#SFPbMR;C#B;Z~i37AI$9zkIVj|4o;lKnZ zq0As|j88!h9ghSI%#kL9t_t$AVnahiTwSdUUp~}QyR58m@#00;2|N-oMF4x7^P|0- zc_d(VAX#MzX#<=OjYk4Tcz1B9CB^FT_46{vk1IU#Ef@8(#xgy?b#Ln@7@77UP5CpY zj~>{8I<|G24r)fV_6sS67xoa51;$s;9Xo#N=z;zFcW+p`cFixF&ROTfO+fBepx9)+ zys9jB=G56UXOHdQw|U(!%a<%(w0!Stz5cz z;o_ysf7!2{Rg~Y?<>z3bb6Z7O;jEne$pgDLZd|)+$>N2J7A;!7V&$cT^u9935Z{*? z8rROBl{tIp(5?+@m#tc`VBrGkg^QLfU7;7-m+6@qqW4hq>ha@ejvhI*d&`EkYgQ~? zC_R5ZrYyeU%Oe3ps_;m_VyVmJ5`V_w#SW$f1t!CpSf&Xq9Pue002);Xh@~=Gi4)K- zg*{j};4{+izfPdI0vAx=5+F#lbE02-2S%qNxX*NMg7^-!0FMOh?&sGr`axKoQ-b%= z($>M!O+`W>h5f;Ek#yMT0%5Xk_aX#IXQ)jMJR~~btTY2J6oIUD$7dHKtDe>Cnp<&71d%5 z)&LvmZ;%S){w3#eaZzD@eka|44J3<^)F8DMc|7RgNr^lqNCCp$=Q7{Xp_OuZke$Z4 zpveQrfPQhjXmlfClBvb$09uPBDAk`bFviaos5}xdF$ggdcvmWq1T1{>rZd&>j<#n) zPLZIhuBjDWeF=t;M*=ptb#nI!=zvvAY!e1&OI2QSsE50!hr26v=ne=Di-?Lr{sU*; zQmbv8?dsA3G+#n!g^lE7YAMR~A;n8Y-f2@ka5j*Ek5*gRSr|B;;z9co;c19HYTt{a zPh~s><84TBj&XS5V`md^w!t4t!9!SkU$Uk#INrcCc$&bxf$aCp78eJwxY}__6+jHV zyo@~fU@6o)kx~Wd1|(MDP^A8Q<>g}W&dO?0)(52*)5fLmXZa8A15*m`iT;%=;eZq0 z&=1DtKQX!&-a1P0#qp<>J{cLAU=HHZiE~3I2gi>=@lcr#fJ$+9 zngh=}oh)z&98e0eu>f>wDJzuaobq50#DdrX#`z@5u;*BuUDWoNYx)DssFae`%2HW*P$9_e^M#l!hk2wCrkT z9njU>!JxxKh5+TIFG%7ap$oAYTx(uMH!3#Uz&g*Pgg@_%fN7VPOyDtr@ck<&=8LV9Fv%uk%i+?7ZZ?@ z;b;B);^_;{wz^MtZrite`vryjJ`u4ADI`D}W8Lyn-AtbyI&$yX9WAA`n>KG)DX;VV z?u$?eQAp;(5F0lyOYN)M0s<|rpWeRf$o^$}0|V`JFB(M0#^dFe`j~0mGqTdwDhY6S zDSu|)p}l*q#)doC+sAW4)3|3Z06};dEXoo9{aI6 z-qR>6-OJi6+Q-3EO?l5Y9tk*$M*>b1kt)-QlGNWiyrZfjmxvw8C`bI(1~zIETp+dl~Wp*qPsEZWQT_QuFivuh`}?>@GF-p;5{ zt4C)aq0S!Xzb4MZNXNnB`J=i}rx$0A?>n+@w|aV*wV{f-i@O)ze^+scfn!m$lUI4L zqv5%eN008gpkfQGWc6o`&K`LGt=SQFMFoMjCUM@@x))@2ZryoBS@rp=XV1-ToPhM( zRg>Ur6CUehe@)l1om(|NhtA3bn z_GsDkDH3xIEt_P`BLUCgk$|o3ok+j!>g?&PNli=ecXx4hbGEfKH8wFv8xj;D@<_nd zoDZ>g==I28S51AnpbDr;2(~oAIYQj3nlZ4UckkZ6?rm#mDl3hN&nRwy_k>j|qvavd zfBpOK!z06@w&u#3n&e>b@U%i;k5wSTi5Mry!~gTok3g~N=-`on83X5$fQ9{N8$IxP zxYX8 zNQ}%Ny+aVpiy|tz+)i=b)KT*)qL1EDG^WQ-sSYcaT zOKnzEV2Fdo?MK#;m2~>KLjNjU-ZRwSQe0M+5gFp_>HbJXQNuJOue2DIP~~6)SbgBP z*TTx;jOe(uh)^fvR~9;tb#w!>vU76t3yVu}IgbR~)W$Md$VJ?VRF|fP&cf)DFmu@r zm-Wb#i$D)vBj#5S66Fib5dNqww(MGTuHwq z8;wjg@giMqqK@2Lw>uXua#xU;q%<6kOpEYHz?3OaE`YcFUj{x8Iv$jOLmfTK#Ljrk z!3PBz2BHg$jMG(F&VEJid@+%(rQieW7KSJWr`Cg;foD;o06EQ?@gEwBF?#_lk1;#? zgeY*w%mO4s@JPTs5-^VhyyXS?tl3}zMA8OWmJN~ zFlRpaF9sLEY$NwS;}29BT=N&!5RAZm%h*roLBzfj&@Yb!T%4E3U{-5dK7RcD{jaZw zdzx!YvXjFA)9M+EM(QO6c?=1(cI`TKadx_K9s0sjs3y2gQj{PFSqo6+IE_F6%1N_1F&kEffvYeF&l ze1P20{O0d}eth?KY*5q&tl7Bm5Pv}Rx;O{sWTd5{#=E)o{og+R@f&W>BLNpAMFjbJ zd3t!dyO|ppo0?hHH#9Z3AyI#5u&=YFwmdH>0yxNCULKxyx_U+?rWUA>Z*D`cPAK&q zP55vVB7*(Ay}f;0^>vX8Xl99I`^MIGR9E!$bktV~cqCv#14jNab@4)pLMg0trixSN zE~b^^J7%y1V@x1A{f4$VY3*!qGq@El>>x{Va|2 z3>;$1n;K|B1$8?er`K1+I=p;%UHSZ}!-o{ja+=V^hqdY9&S!1CAi%;{Pg~=%{OQAc zwr!PhtE*!6I8@BE|^+eaugdnJE$_V1*$^9EwPVL*Wel4zFvu@M& z-O3N2Jl6vfJSL>tm>Fr^zjjG>@8)%Df5G}2w(U5ge&>PqvkEq$($7f$;Y}3<*+bib zgS>X#<{i8C$X&a6PfMqqKQPtH0pS*I$44-H&s1oE#j>DypgrLre%=0D zgGT}$0QMXCIVs1N9B!K9*#!>?jJ6A zdT!Y&AQ8=;J#*HAjWT!jtz3M9!Xu+&>Ep!5G1wiTv}e`IrAvOh0}s%L{gM@{WuS5A^d3M16l;Vsct~hIm8JZb9NodkZ?b<|6Zj0zkQWdHMO{ zNZ1tnBXCxD?x;Q=^|v%iOK7XGPn+#0&H=}H?>GyXZAWg;vJHnB7(5a%_re)cFKGi@ zvmy3NVuF-b#L0hB26!Z3OO=cJw{2OmboP>;C8eaMNUZVcVnrm?)nG2eW0@w`We@Mz zylv6XOBYK@O~s5w(e-e$pi2a)R{g#P59Ib8*t%}*ygAb(rvRsGy2Qx>RIwBk6oNk5 zZD;mMVb87|%NNZ74me=Gq@<)4`z0l%q@-og^}QyKAIWdrvR-=LEG#aGJ4}^Y>Fgg8 z9u=2B{AtWe>FSOnYtedW>h!78FlFjAiG6l1KEdHpv81n!wmeWdx^vBIR^KKg3djy0>Mu9vu{H91v?(mA`3+MhkW5$f>QzfS@m49Mr>*^g45;io9Cm7<9 zfO#ZfG6!H4Fus6b2c;VFNWk@V^ip^v;EIeGAM2-T3UacijvP32>=cg#%p(Cq`$j^- zrvPeCS4%~9T#&n4L>1}gq`ae?@e>kw|3Gh3Nm{tG{v9>VppITfr(i1=e(fI{8x}Pc zr$@Q!-%`G+X5T7AG74SKkiLa|zrKGnEUe2(4tITd9Z2A+?v&Ukl7(AMGqp zjSh0qR=ps5;qt9CA=!Zl_ELwLzTbZT_}f5pabmcy)#ED{WaX5UEgBK!r9dn!LebD~ z@BjRJe?x9`ptsqB%W~&rWfdM|5F84k7xBlXQ;?}nAqvuDhlI(7cC^~Y51JkoxlZ)9$3hZIxv!oi5VcJ%P!Lz|Ya z-g-pk!4sVqdasPlZ0zh=iX)xA&gN1B~r&Vmbuf?*4o&B z-d)96Nik7C0f-0-3If(B#0aWw&~FwEfErnA0RjqOZN?ISGa5sAI}jw}UIogEuPP@P zfQd0l#n&ieE;#o z`!_?)Ri&x^=GtnPFmAcRd=Co`7g7(+kvB+?8tkqUWJkN|KU7gryriUw9(zHdVc|mH zAP{rc|BU{`HnT9AYO!z<@6C|-GP@9gdq7((>H!O^kTuZM)qNWk&6)V`x~;iA$F zV{0cjZ@<7$xb_ACg*-Oe-&R+Y66j#?K>hM174@fPC;;>ZUN3w}Z~>2vjt*4krUh8) zX=`cnNWhdE!y^Gx5s?__mq!AwXen~Lqj+}b>V-3>OG-)3T=mMI`dmiB&_NPEj|AM@ z)KZt7n3}F$WNf^Tf6zJuRgjr7dBHG$e3z+ZB_{fl;KsF%3gT!=7+NGp?DCc>Z zDG710u`w~x1ocUD44_m3W)u67`hNoFGc`Fe0nG&C;wW*SsFazHvNuZWXHY=d8Keay zijh7!I!oLq04`GS0pppGo|Xz56_N#gy+s;45^z;sYG_bOZGCMW!GM&O zz*1@L>=6zli612ft@VP8v>*?U@CvS1Y;hsLj@r9~gMa+-;qBN!S7W^(Gcm;7)y21< zjG)}|a$y{Hbc;Ux^~3wOBfag7H62ny{GzC_KfawlICk+BwWpunLd!9@T8XO65 zvf~qw&yVzcB$shB7@eJc`iZST3!ngin}A<}3Jyv`d<$YA;~a#@R5S%1O!wd(lH4HV z=)^4;oyep`u?h5$%z4iGq+@+{HJifqKBUw*G19Mqkb1>*aOR9;_KU4BxatA)Xf2e&StmzS55JE>sp=T}sre-fRZ>TC=Jbyt!@uJ2HV=FsH=Z1#H zrY1pwAS2q>`PFkRjqA#n6)#@8bn)ulC;DbKjxNwz8wsp9Io#7$_r=3I>Q_~-TvffG zbp5`LzNwX+6QiU1eqNZXrO^wm`x+XzZmKI^xpDu=i&v(WRyLqF;1g|ZC{J*=F?{~y ziPocgw>9oR(s{0DXlh|;Jx)hkiORyvgb05(TXP->m{0<6z;M2qf09G}qw-*wF+37* z2jEq~#NcLu{3h-NuF=sH$s+;tNWevf*{KQ9VZl5SFai@4fd`T&HTXmvoE0&PtCbNy z?*Zl!VTMuW4{Mx4-~VM61@JgJwIN^`gEaZmLWrF}s^b@|QyI%D6i=dZX$)#6+=3q4 z$Uy9en;E%=pd-5&`2pkfmS&>Dg*-AgIx-~eY$z`*u5RlQu^G6Y=sXfIj|4ozBLR;$ zXrY5mxi>r#Fw40h<|3>|ad})^cz~yqgNgn#t-JRPLd&R63M>&Kw^Wv9CdS5uM+Uk$ znCm~&RJ*F8`oJ!>xEdx(Z=aBg&(o4(BO)S#Ty2dE^|fwaQ&zrm<*KG*eo0YZ9~fDE zae8WESX4}qtG$tt?xUM(S1v0lUcPkcWg3qJj1Xa4S$3SCtD}X5vHnx7ySHv$zj{SQ zo}sJhSw<5Y}L?)W0;YTiem)4*f8%oC$iuyi@ZBhd;4f9CGc6CsuZ%-dk z80msfvxMkM#7cD9K?krU;8$n@8%&Wv<{lR4>}W;#1r>qNll5_xJ?zG~NiWK=+FKZ{ zuWw)&4LAga#q~W@YDMCo(OX+lpui&mTRzZG-LdcFkuxXu?b=FE#0%%mnLB@>^pYDc zy)}V}>28nis2@8aBYXDL!M&R|tX{To-h9xd7ya_kQq*D{8}6ceS6TMhDOs802XXo8 zr3U9T{HJ-erEfJ)7^YZD#2T;YhecRRz>o=f}z?${jPF%UI^-Lf8NYowm6aOM>2ovzy29sa-`T7c*>x5Krh>gmP9>yQR zYX__zh4}lmXU12y_fS$Zm#vKb4DQ)KG&0(hV#^}|`vUiva*#%bAQzhQD#{Ae62e@a zyh7Zp?OZ*8286f8l9+fTU?%@@bg4wKiF$+)U~xG%A_fjG<$}-@@`AJDP2oK8`TlYO z@rQps|HJ`khIj=s%g6b{XC!a71Zf0}L400dd{>LbHbZOq;t9r)KIv@GQ+~O?|0fbS zj|6OL%Oe33fQ?85BPu!A-R{+`%gWd8YU^k}H{_9kDGbMyZYGqV+mmzcf0zF_;1EeT zgaoehZ|7eu|EZZWj|2=B#d$7UItq84d1BBb>LVZIC;88e4~BClbYA}S)hkhJc`u>R ze^LOr9ue&w9Tn%7SsClMXz#uk-q7CzuG&t`cnPz%y`#OeE>LyHYAr+k?7NRI9zJf6 zRast9UDt?A6>`mVw&$6vZe73Zh>l);c#!t#jVpFP^iIky6jatyOS_g5e*^Vh%NNYu zz#{=u4nO_DYvYlCnQ}?Kc9Q2zobyP);CVa}@bKu!Addt*jKL!T6JKCK0Q$ls0fRqK zd=l=*a&r?C**Q|PmgtGVJUiQvkA$2gVjlz>F;wFD@c6#v(&)mmG`Smyv?%hW7N*cm z+2sS)P7x{_mMxKzkeGI}wz`IzkP6VJi>}A&L%E8|mI~_@%#fTcF=b0sc79=D9)Jvr ziaC0}(8Ap8=JxqglYX2u`6p?!$moRR)RffpOal7D`UADk?L!}}nIbW9!jBUtODg*Z zBAyTzpOBOygogq)a)XDxn#`gp6DLlX@Z;p4_B(iBLUc?lD-xvV6LrVRYy~0(Tke!0BjWNGuLomyWR%rpQR>E_z^eQU})>+3nX5wfcHa>0Nt@kcjdhKQzlKC zIO(Uo`c`NGf)a!%ApH^@Yw$?GI9YVKn4!*PyCFY``{n1aAd{8OCA0kpov54+N!1sm?P0vP2uXq9!9q~xO%&{&KHZ^+$ zv~(5aBstx@denFTZhOQR$r(!SBVdTt#tD4vlQZqKRTYk$e&rj|$~wk@$%5Whm>3rB zWN%<&R~TcY@kD;F@iU#YIsz*~sxkcewMmW!cb~Ysn3>pFJTiWHNB!bcH+zdnwBf9* zs_qfC5cueGW!mnzjiNxM*{W@iNNV>D@}6o^*6bDQh_-VI+x!osj5tl_>7d z2E==EVtjmjLPAnfa&k&4Ieid|N6ScL-%<%7a`yq_$s+;tNWd{D<4+OxR3-(v6?j`D zIvC%-cu`Gh>)!L{c4%m;-SG^DejSVRpAnkbk?8EKr+MXsj;E2@0eSf?du1+PxA*c7 ziHL%DY)lQyDT;K_ySRIgmBlm7T|4$~Tc>#Oy0x2^UvL-%S7%00MRBN;-rik267ZdS z_wQ>ye0)#+j;<+iK3&}*(Yv}^ii#7=^_{Hs(Hg`A!wdijwhm6D!9bV9=X@9KbOlD0$6OlWYuoi&2S{^2^4 zZBs*Ajb1hl4$wP;GDK8lDAm^LSvRz%1U*|mee2@;!>|FU_X?~iD1M@znnpoDhL)Ud zxcagM-%nYv>CBB<9tn8$AwwHy5Ac^4t0(N(zT1eGlN?ad~g{CcL@%ITZjS)2?-Dgng9t-AjI9>-QC@jj=OZU_x?VAcFhp!RaLvwd#|chOP;4v5Nhog9v=S6GAtxAK3h%bBHYH(CnzE|r?9jZg~C|K zSVKx&?=SB}&7DorMusk4m9-7cC^M$`Etj|i)a~AZ-$%RNwU@Zuw{b?m5j0si1hF3I zGyp&Nz{oGdo$p$LY;9~nbBl>tf)Y7Okcml-^5IXvBkeQO&A|q@91}A$n5Z4#DFrGK zu!;Ztb7-hG*3p{mTTIS%fUmKqk5YPuhd=!C^T*D32TR)juz=I)cFiYgKep8|`4acp+?Q4gH($o&u_-8p4 z6!T2Licy{>M%JDIK|z83K7LVYrIE>Q0XB{{S~5@F+}uy9JJ>sWM`aZY7`9_hT5?Ha zQ*=9QkIuLux;f=1^ZH}M5aQ2umO^|IoVDB&2uf43#ZN;Tfc01Ypnoi-u^*ZEW}=vms`mK@iiU40p}7_yREe)xhWBWKHdqy zKq%vxfP-oqT3R~5WBlp;$bhJ=0U$)-!5}gQ5wWwQn~#@gJ>p62zx@s#;-Q|7w)(=f zxG+%dG9+9(CwF%@@D#VUzx(YIKHR=eVNFqbEO?APU0q#(R76g)I>dD$80-MT1qVAr1QTefW3wo5Lv0t+0;>&mhV3ZvYt4Rtg?G`x2Q z$+zy>Z&!w353Q+n#d(6#I3Md5_tegx`Dxd-t&oF>_bzU83T{YkZ9$QsGR4DK@0OC{ z$pbsUg-a9cycClV7f14%wDiK_c&isW>hkB0VfxJ=^4+{;*IA#4@NimQs}m5AaYZZ{WB&D6uQ6YU-*Z{T%HqUutWq$R63VW8=n+JQJ|Y1vSmv+K-+*hYqk* z0PJ_!o{u%;PM$n|TK@9Y>zcRj>F7Rr_VTkByJ1mQl!v8}k)^GLq5gBUm# z5|ow{VYdXdXG&6RsGqx&osAXHNUf|nEG~d{VLah_rl%yu$3=$+`Fdh;#WG1lX^N6! z$N>$S1%%P0q{O(Wpa6fAnEBG=oV0=?tmKLy=j=sw>yRUlw*iXkduimz0 z>5`?37cbnD+CkvYJQJ{(KDxiRzpWtL)738~J}xpMGCnyyD<`j@pr}YJA7nTaooy}k zm9V;sii@Gw$gwUfBRQxDhXw#=!R6;6)((>wXqL>yM9~OhI~*JY(J$OlR09VW9q}|W z+n6y|6Wt+DrYN?+GXYZ=hb7hGvmhp3B2opLD{G856@emjpr{SvHbmpfF@b*FgYwcT zv$ng7#sk6YOJh^;Ou+HY?BrHWOvC-&ue9Y4AH~69$>ODYu4;Jb7pT0 zh>1%~NzWV{92^?8zI=7>i7hKv{VT-ZR>~SAs8fI}$3d1ouOXDX7QEC{?6pB)kkTAr?Ba+ty=)1AT^o=r- zXabs_~n%4>=Q!TMQD2&>E@Y$AD{nW&4wjd9_G$jwDIJ% zhp$X+ojtsK0JZvq7Jcnwpl5mX=QI8$IHvQsVOSn;UB2 zm;sG4c$D+=^Vs@DD!{QzvJ!ZZg%cYG9UO`vLe2ylsSuubN<4#dH8s>CypBF3Vhu_F zji3%xsEzD1%mOg$F)`^e+wf3CKrun4|8R-_r<_a>CPx~7WBu6rz_gZKfYq;X`j0>& z%`0wVjYO4tD+0o#|7hQllB0>!D-fzDtDYLT>QNKHQV>ZDZKl7E{W#VEQ+<|`jPmKY z?`#s<|HX1PkS3vqi?GY#3S;BBD{z;l9xQDG-&hecfsEiqyd{k5#d^YeP*w_{MCq#d z9f>V%^+l->zOL@kbpZP6Mp_6eX) zbw@=(_QI8WnXm>K4k=-(^}c)maa33w@8w|j_{IhK^Kz;hHbOG+aS!n3dj{T({`z}M zUYLuOiSAWJS=sZlm!IUda=`2Kck@iZJQFY;7vd5^a16@|tvpzt@^W%=SppAN-oy@s zSa+%zL^Ul>Ac|memZU&+KgcUU?I|5?KqC(ien}A-45Xc00>tMZpt+z&L?am*6Myw1 znGvjUuaPP6MgPU!zm{`CQdFk@Y`(w9IVENb57U3TASGAOw4e1Kcn#gX{ha=j9fRD9 zG5yD4!ZQK8+r7MT;k>MzjNBEA+}zweG^`)JL%)3d{Zm_hl!v|L<7-MX=Vi~Gzx*ni zYJihdNIvl4!~6c0tPmGllSdlz=VZ>ElTm#V7y=qU5CKBo_im)8vnkEb-t^TyHQCc= z&dJED>UjcU3z_On-rv*FTo&tLsjque<=n~BXU{3BJ$7{Qq8?0+8kf5KC^zHh_irel zJ$3r@Ii)MlfZ-3OzX05KY|O%@s;p@D*SdGEE6bcdb^6?eYcEaE!PU!`_M^UDo(ULs z1=D}BDtIPfY_3>jK!G}@|2?9bH1Fq{%F4>u&5PLnPsR!CByRubnSg7I&g}ki(f1Nl zCQX*$nShU8xux^;wUMcf4Q$*t+Uj^FVA`1p!HSC8Tbq&LOCW%?HSA^Dgqah7su9rv zwghz`IgI|#joIX^${B}SagOkp4Vsz#*vna&G`trKCcBz#@VE#yF`_WCgjtm{=C7%t zQDdzO@2;^0NqdmD!W#Ok97tJpWuQ^!nSk$UsVH6X&*YhaeSQ7mi~>;f@aX7BN4Afl zk)@rTi=(NDrHzA&hqteve*hgiSz*Ee_7FiuMPX`WppTCaIEKl*359`!2B>Jlt!@`K zAjLX6B{2>xpV3iKQTR#+JWPRySA-|O5lJ?n@m zMrq5;?a+!!K?Ejh2%&)Mk(&cuC4T~WB|umR#w5x}K`}8kA`8WP!)8k6HcNGeh7o>1 z17U!&7zZc+(gSY;2MuCY;^bq>4?V^#WRv54(hOsA@d+HtE$%??gb2N8d~MCw`j3wh zhkUBNrwN!G>%*A-_w_ZGmS(02+PYB@O)1jsPaTu@^yQhTpE|tjklG8csxBcX$6rby zkZ8T#Vb8AWT$5K&JgazG&Z=BUZ-CgqIC)Q3pp~1M+l$A~)b{L?Ub*^GhVZkTZf$R> zWp1pSgR%ar>)U@^zF^sE+0tfwK-_pNTh0EyB?+;1malGH*}rPhy!ne)nbEBVof2+d zcXw}zTS!%ko1xJo4Y{2w=g*lXwctV-GQ4r4>E?F#B)g~g6?&Vz)s)%${oxLNY`DM>E z&jgIMg7$wd1X@c4XO$IW`cLYOjhU?@tV2y5lZ*AA)E_+v!i4l6>mW)?h-d)&H>bd) z|6N^N2Y5f>hhTnGyf&ubw2t<4!9fa$EG%<1sDY>`|03@1Yl8l#l(cqsw2+I?09|WtFYLG_;I-AO}vy!4i{Cz#V+~2-5wzPBh@bUG5yaOg$PhWd& zaduilbZAJBzo*$-Q)@d%7k5u@A3$=^1-g1fE#(CnNwKjJJQFa_1dO8xEn+wW@l3#= z*{$pCX{#uV^ma8ecy#ZoGS38TXk=_^ZfR|6?^utcETa;}X|JNJq#z?cA_zrF9`0x! zo?hN{bks!$GD&eTFDc5+NQ{Y$2nz{82~JQ@5Fs}JZI8~gwXE)}q%b#wTnJH7kto3k zg8e(T&dKJQFZmA4*HwS{M;I z4n7^i(gc6+poF3ho(Xu;lyM7QnOWL8y41G_H0SQnF}|~N)vUQZ6EM#NOnw0J1mal9 zGXcYo(LXr!%fJ5k<=s$!U$>~ep|+wZH#t1m*E=YwxVizbZG(d!|Lb3Wei|90E*e&k`}0GcRKhT$0F%*h~tSq>uS0VCfU??#}k6hDOis-B44xbU{f;QSqYY zBYkrs`faMriwpC2b+9%vf30`>#`P;ID$18sRIWdGVQh(;%QFEp&7|lXi_YOJ0uvs( zO&%zwZlC4!MAKP?P6Rv?Fp{BpCg9@W>}X4#3E1X=n!L=})2C0JIDSIr(hZ%*FAR(= zkS9RaVNHOvk*SH z`&p13Ouw`%f87C|0tyR=gY1xD10U-_^+gor8geg)(>FXa`r+rH&f3C`%Dlp|oYc6e~> z8(Xlj0kjpag09wF8^b&Ic_v_d*0g5xOu&@9gT@&MoR1$!UOW>p_Vpg%H;5apq^QnC z8D?u+#lD%(s76K9+b`bUIoAR#F-n6L*2}KEYxAh=KtX#fy z-n?b&cPrg{s!vs%HPYuk# zW56>3l_At9%{>tb~kyd^XQ3ZYC3}eDJ;xK_Dpa8;AmUq z&+q%|v;6E09zS^S&@v_|JqyRryu3U%o@W9ElmOv1@JzsDy>dYSiUf#@gg>tzWIwYW z?ASqv53*IiZd}BHjw<*cM(x)!?q_ZWI0+nJ35 zK-Pg>8&hy{I-!%VzR%vkglH$2i4INz#`H-s0%)PIr>(~4vWyXEfy8n`OTl}BJLXw!IJQJ|`xgX|8Nlu?OLsD|jYYW2I87uN94{uwTjP$Dcvm|Crn;{{&`LThO z6LE2H7gg+mOzu-#6W!90OA7>04&FB4`_V3bGS7&+}&+aGOL!&oh2nX^WeQ_#T_ z7y0@4LOR^rR~DDr(?96Zh5J0rq7QazyJKMuF9tEJGQP> zeEQe?<<&&$@0f81b&h6fJ{P2$l0|V{!FB(M0#^b{;^)b`EZ)EjE zwg{qX!RMjSY9O(RvV0Ou1c+DUOe<()?_F@&X)9RFzI2J8(hG4Cw;e<{@EG z*gC7@J&m%`y{yfmeH=`$s~wQur>1)CwS~Err+;X8S9e=!n2S+ptf%d@w_dhdipO^D z*rR^qnzFW;y_;_^JjhSJNV z;S^uT&%@C=3<#S`0pyYlNZQpz(XzpgQBOzI-dXngo2Gm-ap~#><0g*(_SRo;o{&6|O2M-@Sc%=9Eg}$LN&jgG_Ll^_Jw^JH0dG2^7;NFgM zVM|@6uZNj;1Yl$mjCdwsEc$pvI8Q6j1kCve$@s3TDKBHW_-#yyxa4|ltJEMysX%2p z`<1bb!b&ICAd5~j4mM4On4H?xUv3bV z=3{55Z)O*ioS7Z#6P_IS!r=M!varN-_H$TBM0h5cIHV8V5at#sE`s&(P9VL1EPA^jj0LZaMy}CLZg~GxoigH9# z&4y7APEIeNgrv2;%3e-}rN526&b~uWC{3!prLHta_Apoa@}&bi1h?T;o(Y)6Fv#lW z40QT|Q3zC&V>x~cxLkVOM>a>2tyeAlRtTD(CCX_BFC!-%IT*6vGNm)aO2xU@Z za)_&8w1dgLBP$39T5^|E?%Jm}cT%1*n;vi?^i=Rnz<$YjDBRA;OihgOHGTdvm7bxMb68w*5$HPUrqQMW zY@{UP+e#;nY?(JrQvP{i8K?-GT7}I2Ll92bc52fs?q1Z|w|wT5b$5~5j@<|Pge{tq zo6rRqOv!I<$*OMPnSgmFV4ew>@^ot7{qv7sfB85%*o9J&B-HSFdAPYJ5@B zVsN@b+|c~tUw{AO(?<{$3u^>9apB<7^#T=dU=BzfQ8Uuq`pZB6_!C^bgWXMarCHHo zLB8G|u1=1gK%z{jt7~j&`{VC_{PD~Cq5h7>nxeGma8UKSJ3BkMMaM=*)naYv`1{{M z<@YIt$gSO7bs-oPiw1hApH)jWXJ6l^D z8)BlyErgj)raEl;^3wc_#OP3eZx1(DRB~4^BI~+F*zMSK2}HCcKPMf}SdhP;kGGd+ zxtJ*%8vwvY>T1h*Cg7vWW;so)0t!WtERPPiKoDSI{QAiqRmF414oL5kb*rlbAPNvu znEHZ?H_FGw)KKr9s^Xaw@FDL!__m5LTS|+WyskPW%+JHz=$Y0fQ1u?%xqaJ?eQyYj zwWOHM&ocqrTfBaJM_o(_7Gv}Mcoy+1uMH@5;SMNMs*qrHvMqr2DC&K};q zWy8948#Zm;x@+&*$NGjQL_u9s=4)qW_*nbqm9vL-ZrOzK8#iy?y%}_UM>KjU3o>J^CK zsSA}Kq5_#Z2+9cVla2z|$G9Ho^Hqm(aFQ^9A}oT9#8cAH)Cvm}SjJqLV;RX=YiO+q z&{jNuPWfq|05}Td`~aZQXL(bR!J$PGlP67_IAO*XoixzoRTIir8CXwfcyr?EMGNLh zO_?xp(!}xOC-F?cJQFa_1RUV+>+>xTOhA-IS?a8TW{ar9aG0lfI}Y{0KLHCW<5MF} zHL#`;EtpJ632sSZXVHC4t`q2*!ZZ}RJ-S?C1!`%0uMj@y9IXSxX zOu&lAr8lfv{loHQ%a<%(vSi^pv+%^M{KAq_c1J#x>1fDq-MW7D%4N%zEnU2D$xdC5 zkofeRyn;d|A9+{uR(10>>9s3XEMK~8^?r2&N8j-H)Xd!6TuweT)E#>D#P)5QHtsm8 z`qa$MEg&K`DJ?5EFOSKGhj=DnXdN^H`IJyBmi-1t9U=w&gfYY#D>bEOo)m+nzmc3{ zF=R^5!uVW3pVLnAs4{gYCQ@-D#ic&fLo6Q?5nUopBA-#LQCqq45OP0C-zJ4 zTeWD-YGUf0J?lrF2{<4$GCCGa!g#)EL+Tgi1-m(UN5a412Zqpy=(xn>wDb&m^f{VN zEY=-uEp_EMf@fuBW#{B#^~ld3d;XbLp!~BL1f4h;mI7e|t+a$=@nnxR)P`l9fa8V` zI|*b*AQ)rf#CFB>3O&%s)5`i(j420`lSYgcj}Q$kx_m9C+#0A11@b66h5`2cWqy*2 zXQ03yRT_*jeUe&2<5(1hX95=W^t0R1)d|~$c6(~0@87>CG(K*Hf*d~WcG5rA-hJ$@ ztS+tXd-o2;2Yr+z?-Yu~BOhvQFYVpAVfzuS^q~<-L+T;6(e_qO{%$Zz?ZA<3%T_F& zH*?ALSkVCFJs^e=wzApZ@l3!9$JVS~zUce8b3qd>HG7rfV?$e4?|_gnhLATjQou6- zv!{;q8|w!P*ArU?&jbu573cqw#_~+SD)KVt7%O(XV1u-KYL!yI2+bAHb9bh<1p1! z6lA0SMEi>T>C;MrIb1cXu>Y<@=dF(b2jrdy<$w<Q`1N; z64qB1#5up#zNxNw_UN%wXU-~IGYknwj}*o<+S^iIoDpjG>bAxu`O`lgJ$dqs(seVR zz|hFpgd`lIdV1SSbCZ1?UTdl-oIic^$njHWFWjo|99%|LhG?m_c=faue8My)BtBK`w6}-M({G z?zD`Y$}PPYhUP#qCwV8ay7yLwd)dBuboZXRg1n;Y-6yZ!nprU>5lVl+1U;gPP)7?B zqh~kOuikwHmH-P7jX1k`cvA8ZrLT8)36W7(o*Nqy;P2<_?d^l+>mLwE_BwSz!Fg+Q zJ)j$kvQpyXVq;@sVj{vLBcoW!3=)nyaQJ8fKrn#*3n;Ril9HMXAi+ecsNo9cIsGTc zIL`!32O)f>tzJ5F`jjaWWMON8BoN2kY;3Rn#f7PjJQMKtwTtFT z&6JoXv0&*2o(Z@DDC0yYG7ickuF|F-cqZV6j3`Ha1Gn;a=I5!c?g6eRV0wQ3{hxpS z`t#^OC+uv~$4{SI)qr&sCU_NbQ1$hX{^KA2{QcMWqrFX~5q57LKh)L^X`}O7bp`g8 zzJa0NAP1QLNKa#yoAFbD*OQ(aEL^ zgp#5j)HIL0`}1G_{_jukMh1liv7VMs@84Fwpc!2Zq}aS%U;qI~^Y8!l-~Z1)K92O% z7sdu!KI55ypP5?OIlFuN`ugMe!!rRhuOy}J@=U-y6Y#t_QZuFIt$XVq5F8c}3F8!L z@=@-YVF8vGPMzGlZ1yZkNy%A@HokFn^YHTZhXIU&&&W6W_qDHY-?(e@0!gXq(Zol;GMsySY;~Jsj`0cdjcQ*tUA{ z%xMxZQKS~Ey72rpI=Hx_o*dYi)f(e_S{hKqXQOR&TxZ;K^(7dD}ZuW*yPjwuH#;*tKcRnlE}6mOK;iqWN=XFFKjnjMSmeOu{|AanBu1 zZmFCl|zXP4&*5+P{9qTq%j^QuCJ>G=b6`XEo-( z>`6Cq&GWo-{P2#oOJ_>Xm_B31T=`Ov!4wzfLyj|QsAXKM#{;?JyH+lkBQ;|xkaVP@ z3UjivGBXHzp{vu^zqr%k!G)7s7Xc+_`ZUQIlIxt)k`ll)k7}8Y&UT-uqTbRcGW*v- zK_sV7mz0>b$}lV>EIc9tNNLbOU*G)3%%>-Iu30#XX9B)PBsDdqp%GlR zs2?C32ahfCY?-_i>1nBqg%mv~pMVSiI+XHEz?6kd+c(byjHicZ0&YZWsH-f@ zOo;Gzvo$w1+Mwwt>fWN=LpPyen{_CH=ei#t7H`Z2_78Yej2fDjDqiE3D+SbmN&sVv=vN(0E46&tw3172EO zR8+{eT~0Bfm17;qkHCr)cqU-f8-mPxU>E=#g2LkZZYq@`JB8@Jc_v`rUY-dU5uAoZ zo(UK_fnYD~>|!Eco(Y&|0=9M^c=x~l)z{oDsukpA7T1?F2s?X62YQ+X1=;=Mao`5S22B_= zh#-Lp5e5RJuo3`s&4r+FawHcNdEk&>!Au1Df@m?FswiG!xcqU+<7q^tIzp@g&|D_YKWzX;F+54yD6qi*ug3!LE zI>puK&UqQtC#IsF_kAh8aY>#wMlY@Xto>t>vMch_f-Q^;uAfs>*E0t9N^iEgQ)+r# zh>we#Z)|8(grB>S>6=Hll$BL(J^}4oe{+3aT6R{EmvfM%gSD@##cOAM1I?=%>esH_ ze{BIntf#grB0nn7#6HN$(A>)A?yU!pbgo>|xN-gND>EBp;P>`*H5G(7ybiT}VQBmI ziI#@;eFZtqTUt8$rdGCGioU41I3Xt5`=y z9gstwHFCK#h+Mc4(19$7QtWLo3^>ajwmXfdDu?WM~LD31D0CF_3?DIG1 zrU-D`@Hf%~|5HwyOG^TQ{1DoIOY_)3rr(^RL%EUe3kV=ONsKK|%!$Z-fg1n~3kLmT za&8e5dm>o~kaeI=O#XTOKo>R#%|H|IOu)2!5DW28iRZ&phgL6}DJ8ioxl1Snk|Dz< z#LdTDN1c$hQ-u1~AAguBAu(I47AX+eBM_=Br|}REQz`GxQz z=jIm`bMk(Xg}Is5?jLA8nMBm^yjN)ag=c z{()gpF>&z;NhzW(x_*Cyhy8Wg6_Qh@PM$nv`i#R49_SDq6B`%L=0}&VSXpVvr%awa zdFu4-=8j$j!V?n{3ppNOJl}0R6R`Nvr(K$70;UBB`9YK-|8>LSgEfNLIwbzg@u1IfES&(EYh6Y#9rlBhqBn5AIr zMD_ilbe&13!E@Aj@dIN{z^M*YimJg>| zrnN-b*y!I;R<-gsy?Izp_Rukqn><)}(UIX{A;GB6V&+p^0#U3WG60*Qt`eDj z*~tB;;s5}EAb%h^C6#P%qzJSN!MsCRtEI&SV28-c%FN8nLd)iCbMkuQNJ5?iqz@F6 zZy}Fs%mabrKUdv{&;w!*;_QLKLX?q{#m)?Vo(Z_VIXx@a<&xa47lS=0iG&G(0~$Vj zk*KY$DlFgnRcN&7y)&|V4(WRZG_}G_L&=r6z1^h=e%{8W`nGmO=@!?X$!#~(y`NA~ zP4M$o)qs#`N;H0=eAV6C%G}ve+sgE|+L@Q$)^7vy@(PMdO1nkvg|Sx3m$mJ}UCdrw zRd}s;`|?@8M62g|F-b`P!WDHjW{0`hzB0%Qv3sg;^xz4(bNe@4_qBNRBs?aTX9DJ# zfY}1T^q(AuNHa$L5Xytn0pgaPmX^jS1Uda@sqHny6_uZhB_unWmJvFfQuvV$a^!#D zeuiHX#xr>cnCVO};vX}?>A=S`0oMva*-~5I&>-sWZ8bi!&+65qb(7|pJ^FEuq{PCb zKTfkQLprwrBrV;dz8INhQtQvEE}yD6&&FF3MJvDp6ni-OY2P7v>oH_SzXj9kjnK^lz zcSsnh3wye()=c=;WaqLgGrs%gn?guUDGws{IE!#iq zyK#UYn6qrY#H5K5^CZTvy#l$BC3e?5%ZpRwwdVhA{POwBckSQ3;m0kTzn?m3-*xTh zZ%u8m{fe6J%sQkr`@X96BZ8~yXop`qGXM{9aGm{SLKGPa@~o(Y(R^O$>{X9BLSMqwUo zY++qXZB|rZh=axLN7j**bdI4yzv`;0b}T7F{Vm01Wf_qn&Ytd%)Rpg;hUAqNqY|nd z98g$&27Y}nsw~ckj!TOObuxZy@l@}rejowC=NA^2VmQwP+(bm66xl)C2D#=<4IPEi zC1K`rTUB3^Cl+!oM`!56Ti?r2v!zh=l@o zh#I~@h6daKaisvtVA)Ho11F@hkg_BJ$|B&*`raPG+D0}43vMwpCsxFQM{#jYVVGndk>vaQoF7qqo{m()5bLmXC5+l^au#+ za$30U{`q}-4<0>n>dbj1)f*ZY4#^zbw`sw=nMX}+99-`%JsxPGqy56l2~}%$R>se6 zXen!)KDBT6y6^$!=M)=4>>JLmW=|b$nOQSt;^Cy1zcid& zp$sieRfft6KTMOru~z)_j&&ihEBNpm3U43TK4~(YYQOG4+zn_g&GmJO`_@dF%TC5P z^Rg;fS|dr!p8vWU&%-@W;`io{qNq!nC+B5DmMzf=JlT$=%%zJjJc; z?|%FA;oV?gr?93dJr+F1o+#3Faj~;?aB``GytDTcXp#r}Ktx=S5*6y_?dj%<9#)oC zHg+WM5Dk6&FxuZEY^cmlj0o}bLXoeVtD}jjxrKFIQ%g&ms0UPR{avk~BS?w}^7Zoc z@N{=GH!wCev#f6bY!lqDLzup$wmdH>0&maD%fr)7|Fw~csRf+4&B6|J=yix>FhC{33&6CEnBwkycClV7l#{GTa%VvSR8NlLPuTx{IR_|wr}3N zdCTT4yUzMVgohK6baes(Qm!`0u2(#HKzb`lH*eXx>$o`-p|Yx`t~%1s(a!Rvww8+Q zkv%&$Zrr#D@@>+`^zG~%FublN%7teFE)`^ZKGu*sdGh#a`O8u2qxH+!73E=RWMpY;VW|Hc?d7Z224ETHI+PTF7aqtRDM_)Re(p|oHddCFmR8oK)B!z; zF&?qMtn`$m__*lsAYV@`u2?2%Zt74{OmV+lIR612mXsJ56-0alzP>a$Cod{y7sz5H z11ZU|;i197L4nX(*!fg542=c!52xdd$CrqF1tdqT3tEg$$V@}+5x_($L9mQCBF_X| zLZ3@*ldbA?Iay@Q&zLk}+;{kI-1v#J-qr$btpp2SZC%MD-3M3B9#}DF`h@Y{0Y(QM zCQjLqT2WG52zgCi)B_8r`zLmlct@|NJ&hnDzB)nu{QGxGdQ<#{*3Vxzr$Gk`)>S%DLcY|BLgQ$O_}xs zd#`7^m(Q9sZv1!OeoJB?`AlAAAK({IR$f_?uYOkf#_>H%q^6Si8`fpQgei-jIypF& zRa8|MC?4FndE1g%GbW7t7LAinoVxJVTSH?6IV#K4wr*UzV$qzbQ<QW6q0Kdu1z#3>T1u4@w$H+Z-+Pdc+uje z-zz1gAqBZqKordH+g{w^nSfDAM(H9*9w8S5!sk@V%a~kIKrloU+K6 zcxCJ(CeH5>e2hd5+Yc^s@;G;V7LR`DA|}w@(NT1v8OZC?**?g9{v+>e?Ntx%*&==7 zPR8*2L6U;v*v=nTEm<&c#m$7iVTf@bahot4;${n;33%<=6-yS* zpFe-zoLO_%Y8X5Cgha)}gUOd}$8f&Mf2x)S$B*pC* z> zGumZm_V&_&eS21~SU4LsND@*rXU<&dmz0!}l9mDaNUw?BBgJhyw=7#ceR8vT>`eI5b9i- z6CI4)|4>k{L`AbAN_u)JvzF@K8i@(CI2Z7|DXC~l$nsA?x&s{wxGaB;2^5~bTq;4z z1{HG#@-ona9SL|QVB{!t_xAVUZjfz{gq6=spm>F60_K^3@9|8)T;z-(Mkt<;juc(W z>_SFAN?NE20C_|3@56tOkV0X8E-jxdzh^8%V(dF*d{KTc)~~Ub{`#GXJH8gvTKrXK zZv5CF5^{|zc4QevVh{oJVT&-zc&Wl*Y!X}^OuzIFAnonz=|nKOt8cJh+yig#v;Oyp zcqU+;30O*U#^i~UB<3yKD5H7z(Mv-U8|3g)T4T50$=y5?Ff#L77(x(&c{~#^!MK2K zgbo$3AO}W&`R$jFL%m%*6R`dr4KziW_D7r8_gA-Jv245u(ET+*3sVH8S-3LO-*LiqQ!H;^F4W*#H_{JwDk>5%*-v2k>4il z$bES2(#c)R7tE8KI(gEhX%cf6@4Nfx#am-DbM}0vtYJdu?b++ZRaYI-^w&>{`2g!Q9zW zQnMDX+jdUlrnWAqd=2q@Lq+f|LlpPy-n@40`VAX*96Wed{pP)gy3b$fzhNwI=+P?7 ze5?A?v9ssoGKs;XjF6dhm@RQKkC2)NjkD3cwB|+JT zhXsZ^%^)T^;p8NyRg|~{u@+LsB72iaQ>5Wc%ypp-v=)k){sfHamqdg)*3PmwcqU+1 zXL}nfyNKwhm{@F3Z6Ahy{wJus20EImOAFHD0^MDl9PG@k{DXr-!@^K4HrV(6AHR!=DPBtf|4pB7%%GB{4nO&yJve)OLb00gs+pGg;yTd zFplXIa&(~OMc7ndoEaPJZm+L<^R`2I!(Zh@E?$$L72)kNh;1KG^kAW_X)eYg^iUtaX}u=rjNC+Ub>(xui^nZ zDrAF`ysf>br@dB?5#sM?_42_TwM$A$O6PTA5uu8WrS~VO>+NZ)D2()WH8OZ~@2awr zlDwRZhKFAOf?17CjZIN`eVujrNrA2w1}`7nQdLq^R8Tl`$=cD`&C{o$sk%5ayQ`%* zF2>j1#7Os!riuclS3G~!%*xK*$)%yOxhf&KT96$V?qzEBQs<_I%0;CMmy|Ewd1(y$ z!nuK{tOW&vjA&oyw=Z<>+)z_hzIgfa#jE!ozcI6Mbb-~{NN+JY+|yS7<-@xm5V&$x zT2J)b4yE8?aEE<$1mTSqKF9c21fK-p5Sg{_~P+n z-ADIt-_d^b^aZR73rp)UInM-)XCE6hw2vtm&jieX*h|`Ac3^>k{&xsV6a2k{5{f#& zbi;t!vAmQBp`Vbqv^PcG*}rS`ilwWz--~Z-#4(G(w3pyB1r2+1d;X(S>y|H=Gecs^ zlo?C3N}9&xY88u^#lVCVp)>62nBilVzLHAiyt42iG|{ z<!XTwOQTpAXuC)|E&I0aL~d6?k7v%zOMTGjCex}4@YkgYtuJRA81`uQIu0qke5?3@WtOZFwmTnp6KR<;(a$u zD+Arznm7Z<%F4+pTsC#>6BY4Hz#13$<2(~Em%YoWVP^-G${@o2S+m({KnlyW zuoFj9D0E*xCH~MlAl84RC9!SE396!2$=T<-ztz5or$&#hZman|YGXc{E!ZQJLiF(+VX=f)- z0Gx50zn{|Y*d9(@SO@Zwu?{RA02vf_tOvshgTre~USCJ!sfjgP+~LavV{)K%L@Pg8Jjfqbd#9W*yJH+(Js(g6V<>e7*&U&CE~Z21tI28e2*LuCgA^z`p+`~o7###eCS9uy!*s6A*V=CRoB$o23%E~Z3H|Mur0C_1KM%4rSmBc zoGn#($)O(Zo*wS5uI^r}#wRKU`48Oj7VxN~+X#YYQ*sX1l9L&JDtAW4o`M(^(R@<+ z4MFXr5lAZ6Xk(*P9mE<4-;*8h3ktY%H+KYQCsHmxjR1E!@JmXHk--dkPv@3FQ5Qgwfz!yz&X?g15nSgmF;5K1> zezdFc^JlMs{%2uj?@Uxaq2W}e0^60)K@kaRs;fW^Uow*Ss3jyOCK5n0>(Jg#y4BQJ zS5;n0>W}DbMh4FW+|H_dm~G8{fx*xGdEMYs#p2ZdmFO=WP!@@$tNklE4eMbAKr{tT z6o9O5|C$L9zJTtmK{1pHok_ZJ0d7+G`H)IJG%{@V)PP{;2}%W~U00is0mim64-{k0ke8)56i-+QJwQ3;k!dMHQ7~V+w$=Me>2c;T{ixsHeR;Ex^W5PtPDe zH?Od`gf(vbXr05r&qHmw(e5TsbsjzOOij3GvOTm;{bb(R!>&&;5V8KVGh(^9iHY1o(EPma!5i!l zLcQTf;b!NVfJLGv{k4miNKTtJb=r)BZ>(Issfa!bMf&s!W4-L`YzsfRWTE8rY11W6 zys~ii4g_5uE8?dPu+XvImOk1tM^b8rq@upHyMGW8l%f*w%#oDjIDNOIKHj@z{=R3H zZoUCQp^5z+`P572z3O5&p`A~IJbZkO$dR9&zb6&A7z;vP>NY+P$4gtEt!lI($ z658B3pEJ3?$UW7HoDAf6!9u5o{htm9+(SzbE^}a!CBS9N;`o67zfd4>J~YDWr=0xy zdKy9vvv5DqgUwD7ByVWoUi|WvG>a^Hy25|TNlIc^E9mgGoZdT=((H5;U=uWcW&$VY z8Z`=xv6BKOp(mVs_3N+L6j={SiWAGf)_-!I;Sy-9!++>M`QRwH#)&`g-*gMPM%Ut* zfXRxaJ)Er{-J*fOIlE2ItzSBSmZX%_w!owe5Jsk?q-SMw@}3SgO}!g?md=)(At52T zWUVWoRMK1Wbq{ zSjA|ufEu?na~};Od!f&YjBsqA(9v!zmT)LAIq57?6|fM~Qp&n?31~HKZT&2#(O4+3 zSfWo)Z}WpTCTBxwG)ZU$AbGcFB>i>&S8@`O6m~TwLhww$JQJ{mxs|7XXn0q5TWOe! zQE05E?X|aFwpxnEcJA1te&d?5wwb+~Z!qM-v|v|Lix79ci#P7wQB*v8^5`Mio9YU; z%xv9!Lx|~DSeoSG>(4U*lTrPpk>?Ql&&(1Mz!SoZ2(cm2(ZyngEn>4}%tGP#q(CSn z%x4p{{zc9yJN50Z(`;pSf_NZFKI=bz9*ooN<>cHL4haN{0Obsb20PPFCiXBnhT(tO z=2(_MSxi!Q-+i}9Pr{ysWA(BH=fcQ@3VI`T}w z2dpff>+IWeSbDSa#T(XcUck}9qT7)XR8bu2^!nhwTTgH6T-dl{$A(4n&!61VcJlTQ z!Y!#z@(zpkGQGVmGSuwance$O9$vgRD%9%H`9~h!zPPD1aUMob9Xwt=WVTjL3Z!1y;syUUc7z&!W=*V7+#a$YZD&pV}DKG`K`*CL%VkFR==hyr~X*q z#L5A3VOE&CwSAz|Lj~0<%EwP0KYrxES&d6)RPZ;38*Poq{mXeeJrtm}rA(E4s2a-MO zlyZjBIi3l4V9;9g-cvZ}XaQsP1e|moHG;&4@~`Dxq4zkRc8$;Ta#3Q@t=Zym#pR$Y5tp zeYv2jiQE^2xxjf)*wu$Vefs5nudt!1tTZM*qqqUi5Bw=q=vzhdpa1pS@W^nFu(`6P zCOOzUJgpF}@d_d+S|K@trZ1Xd5H;$u^EL$#W-J=3Oc*HfB)Yf zn@Ve%TANzh@wnBMD|I|<=D5`8|1?y;AZD&VAQbJl}cr?zB z=+oWb8CKR_mYoojlv&l@)7R11CTh;gNe(h`iHVC#N*!pqblg4M#n#f&+QB2Tux*fM z0wzy7&jgIaaYMDBn7n9h9nc(Vq=ux3*t$qdcqU+;3HYSCgT1qNR8}zE-pfW_xf+TqZM-nhM`Av5}?9dz`?YqSlrEs}|u zZ@aZ2`}Y3V^gE^5ujzgaUQm@+II&lH_qrc`JYtoaj?Fv2umt&E9ReryL&s(0 zWo4CAt|*+@zirL3g-f*pqT-WMGmzrho#k@j^wIstLGi1qdhygn#ZwzruU)o+X9C_Z zM`GrwF2)0nPZ1#C4%b*8^X7`;p@k9>>xkBd8uQ?GBPL6spXR0-pSL%ZSI?68 z9${N1X9gmfk8O}6g*@+x_PxDQ60;!ZCZH+Mlg?#?$;C4PV|}Qpt!5^J_!DGCJgU~k z3HYGstgB&*7H7e87QT=$FKBhCLmgmU%{L6ohuGH2*iWEUW8X_lON$wEApKTGu}%=S zv2XYRT_7(dYC@g~n3w=-c_v_<3AnqfwMLMW5EB^@5#niO1}~twg_R9G9^e{gn>@;U ztBP{7(h|aa+?*Zk?QCsrY$}N<5*=t=Y^Czw^3wc_#OP3eZx1(D7bmBRYI?rv8X43C zFgmKrOY(EllVZbz{QZ2qy*$Ch$n~In0vwCW1w}xINl#6P31t}MHtFv`u^P)9@I%uk@|B_iKl`|ZjAgEFerpSg1eGZs#(KAuK;^q*8)$xcCg1}K*R<~IJ_Thhp4G}y$H#h_ zSCnLq?%lP0+g9oQ2M(T6(YSe!m`DY*wN(^4KhjdYaQ5gv>0LXd_a6A^q>|dT+uC~1 z*h7vju&gxrrTQhg6Ne8RJb38HNtp|3nzun@`W!mIHbd}@@=U;Z(($xo$q>T;f=U># zfZiKx%oiqbf*Bg?P!7O9kPFinaDc-{+0aBMR9Geq!l1N_Lqn@MX- z>KbZ*qEIGg5^st>xqkJc1rk#yjGqLyUBKwvt|A<+vQqBa`O15CZCSKnCS()9?{R;k1?cDuW0x@X=!=YEm9Bzxw(yWV>L?vI_TTP0US zW|gWkA|r!7LtSkxElo`=WFca(`Dp|MPT{}s5MK``TWc!|3k!1s7$=5I)=~r~JL?`C z5fFF71$zX_!kBf~C@dZ}6yW7v*fYBqc#SU(?w*$kHQeqKk zr|6fsyPIJ4`rt@A3DC5yjlu1K18krZ7#t1m7eMo25o%k)Qg&!Qk-FJntl$6+Cwm1^ z3!`(yA2?9ekG3cjG@$i+dj69E_?>7kKnX{n0L&H)Jv6u_@G4#1Xe>amsdxqeplp2n z{OR*Y#?plWTvm`VUrPP-rM04}xTfRNC$L}z6cf;`wFj$X`JRte4%ZIs+O$LYQQC(u zJ)qP293AYvUC}oVDQ#6;zG%+OrT61IKQKDpKqIDuEU-SOa&*IH#rbpROq)J==Bct? ztd1%NVI6z#BC8vh4(-~xX4U*8g&0W4$XXFjp@2tbl; zEc-^fv#%ZB%Oe3ZiJ;`UK_6mLLli?w?P?8T~A?s zg9M~<3=$!dPLf_B8bA`>27F{hVgo{e4NnZLj`pE!Rv3DzJhWuguft-T*U^$2q!T&xV9 zUORDANk#3VnUD;8=wO=O-rMubH$l3;or#|MB{bMmQa-DZUPo14l@;`%bbS8s?T@NN zcMAg^3HZ<+9tjvtM4_QF;U7*-Lv8{MWFb&hIof}clb&l43co={I!C1>1?tU(9R-gV zU2RZ94(DbrQrwW2hr#7Q^#4MULl!b58U`c(@GF@SY;ez!DL|44QDsjs_8;jSkpvaT zHtET}1*G#yjQ&mjiGh+UXjyi#B89*s0c+hlt)g;NMfIvX)u190OP^ZL-@pCwm#83u zM*`-NfKi-Gb_b6HOeITDiro3%TA$)?|LFM6)eC1&ox0ArUP5Y$?m=i{3<+wjOAU50 zyrZ;p?SdJTCd^v;phiq|I-=S6-k6gT>i#9zSD^e=ECY)d_I^i<|Oui}M`s9^brj(KNLBAFVKB&eck)HV3vGUcOb3 zomJoEar4kR#l=$NWk7}V*6pF|F`}xl!0oPV!M7TJ`R-tDTj|7Z9 zR?*^Ca&iCmkAMFCx6dL$X=ac$j|A-I<^~Wm9~9#G`9Z-#MU#sR*G*v+Qmj*Aqr!pm z84?^EjGuJD!(|X=EqX>#3J<_SDZ_@LFhxa1G!g?1x~&QU6oDijWZ?j#Gae%@7BJ<^ z_C+`do&hXSUS15lE;|z_eeh7k)l(HA8O(HFtQQirL@5{;&MyH$3F|3EnR7eTp;8cm zi8Az;&*wOJBw$n>(Wl?q;{W{8gUdV;@Zy;~5-^VhO!|Rc64IDvVz+Q~zJ`V&A1Dif zB6Ei>6W{67W(j$URGdT%WfhE0x!)LU1@fRGl@Xo>=s}d0P^;3u-X84Q-`vbG;OwQ; zAo8PPA`?&&QVLWw!$AtD6sWl})IboukK87y$;(TM&8=%{6u>hEdO2F9_R+Ua$5lv5Zxwl8S310X&8p>dXUy5Am5nqbN{H;Ew>G;zym#>uj|4nf zcH~GN30PZGn{bd#%`Ge`p&m$#z@V)`ifwUWZgOOxkB6(P3leN-P{utWaZ_GBb`+Ar zG7_W1LxKVW{QZ32dwWqm1FZneni}l+Qlzcrq$kD4L`8&!04O|=(K)fIrgXCs+euecg%a@5|Jl5=x2(~=XB$RkAx2elSk8d)DmUf?h`!2t^*4gyJY zF|a(BjE5DF9LFO8L;FAp;*+7O2p$Qzx2L71IzKxxI>41j0(NnBc6M>4ivaD8dKl>l z2SFAq%L+1+iIXkxcR(9*SE+FpmU`Gy~M9V$d%f zOfLbDaaxLye2EGT%2+7oMLLW}0_Kr`c_iRa=nu-{#JSCN+$ce4FAoX7IXK+8WU02q4FG9W=VjpdkB^Os1`QeYunizqRO>?s1V|f3dINJp#IXuyDpDpR16Bgq zm;|C6h|b4+iXDQ;evlzS^|zF|#3eWs8)y8|`aI-TK?19%~N4f+EC*1#hHm+OvRr)h6_ ze4(7eh#|vJL4gv8p~DAGdTn5AZf#d7$iF&n^#k48Yv#(28##0!hyw==88K?`!e?)E z4UEkys|4Yy8CrBo@*PLRnqPilswqFc;c+FvLi;19yMmdl;ow2KvfAm;gekTBUD7w$OfRnobu1`1t$d4fP5=a*zhbW~b(9NGU5T(a|1yYHtsToRKZFnSLF}5C$1Pm$d6I-Srkow0X0rN<}_um+_N-FCc%CjPayd7;V z^|f`L-@SJ3%qid$tE!&7`uL3@09=GM#p&Vxp7vIzI)-nb+`4w<{FyVSPn|h)<>5UVG6xOVCMg$tK%K6Ey~z3`+NGs430Q zN==N32oCV`_3`%d@p$Erj;S8pv3cc6#YJ;w&zv=D*8Bwv&qgLl3aowIU*Eoc`M8p@ z(ym>bRw&L}G=2I^!2HgdGk3w;FiDbglJDC`JQDEk?c26(-MDGX`n7AI8u`Nx0% z-dtau7#g0QQ&v@5S1*!upqjd+EIr7~$dpF{reYBu37D?T>|W35hwtJG{eZ#Ae;>pD zHS*6R0rN<}*pxWq#p1@woS65H7Fsve&RxE%@l4~TwjqxM3@^R(c*jRX^|iRA|9JX- zKmQ@*V8H3ZFueX<{-x(X6eaFSIG+Dc@((rf(Gbu{b;OXuSlNK-)47(5Uf?eH` zJc66wDsB!_UI+T{VZ(-v7`4jK+QrK+C2IH}keOe0KZl(|gxW9disc*M6Onk&&5`D{d-F2ynD~ z`7X}W?76D);hjncH?F?t#3KRoNWjS$DO_$IXH+1dhW0)M1kiy7X53&seqge5)qS)n z%c#g0X~0=%be54$o_#XZVUSB7**}>1P4dsoabiHfF4j?iez1SCSRs!D48LYxq=&1n z{u^_P>;$9B&sA1wtKW+(Dy8=GC8d!6nkZe3)0Z4wO$=?V@0;k~I(P6DaBjWQ0W+AJ z*CJ}j3Ntx<_P#}cox#gX$B^T4_K-)E$%`kU(a~{9X`<%p6n{JO*P7|R7SE3D+PYWe z@Mgs;?naL_0z$*WB4R}?#nCx07AdqB%)S2j) z)DUHB`}V>41J9gwuIx}%U9(kL?V6>Fr*B{|j>qaa|J3XtySFDd^GLuv5-?dzaP&iA zu=5{xH?H+2{DPc&ka-9j8(TQ}$8*F%Lrh@=#)kgC_32!&kS>Qb$P6RrAnXk|34!f( zuAcS5qcv%2pBI5eA(~sdLLRV2*epeuLJV*z;%$kWYsi`3(HOHMs+G-$rx61b^74yC z_DAnE@kqd<#ta`YTi@Qz`@Nq?lCy2l)$C)_ru_W3LGuppoji2tc-hek6Nkw?GqrRg zB=2_b6|?WY*`_dXu)<=sMWcrN{PTbzLq`u=qM;AyQa6#fF?gfA1b!~U~6uyud8QhY-VL^@95%Abd<)U`lTX2EgoG%!UErWxS{~j-OJm@ z*DruwZWLVh9D7Xn5IKmgQ)1nNILJe}2{k|HK0i=6eg*7k2b&Akn|cK6I|aSl);8_|)H|tY@@2xGkHI4W zBL>YQ0hg6F0A3rLqrR@JAkN*_%`+-r&;TC=6$7Fkkw*gNk$|y3*ew=@BXglsTP*xv zEfi*HsH#x;J-ejRLCVqTiDq<@sVuQnIYJ1&#PfaN`idu-8w3@3p~~B-SdDo42D=RN zNWcOZ9IV`ytZvRgr;nHUP^m9T9n1{~Xn7gvtc5x$c$oKOiI9&`D!@c1qaR(kgys3U z1yzkASUY`mOfSXB(%dX=O!hH;eNE-mnLCyVwM|m*B$Iz?oz5cxd&H!pa62_AE-KVr z|HaEQDry@Pr%o7uLEmt<+EZg2|ICt#Dhz<16|}w=IojUV`b(((KfO550A;N zq)s*P`=XT$dLu>a-a5W_=koC*#~gi;Re*BonmQr#|4<7j0MM7m8{Ij1W7ABz;fwAd zn~GXoKu1Hq$c==4HdU1|?`|rq@kqei*Sy01=LYq(X9J#L8a0)rMHy+?-1U@La1X$M zXN@Z6QSHt->qu>}yDd1Fc;Q~nlI+pYvm8bJM5jUP;DKFwsob3s=m0{;BLM@ul)5LC zmsg7bzSUe;fj&)P356ACmPD-*(~622P->YFDJI<=qPp_@)R+KIpJeib0yH&^kXWIJ z8h`!v^*o($Ew$yjDKY+lX>|?*JvTF* z11jW^fO#Zf9toHPiQ%UasxbQeK*9(qm|zER%0Qt>2iJ~-V~|cEBv71SXwXs3bs=P3 zu(3ejobt-*(j*UKowu4+VTCnS)X)S_oZsn{MPXL2A6+|l{Lt=QrwvkTD2EI5JWf8# zEAzdKbl+;+R#QE^d&|1D$_^D3Xo!N2DICsdMM1Eeoxb*yyK1Tj_ikIeX3bWu5-w80 z=oO{0{vJ+-I?r#M0#xtTHLF&x-t>;TvF7Hm^(%QKV57HBZ(lg4ymj4*rHdCYRb0M& z)y5qfhK45Su2EJVZ*6I&^Z3r?bBDICU%q6~q9uyUR;=B4=;<46J%XSvD*{5SOR)ZmbsP3vz4K7xMG*&H z?V@<;);OO0(x0R@Ixb;dwbU}95Zb2;DG~v z9ynmYz`-Nr-sY#JCKK>_g~Kxk*QcuMX37GIc+k%SX#Sv~Bb5?kqasTRi%QE(4P5*+ z4=rLqwSQ=EP+?(FS;75>mM+iN&y*iJaL@oa_=#uG;9+wuy*#{- zhESGq;n3-8dp1my9YN%u7|Y2s6v9c~GDk;rW-MVzy%4zbW2M>h%|7VUqWW?l~ zTH3nEVkj;+w}M9k=8=F2kdI;Z5vm`Y26XEoq+NQJM*`-NfLE+oJb(6#88fC&ojh%g zx|452LTY+u7KicNo2#X^Y~{Lzvu2@x!Tik^G_BnOB9H))md4RLJ6rrN?OnA}ap~$^ zYR?QT9J~URx z3vly!B;cca_bp#FXWEp>W5>#kQ!4Bh5lSJl2uV5iw3u8xy>r9*#S5p*odP6YIoai& zEod=DO;Jgu_9p6FKel)Cx=nMZESOJ7y$bU4B5Gl0;bKKdzHP3r?;qX1Ys0GL)25D- zla-YpH%?YLA4VKHM)OF(YzMOK*wR=ps3^=%OHNKoMimgWM@9yR;wb@MF3M9_UMJem z7Zw0xiAG*7hvG>*LqoK5@JPU2-7Ur!PHtbfX2IM^b0*5l$&Ha&>efWtqr9|~!sWe5de>BTZ&-2;44@D808|wss9AbgE+5z+g1`FUJSchmGo$weqp_-8O+egM+ZZ_rHNf{_BxQ|XN zQ!*a+$XLig=tuhf9_T=#3HNe%g}(Xp1~9mF+1teLSc^vjrVoWj0xn7nbu)c_<TUE?Ylcy9{4o0 z(tKEAQY27OTW@cdxF#nd*#6zkbC<4I)}gN>o6nFyMUu~7KX!>K5IeMgeN9#M_(exb zJHh&O9R0($Z@>057RH76SZQ25p>jg)X1s_DJp_BHF_+|*Uw`|hy*4K*z}@7@`4cL~ z&YUx~WH%4yuaUb-sMIQj@m$s~!gn0K?`xIwPQW!k~`Tt{NWM?0= zadLI{@bW?GucR8$V}=dE^r0h0%S~Hx@e#WG8<^T4ai*!I(d2^4X2scZBZiF_GGyq; zG2>=$xpq(emA0OVd2enmNihj|Ss zN4mc=0D1(o9=@PcZXykG1aLMvtYTyUQ#udL$7P=hAYJ=823Xw!n7)i&UPgWc5>Ohr z^CPSPvz;x#(h88{*hiK~YJmhApI_es*E!kFSih{FE|#S0UD&^U?Y46-T}qmT9KF1( zghv9V)YFKNB0;vpozqGic_iSgS9l~~Fs9fD4cX!Q__!e7OPf zheZ$JqwIN3`WcchQBjpdVnsGNWS4}OgcE~JB_ZXJfOC?<-aA^pQNMo6s<4VfLe0yi zkB)H21vOLH>b3!NDNsTD*Se?)Uh>wAo{ajRMjy=tGAOUT9(OC`JP z#etTSl^PrD?doV}ZDna`Wo2z^XJ5&ngb9rpnS=Sc*;y%Zks6o2RQqDtv8|Q50I|v+_6rapC?toMWnsUjHlz(x^G_0Q z$|DAyB6(6C9tjvIHpx5^@bg<|RCn(=ylczmH7i#vnllHrelupyTy*l@bCD#&+5GkW zi>HqsJh*$)h7Bv1DK4BpclPYrbLK7Fb@AQ{k*F({Mav3v#k2rON`?!ft5>M!15BZ`}YpQ;_= zk$_>gQ)Uc2an;no9x-^Ri6Kn?Ff9Tt!wh>0;ZvI-&fiY~d?xD9Bpd_i6c%8*ilqQh zbwJ-efo=xywfE7#BmMq6!}kTG2?Cr>!Ar2a$M<*8hxA9^GpFmgSD*#ZhrjAa`gaB? z5@2IWMj2@$>;d*ux`K5jWG~~G$~j_;_je#c5cAgA;sDM0@C^i@O8wzLYoFrZG{LWXiQ^r zOK?>ft*z}Qk9|Eo>XRah>su(PvG0?joRzlD?w*=hb4L#z30N#)CFY&@)@ss=3bNuO z{q1dBd>u^T!EpET!Kc;MMWeN)p{A@b^}U^)vv+`}y`8hWmv3N52z^}y6xN9<;l}Fn zyp-gm_{b2H4-rgKOiV0&pF9#UiJsJkfDZW&`Nzh@cK*)re~tX}NWeT2Fm^37OnD?= z>Mls4E4@OLvPNAz5-^?b?QPug&KU?O1xH-GuD)oK+;cBF-&@=I&TnRdG|^u7ZdH$0L!yzOzWNkX~R1+!mI6(U5V=*vrIA2(j0F@%*)xuD+3pr7hwgK7IjIrHaF?3CA81QEDoRa?%oGA|u#{jEah4-w81^ zG>~l7!2d@WZq#loDKRlIi8`oqqmgzE>{i&-tifC^q4i{;_a~_+mK_TLr5>VOcK}4u z$=A;cWy$4^!JxS~%Uc?U&(Fh-qE}~@JN^dNgH#%cXF+CWtioCa2mS|I05i=iSyZ5` zq_A)(y*V;lISGI@M7A!S=j^;^MY!|Su{kwB#kn9s!E`&9PcNKzRGem3G2`jG@7WD-eROEREbL1HF&OA924n-6MR zM@MIOm%%fLB!rDj^cFF2&xuYJb$drkM`M|z+Ic&g%hAy_hUhQ=+uJ*OGQ*>SOy5Ta zrAbYERG%P30y=E|oks%Zk$@$%3EEk!5a8fYc`D_s z)l}0~VLO}d8RTfA-HUc5jE?BHl-BeOsG3tw%zB5!vq72$z0&c3{~4WAV$xdx3js0)Vq$deRp|w!3(*QpWBpJ0 zC&j|tk^cwzXT61}!j?M*+52Wc*k8irzwa*cAMy`JWm5_Y0P!x^3p6zUDF37&+9Z~m zdL;iGofIN1O$W5NRVc16b31!PM=a^1lT)^z+zGg>6sH^ecNjK_*&o0)a{-;rmb4Wb z>glOWmYXo=t(ab4h|>Vdnp?fKwKLcG(f)1oXUNHpnH$qA6xJfskwUwqeCVrhH?;}8 zuwvdEIT;y+8|6rGq&CF`g@rU9WF85as{QgFFCRNbcJvt4H>QrBK0dzw!9e;XWsQMD z5Ia8Z>BeajHa$0Xz}??3C_E;CwmXr*Kn^frZMMH#Kmb9cMY2v|?D8rV8thm)A*;(N zh6kLy?ChLeI{divk4{-SXX}uXiab@s(rM7yO$=;Tb32>dY_J5#zgAj`!=29eKEc4o zpk2iy0h4~h0pEXqbKepBo}f~ABw!v1m`4Jp^P%s2?t8>F0`76TC~&Dmeb3V)MG493 z6x8A;A#waLA7>>g9=gZyNWhjxwVj=?qNo)TyS#`+HMLG&f~M@$Xqy|C_Ug97ZI6pM z`Ao=t#3KQx$2sV~*tO^0i#zIP6j!ZYu~7Bd%e$}qaERhy&GI#Ka52`nw9d=h=-T1+ zoAzv}6Tkgy1xt$A(+>i2X^G}LpwtX``g*|uxzmP=s)R%SOI1_Xy<-&V(3 zKQ@W?Fn3G$ver{Oeqi^O6Xy(^t&Hy*;)KW1SsLN2lbqmUY7pXPrGMqzmUWxXsa<}{ zBLOF4q;R%5XH+27k4FNIZjrRx+* zfHDLbHg-!@rM`7#Z9;OI-6@r|FFV8}{iH;z+3foI68{X-*M1@TcMmFW*!IT7tELVH z9=TC4oks$8^$&5;zqK;R&*1XG^_%x?pSm&F&*ZVvV>A-MhhG-%r1Q+m>E+`JKbu!a z_HWy>ZS&Oxe^c!XSM3~K@cB38_-a~bhuF9jzPHvsdT{UFEhjFR11tIJ3u{{^eExMQ zffm`B-sXDYuBLBJC~sW5@%*`qFSTC0G&Hku!1S_6ce8*nH_OX!Y_-lD1d7A@3zyYY zEc`9juFji2WX$wcN3NF(t1EzzP*o*rX|2=U zxyj`9<3&Tq8a$pic8tvAUGqko79gEFA5HRGM3T@WGh`PZQkywy*1GkI$CkoHuvvM< z8m+{{6kvgiL|NAtjvF#={F=jymaHB(cg2{2gT`LGKY7GLui)T_xWrb0`+kMN3r3I8 z-ZEy$5V>)G!x*t}gWRx{uD<^MfnnlilLdo+)>|{<{OAEc{WN9%xPimREAU9bqlT}$ zaP!eiZNp}9(ZP{F|82%*`2hn*4Ie&s#srz6LuAIw3|e>|bRA>Jf4cF>;YV*w_}ieF z6K1a6yne~N<;y0H7`o}o{TEvLW;j1YwYTNB9as2CVe9S#N=H>yPn=deuxi&$bsh8+g~x;vW6Dhu;VYMAe{ z9|@F&SUSIa`TC(%SXEPy7aEb6Q-vrdtxX^qMF0HfuSmic3u}wZ%3|KT2E=C-78Ml% ziU^g)Ab0)Ozkcg(hZWBw0mF;RBLTxEX+wMT_77dP+P5hH$3i+%S2_96;Uh*kNz@`N zy?&Iuegt+5D-oX^=T9V`EYR!gOLUct7#&uM)RRb)xe!`QV_ii?dKNs{089QpNEld@ z7xpEO1k57=8}3yNcGlA|b@uY{@%D7{2#(JSigEBVvo^bNNOj;9Xx*S${Da7P+YoTvfMU9YbP)NW}C???rBBhl43WN?TIUD%kI0Xf!%57tsz^bwK?zvIP@;YM*qo6vh^}ID_Yvul zGPnqdH|y5URYQl-vHl|i<@#UDijY(>_9S*02e@K9tjvo zy41}FhBgfD65xGOixAe|2R%dpKA4eC4MB)pN=3?SKrL|{fY8l6Go3OKF_Vx$DPn>0 zBxo)uE>t^Hp)%+t42&0~s=5aB%E-^nFRd)66#&-4=-jeA5^zsPOHD;ya)`f=yQ`DE zjkR-3Omt)g5(Ddh`{TFYzJBOzYpgEIjt>d&c6V{KwY6~w2@44>2TFM3AAfxN<8rf>}+kV9o$@;F}<$g zS4{8e6gSpaX2pm5V|fRAOt-LcbabdhkCTSpU%vomS<)md%T5UM^>lN#x3{yivoN=^ zv8w>RsrAd}4?P_cynJSCu%Cykvx7Z2OpHy;EQsDH>iqPvr%f!ZDo%?E^!0FYvbT4z zx7O1)G%~HI5eVu<;;!B6xCZ{F(Y=^Md`TPtj&X5!+88o)e92EO-jb#-;KfA6f&CSdRiMj#|bc)xZg1D?OFC#H3#1CN84)%67Hbrdr zS5#9kD=3}vlEU1K)P(4;03S~eH*_n4lZoS?zM8le7XscoH64BExk$|^Ym!Y9UHX(tQm!&30`@2|ZKDl}F$d{`BjPP$KS9#=&_Y+&Ki0&7l%MuvwIy(~TbSKBbZ932twuapNMUuUkRXWq1R73?T@`C1n+*K_1o?#;@+*IHSCC z!|J6=mnwq3a^3DX78X{RUQrfoXUQW0=chP7y{NKp-<|_U&tAH6_2%6N>Ke~qk&Li` z(=aU1brd%&B^!>nm zY=`88*yxDxkN_WdXDC;wNt#Npk(&d01_AxX#Y9I(g$Mh1dGbiW`32BU5Ww=LDz>2K*K))?0DFSG21U* zz4HK{VL?H$_4*Yn=FXlrNp|qh1VoJKBSuYCI(`27ZG43=6?3jFUom^id1eo*Daxhpqr z11&HIBMZL1h_?rq&z?Gc#@yvw4jw&z^6Z7HH*Vbp9g>|xCH?8?G42K$4<5hK(R}{s z-ralmA3S*UB%5O(*=J<~2{BP&?_F$64Ry7(v@|ufkcEiB=BJVP$HzuThKKliIN4fT zSy)(@lUgB$OcWGg32F`$6CD*17C@~Gog5u--m?|ZFCZg5JtHj{3~|5$j}GyLhH!WL zxf}J5Ktn`=G8+K#rM3W4ZznE4-9YjMg#anp=-_}AIS#Oa&H#WxGmixPp_41@ zX>RQ4?d|UB6EZkkD5ghs2{)v4 zTvccVMyE9XHq^GzfY8x=!d;;pcS>LpfG%u9@c}p5P-+f3WSWxXDgBM;eMqPfdK%;~ zVGoQK9uFP~n0@@+AIdG&wr*IyZtv~Ht`8lA|BAwVI831$QDNTO8FFF!?lp7fPMb7- z*7Zn97s&8C;cLRz2Xd_uj|9AM;jC$sCrp?yeysesg%@?L+mcO!u0nJHm*VNZ+LikqrN{pDkeT5ky{_%21W09Bw$voK*@2WA1D|=iUYfut5T(G zxF6*oPETkSAbb-*hjjd)A;>FAng?_!4pJ=iA>)5qgni%~9p4VKD!3;}2{IMKxM_Wq z*kKT6avuj%L|CDPtq%!gDh{mDb=fNrndoGANG+6(c3J`x#cfQ@H8)`*!6k|d& zsiYU@k$}51pDN8+uw)w4!?>|imhQX!=(WDNt&@wJ7k$2+9c>-Wo~Jf0m_K{gq76#d z?mu~{WoT~W=mI$h9lHZxYFmA#pOc+Oa4?SqOai2myeH{${YF5O_icad%I9OLdyRyRGhni>j)}uedft zhY+11fr>jneC%y0j`OfHeSQ1PG368I?z}aIxdb&nuJFJ=8=H0T~MA_iQpJ~ z^KeO^O&}bCscDpggh5vd9O$U~AtyaG2_(M;F8v&p3TlzkP8|rC$DWb{;5&hbx-Y*% znq9%doa}dM8i0XAk_LO~hhOn;wsJq6Xe<$mF-ceWKA$6!sNvYgxlIgg0d7r-?_xlx z==7$8ofxPPn3iStCU&AC8>_WV!ZV#=wnhI;2M*?=Vcy;ZBlCsJXmGeevX>d8E zr{hCy?fm-b+n4%`U?)rCr#Qn1UnNB!IZ=VMrQ@}O#WHk5Qox1IwT_v((~^b5`6+Gl~9ttzNh~EkrUt_ z$=~IlaWaPfIYi_J$v=&MqZ5(c8~V)@7%C{r1|KK?trBq)g2~O24z3XZ=7ZcP|3Gn! zcYSg7^y$-AcqCwXS%tYbs0B@ON(w&JHXaGM#be+4RSTz#la-ShDKlyMl3nL-K6v(4 zN8ik>304{%byizfESxcE+<1BUY4euvyKv{R#;bQahUOLxV4!{$jX{_9?%us?)!fBv z_gr}R^x3PoTDk^i7M9dOh&uEN8*B6O3v**V9c_`FfcytLJ6k&kM_<7HygoMPTBwQchV))~)fBoz4UpgAg z)5CZqU~7FnV>1Am1M1w9M*{9Qw{eSTz==wwuT@pJDKPX99toI=mqe{`#%W;=R=RIq zUs*M8=A;?(mGf%x0pYeqEnr&2wVv*|kzp3budkioJa@|Y2~+192v{m(G1AprT3T}* zd`n^-v~?a|R9Q27!dQ9PNhb;b9D_dXl-}MVj&V$oWVz~TT|KgK-XsNC1zGt`k@PDVjiZmCOJ67`uQJFTfPFriRV_x{DT{p%FxPn45Y z7$d*HBr-fQDmt2|JQ6UKoh7_IwR!vggGxLSFrf)y&?O-acDK|XN9SvJ4yiC6nTBPh z<=Ec1Onj$Pnz;gIV+}u1J*#Sb-KGpn?7tfgBIi3m6!fC((-vnSsw; z4A}i2PIa_uszNej8M-ABz3<-8CaD1ucx-N66A&Az!lAMp64XZ*OEPqiZ&|0feD783 zq9(u}(R^Hmr1Vyi_jRSi+t;jGK6l2PZCcs2pz}z;JQA=rT6!U)pTgmFm1(i5nejXl zFqg)U!-)+l*p&{dEF!_e9fV+dIfgzWJu(JXoD0d7F7P9rM*?oBDoKy?^NB65EUzdf zIJR8mfz&m&h`N6J{g)42Ep?UoiSa&8P60(+uh^U{^m43k5_SCk`?pWM?M>B{NW%AZ zw6}B5EI+8x3)8fLtoRENS<`N$p8xtFg>~tOpm<}hgm`4I;WjY*$PyvqwOpTax(aMt!2#8UA zeQiZyc4lsgu&%12f@+;$zZ0Dh%0U*CrX~iu+gKR6q@zpZza#yEp{9~Y0ycQ{;QGZg zCy$>vb^7G(SGpz^*0#87*3{%@<|l@@+iJa3zkThT+Ub*L&z`(=mq!BTk$`z5U{Wsv zVg8d7R}U{_xpbU85>08?7kMdcI}$^cj;UPM$P%#_R>_ z_b8vaLQZ@wU818(siZXS)xk}uep*wk85_H698P~bYfk=K}Mf^ zQ0Nl>0|T)je~zUk(B+>sPNBztp-8%M763zq26lf}0zj;L;XZW*=kdcWHo* zKI+JT;+n9tBxof6{XiGg5|z66_x5ymiW;j5vvNv>Xv9u`EHZeAF6rp(`TV7~yHnCq zSD2TQlo*><*2E?F!B>hF-JPGl{rag3U?Pq1`W57)#fJq&WL3eJQUgB*`pta(^S7^* zd)Ojss46eYPKyb6@9ydool{zcjoH!h>7Rf8{-wJ^f{(DVx)jA=@nHd;t}YIaUeSd` z`5oQ=_}8Dmed_LPM=-phsuWPt@xgwcsN%J=wRxYJ-TCG3|NQHhj~}`v@X%CN6z1h5 zCx>{sJD`5l#=;^brTfe8|NQgUPd)8*wL#gdK=R=-3%((|9oTtd)-gcnr^l;$jH zYvT~Rn0%8EqvnIrB$D>7_U7vRtenagDwU$sozd&+P&6RH2``8>d2;Qz^8Wp&9=jKc z+Zaa+aNUJ%Shlm?=Yi^x!+UpZKpoq%RXZO9*Ws{-c7sQg$oaaLj_%ukXzz~g+c&RJ zR9w1b)lt(7xCxm2^GLv6t`GC6B82RG2*yAOtD>AVVStBHH zr0`{=rKMq~(h-U+&9HmG0EAv7`ca-=9*Te2UZ?$xOeH#TDG#U$?P=*&L{2Jdz{w41 zG7+@_$xpB;fRudTGpoz#{=K zU%2Mv6I;K?_>}ax_vX*;9AA%~Mw1n(=Z%cJR!_E7$K`Qr)q9 z>Eh`VW~{&Zq*#K3YH9M+xO_(K z%*A~N4sY4HWzUWc%Qr7uI(71tne#TBe((}iT@Arc4qrI5Z~fN8+gGn$w{gL&*)yk) zA3tO9`r|0~Z$gz>Q)RlMb zT(@wg;=;Le=gykFXzg~@EBBwh);0u{()gQd;we_>6ng(XJ zPL#t>|HB={BLQ=+HeAN(#AnAjj|7Yx8sT*ENWdREnoA;GjkTZNz5PJPFAP1BQ`6JZ zGVuAd!9?%*@TsFNE6Uf-;N9a}ci(vhg~x&+EiD~2ejP;b{rb73AT8P7+UWVihq~_m zSRg(zB{fwff_J7H6v=|g%Oe5fu0hw0`u~Xp&LaWyNWgnv8`-*gzxVYI z48dUp6@xR0M*_x)+;@?O^O<$?uc~5C_Wz)3K6iPXOQ&U^-)x(z-j_zK5{(%hPt}Z(( z)WX!&&fMIRdH|sK-^tC>2lfjg9aEc)ruypqoRr9rpdhaMzAqZAgoK8Mg-5a)P;^PD ztF0(TaeqomQhZEQL_|bnWOQ^)Ol%zJhyminL`Dff0Dz5w=07RP$w^5`00QQbfbr?j zM@+YLCNx+@>>#_hCpT{~F?#V}(}wNqmYqI%&D6ogiKVMoFPU=mg~rYMHm;sN&<~~2JQ8qDq~SXo(|2eMqK5$t5EBA9ad38na}lGt zp(ZaYA>_T6m$!$DBl?5bIXIyL)9bw-$u~u{>S`(qGLqtAW23`^0ndT{N&!JZ`2JX% zEied?98g*a;ZI4VR^ZSNsPKk{ARtT%|0DR1lrxmo=4Yh?3;+oR2`tHg(GmSe+zm~{ z$n#&6k0wG;At@=OMi{0cv%cY{_?~LVBLTCV3?2zsByJ4eC~x^jarjR|rq7==aL6FE z;U7G5;;e;>cWJ|e4*l|K@vzO?CjISiGIy2?{OKq3=pQmn_TpywafA1on%TgCWt#8yUs`uG5VSvspg}*86pE_{hh!KN_3>q_P#FW_^&OLdpW6&%vdpmXDPdio) z`P)x3ri~arZp6<&4OEchk$_{l0f!?g7*76?I|R>W0X(3|)Ifr?#NQ}A0166fihKm{ z$*~NA8UzaakbF56Lu!Wn4;%wB09b+mj|AM(*5qH%P>>QC8l6_WrgrfwIU+T$2tXKrk4YULD^Ro~ItSSS=!B)K~oxZ;2giPU)<7wqQtJ}f3KAu%C7 zxum7HskKU&SDf!>>JbnS@Y>klHz*<{A2@1&N`nnpD*pVXN!V6Z5+7jd?C1Z&!Xq>~ zqg)_F)-}FAI!Z*pvdquTPH6c3Mb}q%` zRkbKHhMSsmPK(iXyS4pWPjhcWuH8K|TR3q5lSR$v*=(_xyyflPU%Q%m1wH_>0?aKI zX1yLL4;$Wa@}_rv`G#hVNe)(K*jrebnZd2-HVt&x8p`|q8dSp#GIga}N0)!NZ;kRG9Vl55X`G;4KF2TJ?V(9tuRw66`2z2Tffa&zj*4LF4#JT&rc}C?68bI$y0&jo?)FKjA2+EU# zy?w2WZap>)Dkeq7;KxczFukR-O^{PikQn4^>+JaW!s*-kzUcr?$}1oULag5Y%Lh?$ zPGU%Se4w9=u9nfWC(qt^C$k_>P9COz{G}?;+TB0cKQJsdA;{fAOa0b`lV%}Nu?a~j z>CNanTIKEHXyfJ|78Mg6;t~<+@%X`uyLYra{X(K*;+oqEHT~n=EVSPkSop*wrG&W! z#CX5dd~s#ZO(%Dsz)(rjnl;+G4{lz+aqHd_UDw#0Ru6n!(OV{KUVp#mV_ySCw*W z^IQ5i4n!7Ex`C!<%<6Zk#!>|KRS$GbYX2s}q-#nVp+Y@(=CNT$$|l z_TiMLNan>2zi416jTmW$a+*7&r@GNIj2O8ek<% z%CetWFP{Q7Sl1u?MvutF2DD&CdK&!;Cn*DLV-INm0~XNp2%3;b0%kyiz5n>_m#?3C zI+_Kg89-_Eba8TUj4CR~%}i%VfWqH_r3;Ohmss`lfbfBg34Q*Vb@2&~!g0AT650E*W;H8DO8 z*Mi!*um3<3A-p_~1k57=^GLwEP8+1wunH*XDTdSuFy?$OBi*+ex7Ab+@7}U*t+GQ! zMG3Xx%w^&WEZ$%@JALgZchyu6?%lR_&6=%RC1u5!p2O%BrLq1VPKG+qZ=3>D@76V| zR<7Rkt`zj#9JYRC4tj6-m}ow^p{BfR!@AWgmakmJBLVNwfFm5;HOk84tu4)T9^biq z?$Gx2%a<%#v_x^)inSXLJ$<9CM-bFy1@0CG+E4FaKYwW3n&pa%7cX79Y}NYB=N>(M z`Icc*m&KVG=&0Ynd{$-a>Sc;c7B5v?v2Mejt9Kr1yeQ(pt37nyJ-Tt>l*+F4z(H1A zwtB;+Eyph3xTpTCkU;8-iu0_WKDm1S_>o;3*REQ*V%_E~Tlb&2c>OLRk>(Q?X;GH# z;~Q!x4(-~sZtd!I8@KG(cl_MtTlb$lr}Ga`yJ(8_{uS2WyM4>nt=o3)J3=U-05W|c zJuGn?@<_mRE64v-e#o@}%*!vpGt^L+j1iVHNjyI=6aqM$s+I7gvYNUCQm+ZLZJ}5O zl{pp=osFuxBCq?ZN{3HB^UkkDZx`H-5D)FA*JNvMn<6u8=#U|UM=yU6UyD9n=;FmA z0pk)>R&f8JrOWg6Gv$X4Bq-hi7yvUKHrLY2!>gdMxGdwsq0`s)Y?vlHg2+EHmcfIE zPkClzWnEBIQkto{b?LH|)8t1F9tih8LGcb4FnGv_$v3sMb%8`*TySp1(uK38j2$t8 z(TQi^;Gv@@Ttvhi(pOk&xRgf%PEX^JfV+VzjL|`eesIQ8;IV~0hZOL>!EvCfpMev$ zG=q-Z?mj9W8AE$}Cy+SFmy2frwhIPSu1fg;9oN~_jm82k@cn|`D;*y{fBO89Ed>X0 zS3CUDaBTOJKYeMfs4A}M`1A=ZSOMAKbZbTSIxXMxvC84vfnA$+C_hU3@TCWITA!nX zy|*j+#v!Gxipv+xnYr|Sd?$|tY!DEY3(#0cb4EQ#q<$b^5B z&Y?kypqnj)b|Ew$E|bQ7a?i(RLIQ0?Aqjg=$SA^eLi%NMc_iTFv**rH7&msD!dQi| z<7D@_zYmE(p$&cTT{RCc?%$+1XDX0>#{Hu9Dw|EOdf~I}UfQHej301#-K%|5YObU`TA<9Dffd$(8^n;YlqA5HQa1)ON ztecXSmX@B8LD|zSxJ=b#`=Gk^{`sQ^4jejk@bG!f7$lJPn3L#d z@%q-qQ%4W%*tKup!Q)p9+`RpQ!Xl$#5sO`PV8)3iYwmdVK5lC6xn5RL1K-+}e)ll%ptaEe>!o zfA{##-3!N#s;b@5c&%jslma_PXUcG()Im{^pS6*m&hzURF5P*pqpNRZ3J?PaCud3> zqU5)hW+5`_3e&=Ty*xeKU0vNU+&#U#$zCTGB-Pi|R-*4#c5-Y)cvx6yXlNj1`E$8< z)HWM=4L|}dD#**E$Zl+GTnr`pQ`{aINR)TP3 zD$bvd2yp5t@kRWTvGIgz#{E z`q?j(f(2L`a!sTh=CR&-Pbq30S^OK5RfwC=u!d2RC*cm zfi}VWLEa0lBng5{0?Y;qM#yi6EMedx;}s}0*#G9h(TsOjEkM#9^PaN#KhRt1>naKW zqa2@EOL>@Rby!hWRx09_2Ow@wXIHDRwjv|K#VNL`fwgfdD?<(;nV^#Pp0B@t{nXjo zT$LDX{YKNFumOKT1#+y!^rqU{KY#nj@4tNRX>TfzbkcwN?1f1gs1pEDb~MubsX~I56wL9!n6nUg++`6K=ZQ;E6(_}`Dh6yr$`mQ&6CYH7ij+8-< zyj1hsS5&vGoIh1=qzp_H*-3Lxym$)+J4conFKozsqPl<0+_}?bWkzF6n6>ui)7QGj z=GOL<8xNOPL#W#3jmzdu9yeBYjO?WOD^5MscndslOKXtfFT;`JtGs%x;(`SW7A;<} zY3s53PhM(~k#5PHX{_voM*{Ba4$dP1^GLw+o?7F2?Cyd7mylq0i(n`7TN@V6o-8|Z z49lxg|CP(OF>$lNJY$3eP>jg*m}y7E5q_Y4e;s7658nD*%M zseNl_P8vUE#IT`5N6L(wy6Mj2ms+|8hOFaMeVy^u)BD#gm^4;q1Q#q0OgU%Yvz4O^5v=0c&b>W1~p7A{=8Wa;XyTMu2he)p03i`Q@7QN}xQ)CrTc z)OPHK@AB&9v*)hfM0OyiYw}3I0#-9XWdrE^&DrdPAJ7Nno8L#478a0E&LaT}YUpmj zkU)XcS1S-Uw6wH#edug&tS!k;O)ssh!QFtM9JjadKrhhkZS?hx&261MJbi%mL^a6W?M*cWDap|x zzMk$*E{O!fixEsq2Yq!qILc_d&ovZ@AZOmMoSsUjoV z+ulg?)x(==$5mC29Xoi+6drJAx2l@boS>9uK~8w6yQQ9v`fY^qjvqg+s&ol1bW0n% zs_NR3$e7anl<)u-eI5xIx9+U8#Mr3t(BR;pz<>aMuD1iZ+bAKZxDbB1oUF8@_}Hk3 za2^SmM*=3VEsq3T@?h)YiL=Jaju<_9)EFKKI1l{-lM>>hBf>(Nr=GdWsk8A-74eiiE0 zOSTa)u%GNV#(^wm)(wG20_Kr`|Ld<`KX$d&SJ%|kmgc1;L{j z_O>V*G&ME1FemzlPrrTrAZe+uDl5#+ON$To^`skurG=#ht_bcZ{P^_imma))MNvUc zMq*58pr5;wy}cdQuyu0vAP54Kb%<&!it=-E(i7q%g9CiMJltID5dU}g^kEnUy*(Xx zeT2nwveJ{{qawos(Xfwo;tNJKiL`8_9Zg7t)fMQX1)kX0nCR#zbmHTYfGMq*k|4M^ z0?T%Q`XJ9PWja%IkUBh21h8+AlHrknc_d&S2{?gA0;Z$@9trrQs)~}b%E7JcSFBjJ zaPI7xv**rVvRxxNJ44dsVP*8})`fGYl#Zz$+_8D(O2tKUW+NSc*8Bwv&qgLl3aowI zU*Eoc`M8p@(ym>bRw&L}G=2Ka=`&`|nlpF7+b~IzbJG82?=8cl%C@!9-Hls}APph7 zHxM*9bfb+1f&>Tz4IW5v4aD8uU5UGERotawRjDWm*23;{_SyTn=iGP9RRMb6^F80s z`)AGWR6@p_b5&}sG3S^=-Z$jMUA^;r_a5B6YscobE0-@@GW=f}I2olVv;;V#c_YHRG-uW@+q<XR{_I(^X3{&@uK!HZ>BBPtbHo-r6EF+_ z1|%x0Pe6%N?Ke!HoPog39xPpnaglx_r&NW%Oz?kY0&UC6tK#yDSbY?i?Lc!Ns)e&n zbJ=~TVkYP6ryxfS1>D8B5J<+`n+!Ht$CI2&5=lte5?mS+5}Dd0Z=*CV{F$5pEIB#M z9VCp>q6V*Z9(7m}dg!j7PFwVZO@H0(d51vd(xWVEE~ICSbyf;F*BAAR+wwtY(1m6p{a* zRu4KLvvLADi4b%;USWOxrLl#D9aX5Xk!J#iZAyqoT|L%L5$9Gen5QH!&ocq@Ou!83 z|63+s3eHm_ogyc}PzUa}L$4$yF*yl%CScSIws4CNTyRn;IOC-^&)w=^$n=CqkNkHL z$w}HNX_S;E8Z20NOWY+!%{IviY-va=?uj%sc&@W`*?fc6juym%ah9U|b;vpIU#HK{ zyY|nKlUJH-(9zP`PGIOL_t)tv5Zebmr8F3W|!W0u$3g7@3@$mYK!L zJKMA`-MhSDrm}*Zyu54+lb?9h*2%*MEOcO_ zl)ytN6-z6OOwJvchXR`k;A2umcSphTM}kIFNfoBtx`l$kna!AQJ=R-$FZ>)iolXGJzE=PNXxRo3fz3pjx5;~b2_Xq#eHpMdm-#&5T;;D68 zc_!d<7tUy$yN@zt2REp9W|+IReW26bqi4=)?b@?z*Uww_>zv$o>fSRGD@S)cJdMIk z&#=&!y4t!rT3TmL9XopLG@eDH2243PvYMwRQ`_q0P;3 zIKl$ue$DOh6o41^N9%PlIVn9Epq&19b~KQh_qHYdlptkt&UTdPf2YLt$gTFCUh7LY z9|Erg^EZ(*6Z1;js)Y63{WT`)6GNMgo;CFL5EwI#NhIy;l2li}u&!%P4tl(B(z>~~ z`*8-K*>Q@ZH!ZHJ7Y3vo9JLLRdv>Lg-mv+>WHte)vF89m8N zf59XL`RO|rjJGZ+NB9;X`C>`en}f3ym+n6^d%_&`^~;YgQ<^$&^Wl|iU!|vK<>nVk zBn4L%PaZXS%Gv`a&$lTLAE~_b%#sPCfbu_T zwA`2<9{@(n!-r=A2GQL0iQA4Tf2X{4=P!qjXlfqUI`hlw9ajyW8X8;JVtZ)p9~=Z^lp`%j*~Ha5o!LEBe*o4BniB{kmP-Nn_-+1Aq3*u>n@#sNi$ zUcPL51Nj-4z=b);K8{2Bv7fi6m$z>~U=ZlQ3D*o{?8OyJoWKoJ9s7;DfvdIN;+1C|Zc0q{(~xe4(JG3f<`ML1szgvg)x z<9|Lh2&x*I8=8MDeV<#o+q9c`&@Z>vj;PmK%* z4QML*@JztmvW|6>{3tvVFj9V)6)SFQuF1e>pTi^I${R-So5PY;4Qc>cJf z8hvj=j#_qTQNXzaN3OS!5K=tB^n_9g2wHPnV`6OFgQJ>fF_L~sXEZvhk&ya`I$Lvc z+-@8{!T!abZpy!8QuZsJ37BUBCcF`PDkRlGHcm!YwT>LwvTE+KqxOP&0PM+_z)8$A z0q0lLP`C)>XTrkF0Hd?}Pn}-7Xp;Q=d#|mW!(x*PYf(wugpC24Mr~DDqVct3yMI=j zGG5`x7vt#_^NRs5sJc%Ns#d0&}dn3A?Z7 z_O(yh0hJw6u{*N{0845OwUuRgIfb;dbA4$0rWKYT%fQ78j7d;Q3TR&+>OtA7v`^C} zj@`Ycx}vh2T4@cXSPKAj zT2@|3(kju1Pw(IMN}8*M*-7F4L7Bw!3DVS@az->*)%N=zzrF|6ZgX{UPI5$`k9RyU zKuU^obGgw?pFjWp>&JKf;>POYtfVl1Zx7EH$iei>5f$=Gz#j(sI@?-m3sPgl0)4&Q zT%Dbr?VQ}*-M~}a+&cLCCw#bF?V_r}v>5Ohd%C*1xVYHaIykx1K;AC>1e)aDuC|8q z{N$)m9DvGkWN2&xpfqbd zTdnjRzgpUuSGHDbs+uGRB z^9M3#=sf8?R+UmgUV1`wC>}94SHLBeu?MiG9(Fq|I@J}W#d+CjV4Du|_w(`g@`RI# z>jBuxTAc7pc_v_<33!E?n%c^>C*Q=!#)65rx+*oTpeWAjiQc&*hjwmUvwFpf6>2Ng z*6sHJ3=KU#mGNQWk*+ombagcMY*AlH(iLhe*X=TgB9vED)l^3MIoer1y{&ud@Xs68 zEL*m0IpnLB=pVL0PRej|$F!?T5Q(L`}X99lk=qdcbU*tTro<4hF2$o^4LvbN^;ep(doEQ`8=k8=@V`XV+X=M$DVb-Gv z4si_0Ng9`KWa51)eBe5pP`~MecJjE768Smop^``GtF+DIkfYqT`Lx@Qk$tdbHFr6ljMdZ2$|^VW?^m&{dBRhd40#thXN>In#cAVLC^oxu-Im#^(M7tWhKYqshPRn_TBcqU+0%3DKB zh^&EM7U4K}19&E2N_v;o)$>fiAj%vNTj^;1ykY&)#VYevltII(sOB#wrW90b(z7#| zZgl17?#=3(=Bq4P2)VNI#07DUWR+D`f~dH^+xyw=BinauSgocyV=|aLCr+NMcvuJ{ zuC$~C^0#d^ub&;?wsq6u`7FnOPtL%%}04Mt5!>S);yc&a7#m`NjN` zCMhoqcoUnDoR-ns+uJu_efs>y-8>U8G?&6?q}cG@kqImm{)MR*1up1;MxIvIr;MZd z{lesn4v5xMG>&HiW`0w!e0Ot9;Ee8=1Je-G@<2a|J0K@2Z#;&*J?IJnh9T(8vWnmn z#jgp%0Z%El-X0=MMVHQQyi&vo2JWdV6M3qU!C#5@x)Ee8~AVG5489#ddeggli0$Er=FJw08t1fT<6T8Eeb z0ie=STbL5z>*^j|1H={pzXNFp_e&OEL6%`_X-b%@$-`?G4Z?XQU~QfW*b7ADD1yRs zLQh_fouPs5IV~+B3e~z}Y6pbTz>u(rDE1uEvsvI``1sL-8>fzIUwdii0+O+S;IN3u zDC|Xe8WEmr7eSo`+0nrOPY(?XkBFiyz1TS9J`fI&gqDtaVgfD70X%PV3R+@fVp1~l zA?Q#*k1AOD7!xQweL27w%E%Zo;?vPaTlg1pTH@kliADh(J7& zc60iV@$_eX380GGo653cgWTOBDnM+FPYlDc6c6ctcaOB8I5ph)<&BGaL9J3Ir(i1= ze(fF{?C)$SN{e!Rc~$%TMf+w6yFBD{cO+dOe|_ICsX^?}_1R@j&0{+5l-RP;l5V)&K}n|dP>`(9$O7r z_?W)4@3&w7{6}|PPIRER*_|^-k7#I|ypvu>ISv)&^q~y=`T4*8*;Sn!;_Lp3X95Nl z`N5OAk6)P))2}z}6w)qnRZeoalcB*)o(Y&pd`gJpkHr(x{!RZ`nlL*W3TX|=%fa){ zr4fHoRM`)tfEY~L_wOR^-<$$}EB>ngtOqxKXfO%6#&Q|(>%YjEhEo!$Z2zZWxcpcB zM~zQAg2^3Sz1^6G-V@&57yaj%fZa~;Ou&i?a$`n~mY*_&z;iJek%fPDVXfBwhkkCLX!{7A>=*LAc{oxbQ64kI=)O43d8 zUq2&5s;{v^km7It;NoettFEx0!@|QQ3<&={5~O-NYJ^$Qt}pMNJE?X0l%A8jPY?`Y z33!nE1_nQVdf(e#U62~&@bd22W5>15KCyRp_X!Lkd2jE);JbHyl13!p_*y==aqjqu zQ&)_wo!q?r0z;+J?p{zK4-RyTY6_DB9SrYWI)mWaBQqPwefCq8rGF65bjm%Vg#^8ir>68ziR#F0D`Fb>ys)n$T$jJU{&;7pY*iU6kM7V`-_}`1ET|@`OeNvXBQ_ahVx9o zYgenypEYmWE1n6MX9DJ#fU$&RjKJE#HLMUk6EMYZP(_I#4@lD(m^(fks@b42On7oT zT0lMkZfc$h*xbrLI5;#ctVz_^+x6~0ejDhLw$_viGZUhGU5Uuc+5y%>U~n*Kyd@pI zzkhryl{A-?VTWNQ^$r0HI7^unCfh!J2F|n})Mcz(xooWr#|g z$=M1+KamN;CMZJ>HU)mkRE+DwT>_ac$VlcokeCz@W)5A0dy_;)PL58@z~m$*D~fgC zGM1?Wlc`14R6Rb4A-SMfc6o?eS{iFg3-gOBM9q-1 z`4I$W@18#YmZr+=^ax)kI}5K|3IH=lEad2bYl}n;wM7{*!S43Y4X#{sD6RXeoR*ZT zyvzu1Cu0MH3tFe2=2s(#L#d}UKOCQ}P0i(b$zg8p<_~UNJbCh@USd%mTs)Z>?C(RI zLsVa$9UJ7~Y^&ZlTAsmSq zdVj(iX=h7WL8P~45=47I@{LBY2&6n&+~MFgQp$-^DZ z17ICgd|8VQgsqRVvf{#=^aPXvg@uG5!5#+$E<>I=!ThJXGoA?;WmR8OsMv-J4JZ94 zEEM)RRifB{^cO2kOAC%SL;4Q|$BkgAZ|DL%6QK}*sGOMTKXsrxf#4YmBxwVuqz+E^ z=@l2Da~3TTl~^coH)uSQLxTZr2KX}U@N^9s6Ln`+En)7q+!xoyFDh z{shG$ObK~YYeVGq&FdDWmCdtW<9V@4*Tih@t zXTuv?(=Q#^ws`4ec(6wxM_qnzMlItZFDxKA&jidf0aF1ERuHzQVe{shfDr*iJ#{nT z@KQb?H7J z3Hj$3O;~1DCmy}Ne&m_<(9=gr#2^ZTqp%n`JV-Ij0eBM8jP)Xgkeab4b4DrVyGj9=6NRI z5}YK+62aY-G?fc75@Ozj16<6({N-c4i|5bj+_B@CfO#fho(Y((OFR=W&jfsA-@cuj zHf&h6V)^2Q^XJZ;J8!|_9XhujOC;Ty#s=5V96Pjo*H0U_tzZ4)(xr58AU zuit-$%}o-dr+enW&Yw`lxL$qTO0|`0%a<-wQ~%}cHG{`5vHNv)MBP8Lf9LjX+qQ1p zv}OIeb?R%^ZvN%urJMJj8k(~0QCgQ^b4UBg!TrDdvUm5c-3L!z*1P`%AfGl4E?fpG z_O_@KCI;yo}x)*C?TnY*zA;HQ++)|L zSy%`1lCcg%j}sV=XHEqW>`ODXE@$!T+NFL)tCtK-&3N z{ihW`X8+R+bSxiQAO8Q&qZXf#kbtpVht^io ztp*$ch{Fxx_sop+^z;m_!j4Q|=DWkr3HzKvfpG7^16V+HZPbT$UnnRHc;=p?02AU@ z0)YgK6hn=+XENY1gDeg&6u@?-w?}3(B@xhgx&mTs98Ljk&8RSB1$|s0A$I|?&EaM& z9ZheIx^PA~c?#iY#J|ZgNZvqB&UHdZ&X*{=C_I701WHcGAtQdM2VE56|Ah%uHicNs zjD=R&z^~tAGhjUapw@5X?7gu9AesX2u?<&{G5xVHKv!3{II|bE*5ndpe>grA+26%d z$@|{+n>{oH>mDW`IWl@UCcsW9{IufUwkr2CXI*G4CnqmI#`8?T{X7$}3@n0Y0;Y9= z1p(-VmPc-F;PecKc*xmNf+7n4oBn_4@PE*MATCqG(Ep?Ub8Ay;%fIPAf_d#(002OQ zk9!Be2>7c1h@*6Mci9`7kp6RWS`g`dLxez~sI#TY=k!4%&;rTiF&hpUR z`;&QlC;JOIFc-+#=&tTka}yJd>7e;}(Mi`wDgm-<8o1e|(!OHPyL-1SoTa3wFh8k7 zL~$TOh^@v_Mi=LqfZN+!!uP072NmfAd7cTFObO%-)zvX&_~wOliqd9=I(#EyPbDX2 zVLtfyzK&-`1cc0pprZhrpuV2z?>8^dJnp1GS#5~&eIt_nh)t38prkmN{EPj-oOqlI zkJ4sHK4cn@qM?zefCE$E#&f^@_LVL}!F!IqlIFqcQV$=pm#>6CBaVA#bGhy#m;}bm1>^P`CgSkf$F!0kr&o z%k;}s133iHO;XSQkDN_~V+6@5UHIR}W7nY*0v+=4$`C=m&X2v4^pegoEN6`!B#<9G zhUp_)9dHbRDUbhXA)(Eib>==AM)pD<0nT2#>yJo|=%NMk6o7ubh2171N4R$rP2ywr6;_}Vw znwtCf?AUhr$~m41n1Igma>-&RqXO%L>^u7hGrwuEpw)m0VCu7Ff?lXM&jgHMZEY>q zHL)Px&)eAaxvgDcn#F}j8mnI$+=?%&ECa53MI}IL8WM~jXq|WWwla5iylrKAO?%%{ zZ|he9xeRAlENLx>vC=wy+b-P2?8*6~FYaACz27gv>hZldiHRv0Ig*b0tS}ebXNI{U zb`Ov4*t%Qez~ZiP9cU4QFE*OVEF2Sko`@#clp< zn@3?d$tlH_Gre*8=9z$bCgAVB`+oSSQDYQ!Hcy;9a<{dO6Wpw}b4Cvvukyo$t*%Q) zLjL`TVPj?*?4K}U%o0-zn+|bX`i%wOZ#7suev|ogTTBee&A8qqA(^`0xKVYxBfm!zYX#J89Nb`O%}~r^t_3d=_#eOBklP zmM6v@(VhCY5woYxUblJuj|t`1doGuWG625FS&Rf9BXi8*ezP+OlT$$-Rc)!T0<20nB5}h}?8g?c zzE7WieJ2&wHIxY6#HAP2!8?L-MJffXAo<6C{Ej5-PElidRaH{3cX(<+Deb=aDl3J! z|G)qB`E8G+wN)%?F3Zp4nSiCg4oVu^8={R~yLgpX*EKe^w6ipFI=&%iy<6Jz$3Vwm zYq85M8)xhTpvj_$2wVO;JA3+(&;It;{`SG9AX^(7(A;8UV5c|Q1K?(byubg`9{_92 zaC5N1sTw;5o&VtGguJVp;W7{W{qKE!)iI9NczDPq%XOd~sEbm1`upGi`td`1oC6e; zM0jVc2YU}vHpBbRzx5~Eo72-pSCCD?y-DgY@afaLVjCk0ugP|d*1ao zzP?TYxS>Cd2D1ZbCld4F2NqRcIYRa>{fTTa_CY#|VyC3-uBF2GkOh;|lg|dy;g!7^ zeCL^fNk{2mfm?^$*NUY0hE}-7$p`U8|G|??cebUuswBlX#K%8D*whO77yYLgqgW!U zX{yeQ3Jh_uxTbF%Sx(wdg?`X~3>WuxHx-qXq(_E0d%EkN)4FaNk}D`G7J%oXtPHdF z{Ps>#UX&gkn;H@7Wc*9B`3dsCo%ma^QvAHhm+U6JRxQu_X@7zDg2}f?Y+Vbq|e29jctSjeWr$B#Ixv&u4 zbmmFtI?=z#tcC)xOo37%`^`5mY-Z-Y=OBY*&UiMAMxvqan?|!VpHlWK%KwoGF*6G; zP?7)@qH>EO8Sh-80ILx~;j0J<%`^p>{ZHsumWP)U{EW)VKPNl@;rETV{~uv&$MMPu;LjYiyT!C+QF1nSi$$zBID- zOUf-Q%+Jn9NqFOH`uNEyjWZjT&zL&poT>TFGxsf>!gwZNdOlgh$rTTKQ$xjTt)uhC z%hUOkdwPdDAeSDkp|0TC&#Om|p{MrS4wUPI2JNj$*u*md=jKo)K4XmHc%w>72q1)u zeh@Wb4g=$Wq0QV~DBdS@41(tXh6sR$^KuCggv6CqC_7}0%T2<(Kr_z=lP?jb1SeigvG+j+G@InK!8XM&jbvv-CzIn^WT5_FwiS* zs1amFhXwh1d$>9|dIAL`zNV(Wspa#ZpFjWluCKeTzN#=aIviZM?#|9mZqYH(QJ|@9 zYWwrgKYsi0uD26ZytygSp#eT#Zea3t_6rUQs;+B-{PS<0-r)?@QYXwv2oLu6@o;l- zc6N00@$$s*=GNaaoM!^&nSjYECz1h*eM0F;g|Um%Zve_Mr2ouxM-Q%pYzXN;5*FZ6 zuSXpec7EzW6L4z=UTfds+L~{+&D%FwX?M zVbhkQ7j$nKJOpJeIfDd__wQXgd+gwjjq6sgTB*Ky%htW8bgtage^6Q~#8y&TR^Y6! zd*=B59h=nGtx?~&<)=Nzv@cw{eeV$-F>r+nvD*YcJ$F)L_x3GYw{H7+&p~1dy{-S? zF+pKbDggGoEYJHo8hiHa`sK*!^A|5&y{TuwGXa+%(T&oL@$HNLlO-@j6P*a5hCZXVqyrIx=n~MCH(W6F< zlvC471zsBHjB($m1*+h zMvfQ_w%t)f~V`yLHZ05BoT&%X!-BC^8LbPe^@ zYV%Z7rj8jl?E7Jm4;wyaoWgdUOE>h$+AJw|T)%SV{JE;r6i0p!ISn5-Va6e?vsbR; z7Q$34x~#TxuFCW&3S)+YD|y6-F%#qz*B;h9eeo)8AR2PQM1w#{N_sc}w z4}jajBYfvfbt1t<%#h2b~g%4O~S#}6Ohvo8H;;AMpv<7LthpQJT)}>}9u8``$4* z-as3ML)_%3t#M?-=H&}{Cg52!XG~XJYv2(QmzJHIU%=hj!QxkER;*HAJZH}AnX?vd zK4<9Y8y<%Qh@2cw-q$A%J->VPs^!bp>^Sq#%+4(!A|^34GbcBfyW@S`((ac0a8Fmi zH*v9%5s`68X_=TkzpzjyXBa)G>1e7g$FZ`o2o7Slt(TCT9^9U8_z75k9%q1(*-9>E zdQ>2W4s_G~?x0E?I0zVy4tc;x#u^@K79If54!I!U=LTVD4bKEj%S9I!7}gkXDlH!L zMGFMa1dO$cX9Diee{^KQlI63eO`A4l>daM#Z@#p0@d*l#jEqv^!M*4EMl zB-fnG%nS+uVfDz%gODE3E{gpj0~mFDaLkvIbBUUum}BvzO_6nAX)inzFsU#0PlVTK zy9c};sk2O@Y1=0gg0!2H}qC?95)B+_GuI!a38y1Fj&CODN6tOH52oPEDurQj>f7nyc3G zOu)4N^Gv`{ZE92rhpb;Na01f@<`0Vl;>V#5lodQACmBnGVBc96D(Cx}9|S3BJQ}6@ zQwLVy%#EiAA{v{Rda$$&T9K&;nWB?SD8Y|B6Y#-98i$Wv4d!YF`h?+5uO8~3Ke~V4!9)8G@l3!jJQFan1yNw1 zbdXDc`0@j#J5VLikp40;^&9HR@*%#FlVnK$WqoM)(0ERWhZT+TfoQ(J$p2mcsRME* zzA}Mgap0MNc_!d9hj=Dn0h0LX5R(Q*ai&B_QbOVb4le`{$RcMv6EGPk_)s{rL)wxR z=6d(o&ubRXoiRh*vZag674p<@OyJVy>?kku8;5>gw`kV1sdJX;HFT1kiV(>#>1->? ziu5$Puw(O@MbjtAPn^2=X*(wPDz7doDF}XiYWuq7bEYWD%T1ZJHcU$C((nRN`fR(f zxLn|V^VsH9OH={%FQ+_f-lbaHF63#|VR~s(VL?m3-_`x<%a_hjmY*Q6Fmd`e82EH2 zDMbs8$*P{;d@k%*vvko9iWA1kD^HrT%%qf}PQ^u7PrJ%Rm2K~NCg8b~c_v_>h{7$2 zIv<`17_J_3-xf^ADgyCo4=qM5szwR#uP_ z8R+BV0}`{K;E>SJPsd?_t} zD1(I#j1+_(T12$PR}!BP5qS|2l&FQ(`l-?RAFC7t?*Re{*1^i^S|alr!uDXt{tkw5Lcvks7?QlK z6r-4el9H1D4{+xMk%eWh3ZWR14~e_G8U%ujQDOXq@$&MMc_v_<37BUBh6@z+h>Qu4DzO+) z5|$^nK=Dk#(NS+=Kvm!JzVG9I{_(!Qr>&t3w8~X;Qgs3PYj)wiv0v8>H7m+kn2K%390_K^3sl;K3DVS#hPLK9= ze)YuQ`ep4iS|?7QK5_o${g-AojxHz+0c}mAFe%*A_W9GhH!hvmIeT8`_^HdcAHFoT zvU6hchKAbQFjq^Xrv|sLU%#q*N&D=T+xMToGPSg_ft(Tf)|JM)+q`~q|Gt6#t!vkB z>py(*0vIEf)Jbkv z;glo&8ce;^2-%Q>2SMfv%Jo2cEK_ijvVq_)q<-w&%`*Y>Ou&0@-hXalZjG|9hKA59 zM>Ur#Pn|en?3nSRMvtAKG;PuLlb7@!y*9V0rLxc_o(ULbaXb?+c?q}-0_JYunSg`y z3;RC($3Oq;xA*Vfc7Y9|wx(22l$jYF;Opk% zoQ#y{KrdHkXGdEr+n}WW-~aW`|NP@!zqF{NzOu2tq%bEnAtK1b$=<=i#?m%0_U)(t z`G5ZP2WXI~I;*}$P?DD%73$-Rx$SJM?ft{v4)pU(z{sQInSf=9KSZp8=r=*0(9X}f z2ftBUaulKmyoERzH8nAfrWZ8$`o8!Qff_`06vXt+xdzz}^fJ^10eV25kbjVZIFyw< zlv&LABLQd*y984F8dV5VNn2xWok)yyZzd90aLOA0R`3*mf4re0qp$D?5A``f@z{2jdkD3zjh#AW^`%G%B$i6h^*j{ zj0c(mTn(+Pmb2`>Q!$fM3J2FHVn`N7TWfQBlffqIcxGV8Di&!xAwrT-)7~a|8>MOC z&lw=H@+KysKqh`b{c+?=7oUz!DZ^14x`3?y0W<+nY;8LmD?3p{iKXR>{*#=tzV7UGwXJC zt+psB*xl~c)ic@`Za#SU01)A}PVPPd_)zhAp}HS1wH3KZp&ss@9`3HL?q0qD!C?_m zZvaz^37}yN;Igp}LC~zUl%zziB_+}4O<8(q*i(S9Ni?6xzz2~KVD>XJ8GI-cQ%*QE zlOU{7W=u?=DALR)fKlp$7uu9$9iX~gC2|pND4nHQ!-*MDMj6B>(rZxSB_IKL+37e9 zF-^cAKslV9(h?Hl_xO*bU*$X#aB~yS1k5u51EiW~0w&{v+kVLOq|JyHJle*1CSZIn za02%9^bX|5CPZ2X$4BPKF!S5->4?ZfDMev&Z{OQ8V++%}*3RCZ*?^P)V;Q&J(s069 z8Wj3i_(p~XI@(&gR--%`mKsB3#@nK?N* zxw*M)JkJCSpdZ3x=;xV$X2Pt8(UbIg$yZ-+HYmt z&oXO@*){(=6F9dv@l3!ud4(tfBvy)UiG{hD?s`?F@ngqNkeg)|86BULlAMy3k;&wp zJ=IU_L-m&_$d4N{cH9I-ZU4Zqs5h~3@rlV2oKQMDyX!pcFCLzwFmBwKF=HplZFlfM zhiGH~#&P4NogFcU)gd1{X3Usz6IPo$dJzcEn>R6#1OEkakd_xKXZ@fwe#}^0VC8FD zH{T%Y067}u5=q1J#WPeD#*ZI2UT*74D_3u3U`KOu%)v7Ovtt4sFLC;02L;ML;(qz{ zm2`?M`k@YACG-%)iz%v2M9#4Q4HA+eo4HhiCCGHZ#D1?)d zn2=>m;HUuWi8M5LuCsO7e1q1GmUi&HB0P!D8RI$cU#HK{yY|nKlUJH-(1EkM2t)~W zIIOXUA1n)fetS30U#vP~qLRX_I53^m6EG_h z>=f74nmX1t;?(PMQe)kd-cHhfGLq`q`q0u+5te8DEHv8m=Dx!lwmtU>XlSN#Yn}-> z2;QQ~MDMU@FVkzQB16qC>|4Ki&-NJ`qe89p59y=MUM!YW#d;V$bntkhUlZ!|^x)oY zyS8nkNvyZ^^W%p%uG@H4Tj$BE$4|^{oZK+HD&E&7JjTcV!gFVy3AjvHkeij6&NBha zILTO09!7dwm9W0Mzs5v;Vra9`vxeRtS~IaElJBCkOHy6^!n&?GIq31iN$cj`?#CH` zAb{YbLu9T~Tvaa&NH;iY8-8iQ%uxz6S0B7mEkd1hH3~!|VrjGS&zr2C=`R^QiDv@l znSk%yy>mzZ-u)-fUmKfS+dI+8qrFYsR+W+(@9*y7>gH@~X=-d@4j2-k40!pH9HsH1 z=7t&}vajRgVj_b5yn#O88xR;25*p6VE^RHXgzrK1g*ln2iSZ1CGPM75+ka(6Nnu`gW=1+1R*AoHAxiWizaBdx(PQ7hjt4Md ziRqWKG3XD0hXH3Obok2j2RZv2=!F6YoZfsZr{Q=L*eNgp&jc)OD-|`>WcYfRc}D<7 zCf-OtCCbMqI3_72Ej=wYvqC)BF0B&@%7vlUe&ONa&n&}2BIB}zU{4`?qq?H9^W&#> zQFmQMYPhv$XxL*rzc-0_)lDcfh0m4Bn42ZPb&I4u^=08UmOeodG1&!zY7u#2S$GHT zx(=QRm}dfpf)m|V8S~W3au7(nxor>nFJtnCN0E{o*c$M^=|AK$DX0GAzWjE5py9MK z@JzthZrwBXPAzj>;@T!6hl>njGQ6B0Ue$Q+;-Yoe%)>vfxVjEOLrVSt zOjU^}Eg&h|$KlGM;}#CC-Z9A;!aA%ET!Zhprn*TG?H%TKZ9C5dynQGIhn{h2oDZFr zQl1Hzpmg9tg^PNqY(}>KBUu^d2E;rQu%s(Q^XjF&8`rGexNYAt?Te=lYHIzmeA%Mu zO54mGJp#fyoTjh3b!gMZtvhz_-FN8NnaeuIw;kNMY5BA%N;^z#99(bA+!bh{cl(Kz zlZ&&Xot5#U%eq=RzwF(#e#s9L55Y{F$1?$A?_f<##xU8Jgvr(DsdxL}L^&l2F3RNG zH=YTYWyjN20Cv49X4i8TJZIsH$oQkcKDhi6Qwu%tLYmm?}1{Ljz7{R%4Hj;6}I#F%h@FAq2O zgffzY0=2qE^mlOSetb93(@|fR5fvK-DqUAM??N2KNM2Ll^Iw1e{OkLH{;rm4VNP;% zSb&eGo4ad#5r7^cu4{b%k3T;k+tt=ojbhCR@F076d3f4Ae_>=|YJo#l zqX<}?NQ7u@z|D=P1Gu-hkL%0luV_4y?dzLcT07uLYOSphX2wN@1qTKNx*Hi9o0yte zSmWuaZ2-y-j&kqRVK!ap)vDECu4~B_I-PM|hr* zf66ed1SKUkfCHsQ@ZI& zteEM4RZY|#3#VJV*DqEWGZIX~-wz))Z1~7=N-u;t*_m`ZYTO>WdEeJmpREWc;t`<9 z#rP4U#~(^hPKd84Evu}uHuDNIJg{u4+=x-bFcx3KMvNT0ChSdAWNB$xRmtr;_Fj+H z&z?AXI8pJE7-Ys{=GzDO1t1NfD(~EWt;@SMs49*l@pr7t$dO}J9y&QVmXuXg=4)N9-gXgQtOdF!^G-rakJTn93>bAaldo|cpl6YS+=4Jfi# zuM7=e6|z)rHa-X7;c2PK2mnNf_<1-xIsk~p7J5xn6y;OUKPM*}M88Ri32`yue%>&H z+}(gE&Sv15fCmS8Cg5cYW~!)6`$0uz#tgMYq#&VzsrX%w)dT$#o40OUx@4}3s><}~ zGiIpHP*2FsFDw=aL9aac!Rhk#9f#L1UA|)GY*kg287ebo{%|Zl6)DI9AyF{DYk6{A zbC>#$^B2yWJ!`h=3{}uJ8Krt1Wdta_%RUn zYpko~XoT?!Moq?T-3f0Si$CL_jzm3FmvBuc$592LH1dNW2^BeN-QB&c5t$~&gZ2XX z)Chk=-UPX*yGusGOMIOqr<`qEfePtKKE#9ys2pkxN{7C&zVL`L9#taglr<8;>%w|a zE1#ev0E$ zb}L2tkqg5#m=X6y7yXA%wHFNMv~sY?X@+lB2%ZVJwVA688|;nJ-tzORS#xGg zQBu7aBk7^f3B)j>W>z{j*kE#K$IfLdW>25?!^DYl$_IpfNHK;VyS|nR%ifAD&z;z= zzLsYKo;r2v9if@cD51(P5aJ?Yzl9&vq6n6I<3o{pyGv5VdvSXBW9 z)XFgh_Pu*QC@xR&bFqGQ{nXLJ$Isr(=*C*x1}lpYCZvP!J`9M8;=CNp?q5EBHD#ObpIz9zJ~N@aYG+%^chVtt7o4-~I7tUAnKG+3Q=U zj_^#td-k8yvvKzH3l58n;c_@dX+a(~kFT6LaqPgZ?K^(aIQP`b!2|LL6d5APQz9+* zcQAi)Rr~l!jXk@5IiPjN)Rt!gMyQWx0;Z=!BIzC+?C)$SN{e!Rc~$%TMf+ydqyni2 zW)jo?u8+UI@0ZkMCxyE{yA1u;aVOvx1|`Pi@BaAX_kp(3l;|Lb2Rg?!j-R=jDj_Qi zXDrhHuHSzD{98|BQ9`({)xERFHIAOrwx}oj3kDwPf8TGv{`rsYx}4}hZ?ijRjvmp_ zIC&?%4o=s~igMEbfj>Y0*FU?elS6!YCSaZkxU__40;YoySJ_1Ag-|iDIHr0(zNDq4 zb!KqDvb{}du{O3qG`$s6UL4lH9`#G2h>L> z0iYZ~pir^R^h*!2YGJt&L@$%$0LB_6V3Kc}F$W zX=!8c$TI;G)9;Y}lX?T#jsqCtnSgf|)Kyi33r)z@Cuxm?O`z$$1A8|woikZceuCmu zRl^2Qy5p=ya&c#xiEFOs^V<{riQ(R^&h|D|b`d-iu#b-~L1wi8X&>Pp7_PNd zCHa{tNWzN@59OJF=@5Y|0&1v0*9BOzFgMY?sN1LTNl? zO^nxrxx`tA%JLy5Q4*u(kP@C~3LG4`4w#~p%9WU$#PGIr9oQw*A|HyYKt;rg5B+(8(g{OP+CV>(qH8~6EM#N%pklWcT=VYZ*HgH@l$yr14ePsonDiofedM}?g_tfO1 zM97eoj|6>6<3~O@F%Tea7%9WZC`LH|PVBPka5}gX!3f|cprR-TApH<>Vx9?DBx(~0 z;{CmY;tSha8|YpU3yYvw#Pq+pskI^U`sQ^D=ggeH`es}`Kt74c0ditA!qU~6r@wc} z>}iwa!JQFa_1ni3%g%7`f8j!Zs*OZkM<)tURi9iydt1B=S?43Ql{ZOJX_~F9< z_}0pVMMb%3sqsWh`ptu48f&s!68P$gl zRL0QX-vd_-z~6wPg+8dFOH52iNKEWTjv|CS6R<3ffV@G9CGbqZgp&eGiim=n$^{t- zF>k^n16>@bKu9lMyKN>Di=F_ntXQxjt%A|irZZH->PG`My_Tl?(U^LmbX z#f4p6@B!8qrKKbQxj)F&-pJ^=zV5}dXHIFIIeq$BDmX_Pkp7ULml=cI#MRa6^|QMM z7td&)JaOX0$&)7?6T4b#dOE6#bCdnN{asvLEDWDLG`Oa7`o!^L$BrJ=Gz^tW+q*hy z^U~v9TX;Bndsv&ke0WFq!YNISqew+MX6Orq|DK-4?6d?oFMmK2SXvnxT)TAU7!nmV zG>)D&b?uTA_DbrDGhzbW+&#S=Enhsiql>F+Y8>X7fU!Iz$@VM+#{kqvajoi#Qb7TX zZ^Ge27BYjW#LB`F^BFovEe?;!@}aW9oSbZmkdgPDN&_fEnL=B5teEvlSiU4=;W-w4 z8|pwkxD)1(bSOH$Op)vt{M-_o=lO?vxHZW+*N`4TYW90!XVu#DZ zCxRphzA?^+?=?(a`Kl}jqHTC70fY0+xz&sN$ zTv$94Fk@?Az6i=Q;F*AVCg3hu;O{^7wO1FkmFE_eWT(VNCB-<{`gvPhIC=Q^c6N4= z3I4VZJ4-`uSxG@^e3+|~SBRUnovVj$KoIttZYs3umx@~(s!FqiU0gf^!~I=dJbeQ~ zBBG;-)s2k!zHV^0)mIA;c1?{(>?4+Fl9G~=vAux^4u!PX2qls>fGC3K69D>oIoa7+ zXl%P<8yY2|wo&>Dxqpf2v#78jFRzU*z#1aK5?BG00RxT*kb%N~Eht8YBlb#cgV->E z(8|j5U??ybGr0nf&_jWaq>L&=Jy3v_rU zV9C2*+W}kl_~t!(|K#kVlFItV`ueKMWLKl>hYp^3U@GZ+*OlxWo9Jm{^wiqV+W$>r zR#{$Zu!WJ~#RHn>?ir)BPMT%zl#&)3;^X4x8xtB8;pc8-`cnU@me#2&4@|_J-Ho-m zsactYUd}<54%WV|7B8Hi8(uoEbMC^0TQ4j+B+}06iio_ZKok2Qr`P6IHaD)`(bqeB zQs?r;8_&#aK+`Mj>S)Lhad;7G`{cFls|UI|w{IQQxO7!l@42a!Ee@QdJ&i^2Z=$`Q z+S!>rGPtRI>Vn>lTMrCPt?Zm&Im2S*nSfdH4#mlajt~@NLl_SF!B!Ts?wJ|SQuv@- zT#{ckW!Q5I5IR7WICue4K9+DX_|k#ZXUNWk_CI!`0Z(9Fz<5t@?5keAQ3V zO-gv-k_ct*;LCVgA4&dK{jVRAvl`_u9gwO-e`!Z@HmCnmhJPTNf{tRG{_{-0JQFa_ z1PrAj1?RR&&Qf9e%$yQD6EHp(o(Y&|0w%B)YCID#tqW8xK&hqpqD`3=5N`dz77RJ_ zHbO?dn7@z{vR4K}!a7jB0M7&r+Y|^caI;%GMVwo?V4jk^ys~aJ@D@SWCIn6wEk6+V z1Jg|$&xc?Iwy^Gv`|@vsZ&WKM`vIDI#z+~24=b<-nDH=O-LBV&`& z$n@kMKPn0pi5d&Te8R)Qz!DRml$M#D%Zyjnh5X&*rUyxDMM*L7~~m|WKQAI~^0 zK#z1wGg8u!y?O3d2a7%6m?CNi7#o|JaF2K<-~;m}DJo1DFQ=fe;e`cZ>;xeQPU{by@LIwS zs?VP~QC@DmoV>z{`-WCdZUEs44uKqXzJS?idbVcK-08|FUyxT=&Om(J0OAvj&zFvV zX!TE5E?O{ENl8v#af99y6B|btcOM^rlFxqSfvx~cTU^vM+A^-}3iP9c#pEqN}t!L(TPA(q)q0v|$ zzH}hXnA4;9Cx;Tet)C-w$1C0pSEj){ZauzXaBBJLH7gfuK74ZXX(%>PsBb}tjhmO{gY)VEffkn!tlzY2 z`+}{3fp*VN7)Hm$VQ&@qm>JwMvU*@p9N_Rw^We4}TeqB#33ss3y%Qew2AW-;?5J;* z>Sybd8{lYi=GZSgw;b0t^K`JhZ5|R91>LWV^EAp#^RhOJ_Hi)1sJ(?}0?x|J%*e>d zM9bnhJ~*QSWwAUHaH6=Y$LW^-BgT#l{ifpy_NTV$(#Ecyj*@VT;wIylKu<(<9Hmr1 zOj``k1nlzS#O5tl7LWBdZP>29LhHn3YwYmBVc0+0(u2y1LY-c0-E{TgHNE4@)~xwa z<;dd)S8qFc`}0h|WZyRx7R8&tbh3W=;+3%pnwhz|m8}D_U^u%T@PmSav}hz22Ksrq zyEwbJxOsR1WF7li~2kCE5;L5o-pKnthK2mw$)Bg51`5QkD=b3gk&ggI6Nq9f$RJ9>DPBs zQC&lc;7we5Q60P^G&hlCko@C6en%2^r>L>KswyeiJ3O_Zw5+TQ5l+N7A@2Y0e|>)2 zBWZ0Fi<-;wGjkK-6JpW}3X4FHAP^$K;*bCN&>*O4Xl`f%-&0FXNlpTib>q`BvvUB2 z-_g~VPSb)b8CA?OLco&U1EG{WO#H!QY!k0yW7J`T1&Fx-y~*Kw03s2)wf6* zGqaO|Ok9vgl9%{2u_C?ytuDgRH!LbFA|^R4(%0^l!L@TIY@!pA(=xJh zJH*oNx*r>6K}i`|F+Sl* zflmw{U)*)o!#616O;^U+wXco!u3pjQnSi09oR_vvIxXQoHVdK<3AiYSPX+!O{~kfb@cHvA1bzIo(Xu) zA$#AD=(t3l2^hMBMlO7bm?CJ1!`0Wmd3jcI+jM#PB}8k(b(xLxg{s=~Aw@ECidNY};1j+|sQ zkhe=efhHMLy$$90$x)$x-kxr*=wW4PWn)M3Hc8)y_XFLXqPp^&goqG7FBJK@xjLGd znp;@cG&D7}@Jztqi^ho?$Kq09A^4-yQsN`S!1L+vCoC>EItUtoKg!Fw?{su7K@N(rm;gj@(#BbvUeJ-gP(4T;AQxaX!HXbgg}6fT zBN}?J1Y>j{IsJwRuQ3zXxE|aLvLO)HvXqEgmfuSsJ}CbciW! zsH1C?0fys?ytXXH;o05G+Q;_q+@WQb-9RuL1k=IZ71)}E0T#wD9$Y`8d0^)j^>v5c zYHAovM=?|X>Y9=$9~aZt_imoi+_!t%y0vS!zN(-VLQuryHI>O>ejesVk91Fhs(0(! z)vMNQdWlj^0Isn4Ym3qof`hCK@9Ca7ykmp%h3zE!1@DaEuPhKf#dyqm(CtLxMSnG)vH#jZ{D(X?+7C5zJJFi^>u61H*Wc9&oS)_*KXf?#2#`aW0eSkpJM*q+qZ1py6xvZ2ajuCx^`Rt z!DHwEk?o=hvpnzXXzT&c$dS|MFJ8KOQ_tYRqo-fQMA?}c<85E|#B?l47R+ z4Yp@4Y8+lVbF$oMF!?f~-cb`@Rg;`Cee+DftJf``swgi{OvB(B{{DL~AX7mXQZD{D`-jPZP@OPgJctUwG_dzD>A0Ynbh8K< zmu5TXPM@MYQAv5y)akQ++`doaxb{We>%|1h1>ml{+=93l`_$&nm^o{{+LnDsj-5Dt z?h?-g+$nkc7EEpM=>kXvjehPUazT#w5T`=+J5Kmb4-m6~8j;+(koONT92Qw8HbqZQ zA91_k(23s?iVG3aqH$B8OaEIGb&BEp#cu{>jb{SpnSh&W%YnX3(I47bv9{0?gA(Ar z9>7@;KQ6!|nE^^>D}|U4OCT{GT2Q;eNlDeCC@`ZGUuGM#$B{kNJ?KC^C>+m9eW{eU zhD5A^auZJ^1v7XiU_@~^VpEBj@sJXYv8*xXQ)InUYCL^b#6*k*<7;CbI+4!H#9$&O zu4Huh!iP-ErCnV;kWY!!rSY(SOo? zC@^Vv57`Z*_E?B1sh^eyVk(A$K`vWl852387TsJeKs&~?7)A3L`@f zziBImaq__rV$%Pv!9m$Y*!m!niQm4jwmrFV?T@Q})=lesOEtxm8jmm&C+C@fcW+!a zXS&L?$&)54D=AM@oS|!M?-4*Kdh`LmFT8kk=l{#zTgOM0Y-_{k%rJu!Apr&r5_E7M z+?|93x8NEqkRSJkP=fv#0+sY0@O+i3&=pnoo>uT)g~)LqX(=uWum#$)TNT!dYYHqc=?6LRyie3w=&fz!thH%_{9^ zsVhtg_i=HHs%1=o98)ite3=;AacfyhsEeuItt&cV?VSYf!VEr6+}+{KjOmwW0>(-QY=`>FJYO^2`!~+2?b~kgAN!YA&uAPyuxsa@{fAE6v~oj-@aR~2-a4h_>CxU+Pp_QPP(QQ>6YxyH zKp4u0Lohps3KP+&ya@mwi2IR~ob&ocoF zGlQLNOdn|-JACBuVc`D+1c!htG?LV_>)n8~y)o6-&P@OAd9?!v4<9*p?lDlbhKdBc zx}|MRr7`Xn;5ayQnAQi)^G_U{J*fv94|J^ByhvA*XWG|JAKHK5z~SQ;o&urA)yvnP z)VoU}YOKtRa(k(B`^xDf2lgL0eB$y8Q*?0g^r8KztFxuHBt6W@;KA+dJQFa_1kCn- zI=v%dM7IC)Ou()uw=P>SOL6>|F>+*K-vG*N2G0a+jWZrH{=~lfHmqJWeUgHr+&HRHjavJaOX8`78Eayz@x+#Vg~tHnvFnZX-O4 zwusAnckkY{T6M{~Jr^H5(R+b%pEuUFc7!5C2)&}VrV>F}ak8JA6S5PK|KN;DA6GYb zBr}Rw;*wC*+)xkX-J;CI=tv;^hld6RVgY4?DetKnnNti}prjx>jiPvo@ljDx(Rj$Y ztS96>!QN+i?`1`K*%|3+>B$L+No;=ytQEKa^Gv|Fc{~&F-5Y0)U+~M|nSgzK{BZmL zh$jV++OoWjj4fiM&$sw_||X(EOeHXhD|CO@Cz_!wIY{c9ICt4^OhWrpe-Ax&RV z0WXSJ+*#}zT$$`@Wc)}=eeK*S@PSP|QObZ|ODKU;EKPDt>niXveSP)FPxGfLDJUsS z+!QaMLtY+bc#1p1!Uf$$5#~=dHm*>aq$sDPpt#gCCxf8mSz>!zcv@LkbI`ME``0g9 z@Pnd)lKjMlmhrJvCki?Ch&Y}JxGC-BsmA3S>X%7tqWb@h$REUawp90(qhs?M4k zg*+25Jcm>m&qAOzc)XECCo{Img~u}iAKkfr;S|8wC{O2^fbSuH@6B6t7_KmWa~@tk zwQt?*sgvc$jvh5?oZO@to9;Y%4iot;0kSr?G+SIfy?_0}smgL=(P5n8)VT+4+(idd z$f10q_WTEG+gHs&4$p+~fI0hN&We*)Za;YX(#Q-Y11&;OaYminvTo7rsgslx6eiAC zyy~#l^?N$c3|<*wcJi2uM8TRHH!NSYXvvRD*KFN-=;HOe4|SgD8@ysHkEG$EjMwLO z?1t}>X95O(nE*B1O+-;06tE% zowcaHpcLa`A~6+;TCvIZzwhg5YpN7v=T7PvbVGLOa+-$GS38zuN#Hn9UXuQ7uHu4WhF)g`g(bKczAev zd3*Crz>L5bpoI*#kVtl^VgMWllp9Tr6M^n_r~|?aNY4jABG-e-p+u|$&jidf0c)K( zdHlqw(!# zQdNl#*grWjC2!IygCHl>9U=FgZQyjEfsrU{tOHK&SjVcs$cFu&#xo(T$ksMG@JQJ|e*pZ`AL4gv8QDa6-)qi7Q*jTpk9_jtzjA8m^xC;U=&|FST8BkmjCF{Y>&_;Pqlx|EuVB`vRbE$ij zI>Z{}!X}a;i+BKK1bvdW`m%zeDv_A0T$jnadiw@H{rqmAuS?uqR+5#Go}5$NE|s+7 z(;?>q5Z!$rKmYAxKT^Qk;Popl%1Mohh%2baKMqOYHNczs<h%gP141ONEXKY#l; z(AR@tcx!!CNl8I!WQd=qo0GGXBhLiPGXYcL9hW+XY!={S!=VB$GNKR14pu6Ff0f3+XLFyae+FZaiR-GB?<78)T{>J2${___vjZIC>t?V3K+`VY` zf%1q!BUmLU$WBj+iwp}14h--|{GM{8XurYk1r@?}0JW;5PRU6L3Ba1;nSgmF;Lh&e zO#MseHI5uQdgSQ7?c3HY|8c>*xpNk5z2})lF2Jsy;@6Mv@=U;|c_v^SVRqf8tooB^bFy$fO@F6Q; z;+cSLZR?xbGrcW>%D^2~*MLwYgSbRWQ*(~B(QR!r8_9Fr-|geF>RH1YwQrFF2Q}j3=Kv zcXLN@?r=^WYLG09Tz{1D;0t93a&{=E(>+ZhhvoOrAZ}e{EuBZfWP_?(G*C5=K?3o%HOv=#Az~UflZBRaRp*lR4-GN0S zFRws?t5YAW0A!P5g&=}1@cFO+R7nXdl!eoc8b=Yx6j&CA7nG32h>u4;H>T+n2BQU{ zmg@ZA0c0jA1Nfy%2U>x+3s4xWy0WZn6n#3fE;s{~b0yLs^81lDkdt$r(2?^c$}Wn< zhl+S%(+-)hG|`Z^5llO1%mK+w&R)NCpnuQ5VFHzPfQgNNP;2X#q3pAD<9F5ruWiKT zGbATZPgl1%vzLJbasEy*F2Knl1(rzPK>r&(LxP>>taxPfa7=&z73t{}_qJ8Lox9*n zV>vl_`7xen0_K^3hrl9uCSY0@SP+0-WJUbL>&H+SfQ%Nd*MHN0E}%eT|GoYbbO0JA z_?`(oxPRLM6-5Pk)uawUVb^o`gjg==;ye>@dwWaRzM0cNMLJ$?ufDmH7idO9 z!=vzR(pLZry}h-qg%=xSz2pvL1w0B9niu|KvP_UZAlK|4skN2S;Zm zPCUGS<4cw`x(WsFk^GG|^e&zOYCIFLyn@22fW-8y45aX;WoEJUMJ#PQfA#UTjkA>G zCxD1|<_k|BKx{?FpbCh|C0(UQUV1%RF;Pi={DcW|6E$oc-NDiu5*869frqkF+*xUC zdXZ-WM!_#0?Z(=Q;sPAtvvYEC!2<(xlbY<6AaoRUX`ZkN z>ctR2zRZukl5JVEGVn~m=1qNlQk*bwCS!*esid*V-Cx*Vn4Rc&S~?ZbNIDSaB~}ZbFhd)-d>Op8s=zcXl+{%ZG8KQ=2nwuda1QU zRfaqZB<|ECIvC!4;^zFu)YklwiT<6dC!f07nMVNswxY61ED`3$I$XQ1V;$h|R`2%J zC)(FeX}E>k80lx_<>eO@OWUi{!rbhhze@45eyXW<9w@#sB0!fc-*D$9RD7 zs5J^%K~!wKc50Y{s{2%ZV}{0*(sr_Y@^u5tX-NwuR_pBh=% zIlE!U7Psg61c#d4yLI#CtvlNH?%jX*So`W712f7IB00|ljH5Ri$ix_SFFa^4b8>O| zhJMoNni`%n@~csDEi9@INzM5dLN{q9tDc6^+hk**7yaBHb-#3W==S$<JeBwk%I-{=X&kJPKVttl}!PFF+o0)^L^96o&6^G$ttCSU~mt1ASBq|2O+ z(<}W(DokN|$`mLQuy?+C!S_K@md8^KxL;1cxnVRCjq2{XG$EA1vbQMzM<%4Pkg_BJ zs^*8uO(rrTUbRqV+N>M?k#UJB=~*2Tai;T$1G_fwIih*`+_{tc zPipS}als-Lo(cHekIHh2`#YHSGyTUQ4QDc*2^jiGc{T_VoWrtoWcW3JZ&jehM3nEAs3aM$3 z^!0UjG}jhq#>b?U)gpzGyrn!7aBYLI<+ne6`|X$aecf#h)rF~1VW8@Db8>QYjf#ni ztO1p8+aG^`%J+S*6jZ#qDN!N*-kz>NvUTze3Jk2N7efBquRIg5xV5fQkQo;l8Wa#1 z;AU)SVrurr+zJn0T_g5z0&8rp7G%dq1L8N>!}86Ww{PE?TUrwjWnB}u$pZ7 zab9*>VoX?|pRYI2iYSUi3cxb~^Gv{xZywj&w`C2ua96BYv2yLH==j)JutL;Sr=}GY z#aTYTfAQGS-9N2ay?ps{bXj-EJ3K6m9-pds1f*Q7b#G{4`1+M3UA|)Fx;<~byu2zZ zt81$wd>w2pUfjEJMs4TDHA|N+T?YB8^}7vhZS6633@{vrf1)~X4Ehbk1XLze+GtOinI0&k$~<@!d%LeUq88B?irGCB1b@%^X? zQ?w8<2M>2y)!U`3c_!eT+}tFeH@f#985kQreW-m```&%T5(|sb3Ka}|_uGZYqr6eu?@Cleh~Qd5!>qk_Q|?&JLp(V&8gkt5OsR89{#2`Q~CB;v}@_4*q=KE40Y&dnPa&YwMf>a^)9v!J>9*Oi~0;_M}M) zY64aRS_=6Q$<52B@lxXl_l~VuziN)kR3&Aa zUs+jcseg2ALULLLt^I>mXD|Jbp0tSwH6o)wv3a6BQNZ=S0;Z!5k<;WLkCm7(US0wqxD$ zWizHJDaZq3Q(5j{eqjNM%nKkN?67_F`qY+98yC!(2JM%ZQ&3b?oa>vIn4FxNPUAaG zA3xGuwRVNdj47C00auu)xX8&bI4m+Yp4O*#mS-+)+_P-BlPkgD!tXf3ax}iQ$5I*`QX$$~SwNnk<_S>yYdL%1c_v_5 z0FZda>=d2}m`+)}pWc7|qdwio_KlJDnPW$es;eEp8N}6qRO4=S_WV5X@pDbQyT$9L z*H0cfbVTj2x|Xc~4KNWH-`UyUIoKx%b9}A$=#s{vgGY`YI(ptDD<=mAKpx4(U7eD~ z!a!>S-Fp{~9XN34(80qO43kocNQ`O(cqU-nWJ!Uu;j^c@cg~zRf9sX4vzr$~35kfr zAsNpr!gK8+sB=knR1k9ife(oM>nK!V#Ub~JP&y@4_tsE{m^@GfQj(KX&%KgCBP9~>dD;Iw4=9z$bCSaRakM7*PsBuj5 z+#Oy0*Kfd5;Oyo>84g&2rIPXx2Xj;7r`IoDx}$FloDeIJ7`VE7;EW9Yqy<2P{k$wE zCfMK4*T;)EM7(|c`~#r5z!5^y6Y5%vS8alDj=z{biatI+rNZ22s_|Atz zFe^Qc$yq@?>H-O;6s~o4G!)Ppl9z+$p98(7T!gP1{lhSr)~~PM{_->T$-frkE_~gY z8$UFFgj{2}40!ur&jd^hz_9)|!Nwp2@S5s!z>pz9CTj#! zH<@YzCL9oZ$Pp$WaTd6%Ap?`tk?b6@bjXfDG6L6uCW2lMH}t^9N;yunm{H)brg}KT z32IN1z|>(gz`sR-!74U4Eg* z+EShgIKbj5&jkF`%+l5gM!Jt5jz2sTFv~55S|UA|um=GDMaM(35^_oHXfLMS7={Kx zY{HJH$2viDIkh#M{7Vn=T|##or~}D|lplIbJ35vcXfMq$B!_jx^q01M)&W*Gyq}z$ z)`u?&4l@g31**NL326QrtPex_-__MrQj(D>Xc6PAMBZ`8Y5m~j(ym<7i~F~)+jjoB zXJvnVo(cH1AK_d^urIzn(k&y@-{QpneLtxv zO_Z0HpE!N#D;HOHPai)Rz-?{fh*t*M_pYp7x^DSYc?EEFD$iVR2>Te-?zF$OcjV~a zx}v#l(fkE7<;G1IGe&OmtX&4CfYNhygJp}SN@#QYisqJ83uY*elS3(DLH*M9p_xSm1GSclJBoItw&GJmZ_4TB& zC=B9g{OFL7Go;@P`I=_}-n4Z36lJC9`x2Uvv{a3w6kA`Uow3gxOmCjynSdujyGM_c zn>b??&jjpFjS~K`u>!ZJv7x%Qy0o-7FFi3fDk2;-U%^2E{@BZ?!Jrj3;*kRNLrGCC z1NuPzbworslT(cj5_~Du4diy1BR?yh<@&@#;eCqKBVOOsfHXPq%Q6R zFcLyq-;gsik=pKL zZ)@-BjShIa;o<4~)FTnq;Pe(7?hm<(qnVk7jgyD3UmzZT;Os~TdfFRHvoevn=I7(? z>Gt}CiG?kYdVRbhZ-Wgh?P{$l%1Vuo0!fdb$D7w?aL~bj=j9DZF1kR6R3a?PPfv`A z2@m$Pv9z(ZcXV=*$$2JVRyGKm9OeNX56FjD3KViWBv8$utPMF2cqU-nDBJ|b1P-WA zR#pvq@T%q2= z2|1be1QeN+2pKZ!F_^q*RRAU=3#70RDZ|JpMx=o`2kDeWb_7BM=!V|-Bo%moq5p(B zz|L57kb;?kAOy?|rprvyylw;L+c~!j{{gQO9_Q3kzs@5sCzlPh35)WagBKQ{_jE96f5h-1^`Gq>KY< zp5#qUw$JWeIIwu?0!Wxqa>kVD(jxSQ= znScwjlOqGX+?*Zk?dYy>CV0fgVTHyA`N{Np`nP7mwpP!$vuWudWU9JKLPRaGEsLjVlYOo9SreQ05TjHK1z$Y$IhB_+TM zgmIr37ZVLX7#b-z(_6v?C@C(%A3Ps)LTH4Wj5?rU5~wZZjg{h0Nl^hNN&TKYcg(*B zR}X9C(xH$d)RoYBW$`+OLL%$R$pJh1{XynUr40sRYLl!DtdrHYDVv`>F$V z5fex^+>PWNdL=n;{X=|#h7m2~T$CfGr zJcsE)5h6$>O@i!{@IZHaThHWL2|$3cMv=S`xK$V{ECKsVWRRzY*^B2d3|z8Wqzv!^ zfeH#RN$SdS6Juk;{5>4)OOtXM`Zc1vcH^cJc#tTq679zJg?LP+dcMqX2=)Fo@F)x0gP}R zs0Vif<;FrRi_Wpw8M6keSr=CF#|nGcB7$WE>9^c&jgQsV2ECD${2)As6rD~fx0}nh z{Pu2;B(u0ZJ_sWG_>s%rr8)4i+bA7R0&0z+{h!PLN~vPVpU^S3Zgq7dy8vB=dm$}} zhX7)w#Ou*j$ zUbatfozdLA=kTsAo7b*dxp>~ZIdf*K%$~jYr1n!uSDuHB{yi<82^hN#&jgG>B%M9T z7eRRjP$5|Y0jWOIYpi2*ZeSjR{}U4^&jjr1)BT_S^Rpy7GA6IIvZlUC)P{vs+SC93 zQ(tv*ke!2_XV*Xe*Y6!I4e8Obxkc6WP4L%u^$rdWimP)Ytj(>g+$~H%l`|FKOSvVE6luez8bUC9EpK2{|S< zB_-b1)yvDu%)!G)$}<5o6M zLkFT4qcy0dT5bBPaG6}zfy87VF0*7fq z9WX_g|3Pyb8);LOw42PEp$|&c325l{V84s4O>>xCv_RBJ?h@jprO!`E=MqUvoS~K} z&jgH{f%`9MDvFPe@_J!wYx-2@?)fv9@88kZHAEhuBX_r?(zd#yq#!rj*Ei3dzkF9$ zPZtp3HjZxI{wzTs-yzQgOwTZ$->-LH95rbF{rC zPMkty+c!BUhxczTjY%E-A0FQz^ zW`@X2)&u1IJ$9M>wFMaouGS{T;X>pM04J6J(AfCizIQoh1%sV61<~&222X7Y%PSBq z76^c`MRE+6x(g)I)~Zy0Yoo`H4dZfh3mAM zq@W-V;TJe523sONz3;Be^tCg5^5DTki|E9(%$%H@+}vC?o@W9kFa&Bm6ELj{IHjXr zfNBPa37k0tp$BL(`v95-g%_z5a+BBJ7xQj$~BxFUWKsMb8U3wg9uUT*B@F=NLo zoc9X|jf{?si%(1@CQzxgyWZXIirO6cv13P%9y5Nzc6)bpU`6a~e)8+asI7;5%;?dh z$Btk9*1^+1gi+Z+&RoM?EiYH9{Gd2)^cY-VrIC%RPat)OVP8749UBc6&6p`aZrs>$ z6Slsxbn&7Rgh(*`qS1QEGXXQjAk`t?G6U=XipiG~4xc`UI}E))JeVC$DYnHk0i$M6 z#w5&Q7n08I&bt@2J6Hq|#}pA+eeCh0z=otrQkJMQe}T5ROH2kKQ=FvJfwMXhH`e1zB>f?lg+AZ?KdCO7 zIb))tyh>avC3II+RHD)T50nk5WY;O@?Q>OTD=R1{T`4auD+3k;?cr>^Ske=qyus}7 zl37zG@=U;riu<*no7+19i6bD4?mUav(8;e=l=f`yWTXcB&>Y1iDvrK!BrwHhqeL zH4B-XvjQN1D}d`G=@6hsA039}G@3aI$Y&t!Yk0kWaYhLBYYLW~XrX)n9kcur2! zvdc@_$k*1>k#jPwi%>L)36%Pi87>7w7b_=-@vewD_CYzL)uW#9{Hc=j%p!r|qoHA2CaqZP+GIW~h7c`PEHD+r>M@Mh0PA4bl#*mW{N-hyo&VZ!1 zJ#Altl*us+|6|eNnSf31ojiHv%(|^S6Y#~$=hQDgK^d~WE7UtP)XmB+!11BRxeKTF z?Ax1n>~Kc`VGr8mMTt} zw^?oF+SlpnS-JUzfS0?zXwt|@lh+Ro9+!cL6vWiOV6$fXFWa}-5s5hukbpy_1vS;Q zn{#_G1)qmHAVG&vI5_!`m`rh`-O;h(YdH0>z|~c_ zJ7mDOG*_3V_yl|VB?yG@kI9x7XgE$95Q}PsHJOnC!S?329$7_HkoHqeBlI7`#eLnv zqSDgzh+roVw?`LG-!==*Eh$1J6i9H&F?-Li?lp-OW@YnC zz`J!U3d^d9$&P0Nrfm=nZbbOdDZC}D&xqQw2ETEkF4`xt88R{B^=q!ry0!Tw{SL<> zQE|%}X*R`vo9ZgEv-2SuYD^BZnaOE`<(Yu1tC;^*+Ljq;p?^*N)R{YWX-(}&0OrgO zXgDQjNooSE9gT0EK6Y%&s<}%w>`JK6Sf>9ZZc1`Bc=hbYnG^dD?p~rYb3VC-u#`GDQVce^9qWQLDnX4ytr)-&jgH$3Wk?e zTa#*j=j4q|vlYiIzJqKk@X*s5jh%~u#EFFUbxE&os_{&~9^2QxV8>;8p@JFKklI0w zX96am;k;ac1wakU%B!eInKcdo00;pDL<%NfnCg{P!~{rMjq)U%D>$elDpV$>U&eF_ z5iD?FW3`}IP*qn$I3*=0b>^9XK_lDz%Rhem{nw9!z2e5&lFX>kKp!u67e@z=q@=|7 zTAm5GNrW`vzTU1jVGW8k!@+~>>FMraYw*(8)XW@ts;G=WhfZm0V@+{Ze0Y#A2vEIU zUKzYLF?(YH$cl#M7Mu*lJQHvgS={so1%}XR0{JLGxgpz%fkHqTYxEZb8$l21KyrGA z6kMkNZ0lxIPzTu%(tidW3O-OmBEdyi5hLWXR@YFK;cH?1($GGptg)Utl%o)GSYB7o zGXe8V!0Nj;fCqWm@--VbZPB=VLt96$40{iDm=cF4kFQ=heq`5A>sGH?xqkDOt^3bt zUBCNC7l?S+O3KO$oF3gccjC~lP3zaKS^v|P9s7=-zkKW7G!-+uSqh;K)X7&+$0lyWe`Q`|g`>Y5aGi#vKKkRD30wR#tC3Lk$lvoigFO zk>6r0{(Sr0_hZ(C@=U-t1%zf-BFNBGT{wNJlH8b4$Wa_UcI*U&dAkp5oVk1h+DnFL zNy?QUXa6usemvT^@yO$vyJf!`={O*DU}qK;6y(0yJ$Kq%St_b4wj4Zm{N&k-S8v?9OFX`Xg^bCUX96BztiU`I@a$97 zJQHwmTv~Q+e!&21?}}faTfS=jBA^4zQdzM1qM?INSX@d*PEHOd@9PtXT-v*O)v~2) zcAe9EW9#Z49+Q}wi6R9i@9*pG>~6^q^KkKvj*E>5kBCc3%f#&Yg@rPCFT6ZnfEM`_5rzNm}acdwnNI&9-F^-nab3uQzuWEwMy;oD@$kZz_5s@7-Evf z*U{VIe`d?#MXK|D+@yI|&%o5i*^7!0V&WmEudB1OTa+8*>gW{_6&B#@8xR~G6-z0; z>BK}#G@O*hf~-|xZCN2B(Lw+St4Chm5EC}j3XpNZa|b*>axPITDF(6(hI1UXTznhQ zP^ixug0W=W){qmEFXOuxhHW zE(cI>J_^YU`pqe&|W$1cfA;ITw75L_G#3Tge|)(||eh?4M3z%0jsr3+FyA6F!Z z_Ygy3NxY=q1PH)N2@o}5B9fBgQw}0qsS(%XR%9wdmgWEA4g83LTVRUeRwH&uP@+Tg zN;_KW3RA*;T->5+0rUmncR&(gkCj~(+i`1IN~nve-mNP-VeM3QgAE#AIQOZ#`+FtA z;<#WpW1VYfFJE^Ay6NzET&;6put(gG6YAq+a$ieR^Y|4nOwZ?L?b)$w-=S0Yt-%ry z6dDnO+#2A-iP8eyt)E>#ck=k*J==F3P`~)X(%v2Na1jxFqf!LiGW<= z0-iGcMU0p+mmfCj3hOX$Is>d|?8mF9LWC;?8bP|5>&M z&jgHHDjod$=Rf`_DUI`Uvwd;x#8EZ%BkC8-b8@JLKNsT8zF$6m{<$SD(%sJD$>rlm zj;b9#dR9M*YJiiHN#66}!~1TY37BUB9#&TTKne&ArQP@Ix4-<%#6Xq%BL1TPtQR+a zXu!YfKfU`ea<&LlA%SfFrw+LM7yaj%fOoA~vhW9m@nhwblqWAWErVwo7AW+eX9AYy zUfs844bKD&;7o?_TU9PVa!odvKscswmZZqAxp*ev+PpYV_vHFk_;KK)sjj3$1uV#( z!C(IN%g4UXj{5XS2LnUbGGrUU&r?&yV2-}juwcQ<>Y_sp_dMAT|E zmBc~S)jjz4zyIU&ukQyt8%x4%Up;wv?_O{Vk*-#i!;{g~)Au*XfBQHvAZ^HWHPL%~ zTkDc>6?urtN+Ab=(C>f$$3K64-{0R}mgHsr^!}amXYOZJ0O`575C|Q;1MhzS^Pm6u z^SgmwQGSevg`W1U(w&h`rF~m?Cg5=Z5|dMyw)mmh8*@ur2f{mUZ4Z8?bN>7h)#)>MCg9BY@IWL9dwP0+ z2bgC97O;W`*$XXEuzHd4JH!JBCeeRs4fUws0jC*A(bzgRBXdFL&T=VFqH^`61HjD4h+Ba-Dc4;4c{jL|G){ znSfhsdpfF%bCZ3&{G45!%?a(8xSgpP2&0M-9g}su7 z;*6L8S2qtY2aA`w4{n@4by8DZjb{Q*;F*AVCgASgO#MseHI5uQdgSQ7?c3HY|8c>* zxpNk5z2})#g+;!rr}*`wyO*FaNZmWQb;HV)%NME6ojq4|!H?T@GYj*&+I{WK^=@4} zf9j})=D{7CSFKvMc;4LEbLPxhuyE1Y__VH4hhQK5+qW+tKdN?g*RD+~m(5>1YZfZ$ zX3v?Yy6|O8SB6JM@XLqyFYVudWbd9`o7b*fwshg#*(x(nTfwtpvvFHxPXqB4sBS;i4iRRz*2OZ{o~y%BUmuYB<0f@d8$Ic*;kz*=4gJ=Z~lozVPs(2W9TE z>(eBx19{1CZe{L%k|PUnxCfOu;h)M8`TkcXKyC)s;mZUxKi9YqK_B6(PcX#vM_pJ4 z*%fGlCY}k{*SB@>v!p7!823Xc0#*&06x8YcgCE5CX@2%R6EG~}Zq!lr_jih08>`E* zgPff`0>b=UoIQN}Q7ITj#&b97H2Y8`+}2Q2l9ic}8Xtx7A(kSYluXt&=-!Dyp4s0` zwH2ks1t<^7$&0%OfrXVYb%)w@f(}Jh3UrU?II%)60>d!TX5l!yZ^5gR@O|JU89_1A~L_KlU~kBm_IZdG+Yl z-B=C5fYOC9@I=Ay+htb20pHVKOpjLsQ?cK1AzwXV!2406IkvfwfTMnSi<4KzJJn6P;%Q#`8~=i+IS&-kO@KPXo=*ODWYiir{2{ zkAjvDQ1y296?;6~ziokvqJq3?Qin){VlEQd7`L&T*dBCLukpa&H%+AgD5(r-hnffVDewsOD(^Csq zAAc|r$0nuGW=3LiveEIosW8+#EDRLHG4Tv3j5)7Z7Y5`_F0_W4%2EX13JMAfi;CfY zqyOQDBlj1%r<#!yhCJ2EiZW`j$-qRxwo7ORDF->Q$je6lwW=x{P-H0ToS5^WAq^FF zIDBw*vUkyoc3d=Qm23jY>+88Uzr1pOKJE(tT~1Q+A;5{ie0*QV(`RQ=nw^dU^bzYD z8ps~~x}o%*J1Jlif-Cx3#{CTbP1b{vA#n0x`+*tAoC}ZA=3uL`6npLqV6W_6QgDtF z%b0q<>hQ%vrr}@7nQhWTkxqOLun9GbTG3-@BvcHoEv8r2f#f_BaC`z=ece*A<9auYRd9NoSBgF*naMNHtG;?7EA(~F1aDJ#g2A2&f>e&b7X!q^E63J#(1 zF5d&o}ggJSs7)gP=C$Bxzm*7(Bzjr)Hkzna`nVD z@clr6=v!)dv2x-3DT;~{;?}!QO>7*U zkzwc$G0!Eqp# z2yCPt{i&>1fB7 zR}uVtWfk@(%j&g%E@%deOOO{>5Mz1z>^<8s=QqzU zX}o-V>+B)l1j}cSqZ1PWge&Q2$O?6~(KpNuw$;_beT>`YQFKml=fRwlxinVA`M zIEBj(jh#fv{YQ;w0%kcGIKU$|BCc)__^0b=*o0l3KWn7?tkp-Z*NBj(S%XvyK$bR} z?A&Cj|7h_jWu6K6uYdh!#K@7O6|^=_ob>%(D{DtM&}`<6`gYv(AI5KWS@J#P-+cG& z=vg|4#*ZJp*v#A-c4YdU`QL2SSvzji+r=Xx|9AOWA3J^S#`BN$jo)-gt6$C-@z))zM*iimDl^B9nKbsBzm8B+ z96yF<0#2sJ&dadhc_v_<378$;q{uVr{N&mkED9+TicM_x07pI9Sn9l0Br<0lrZ9+nKNZ!!Mscw#V69 z&P69ul>n(ZyEk1ZChV;kWz=ipMQQ|Y;8>LH`%du`1mT8pfaPc z=Y4;Z(QP959QvoxK3U+(wp5xNHRq1b(Eat5K!tVX(ie zrACmG92M%1ayvJd_#!~LLR{bU;m<#Q`}yO$Ua6>BkR2Nq?C0(2>g*Jdot~NkHzdyl zERpuV>+kMpuB*sT3=i}HLYIe|>sv!&@~wkq#4`c2Xdwf>69A_$CmR642~iPYp}|2^ zEJU=rMDI(D`p zPHor5^=nqHShaeKLp7P~r2jS5*_nxMN?dUCo0pyv0c_v`khE;Euu3oonih`Wnci(=4hA04-kmXdbfJz%!kELbl>g!jp zm^Xd;l+oXQ`^~qIe>-CISo!T*SMS^}rB>m9;%L>mGp8zi{|)3ceC+rcM^9h4ejAS{ ztj(fpD^|{(K5er6=n>#b{_eZc<0mMrRnt6sg=YdTK^`MoAqo<6bMtaCGtyF0umU7T z1!INq@%{#x(S2Yt>j5JgHPCEw z0VPSZ4(QP{MCm7$(0CFtMpm8)SasgaS<`qX;I%sLJQFb03)jhXhbb1=3Mqg<*~ajr zQEO`I8B#r{70D6KzzZiwty*(;H`7~EI+7!w5{gL-c;u%*vTipOFtgSTZd=Hqw3vWr z0!HEk<(2YGz=ReWu=J|m6Vm0mikA>CM2h2(Aqy}b@tLvdsoa= zou@QOc@nxPPg2Oo#nG8rJp^=V;2!yFVdU=!lgGLr&vVeVscJ-A>o_cP7UGXYy% zJh^@S+J&l9=l!6ds38>QDO5=KB{p zTO7EBhv`3E5QPa}m_W(C;F*A5-#V?XeoS5Sst?tmBG8Ik$}<6XiK}yx!yFBD?%uj^ zbkDxS>gTnezB042cXIPYf*+ooPGLrLp!4fTw{Bk|s_!#5A3ryGYlAXG$SFrr+F23i z$uj}Vl$rfS3J49w`b%5;ziHeG@U?ho|Ce><#t)4o;TQd%1^BwX^}ESz9~L1Wd`DIMDMtu!-Ky%V$qrbVmtcd_tnMlY9)n{o_CW{@W*suqr>o!Qi&m z`7>v)xDr-IL?keN(0=*t^DiIznkq|D{NCzbIg57F#XmSSG%QR)5IX}O{`SkK-i}&9 zR+P)DhZj$sK6~cAqnmeNNN5;9e){?b-+lV|LvMRcL297=tA`hkpE!Ntxt)`ncR(=7 zdwU1py?@^)X)4Z&_p#8ubMeHJQ0b*Hh!n zeDXaK#1Lkn35cwdRfk~`cI@v!0RYQ0Co)fxmzQDS(7}Lt4FPv90z?)8CD&CWoA`^k zyQ{IJBqO=FxxG!uAk0zkGQ{MI4ZbVSQLT}y`RP-Pf0euQxb6f=;7@= z6Yxa&apT9y$xWL7!~w@+A77w~;+*AU8|iLyYvZE1(-g+>Ou!~ahDOALY-M9>M_%$K zc*~nbjdgYP!0_jpfH{&~%JZkjZjkJSLY6!eaCUB0b0Z~6GO7k}J2Z*fC6eBsKY!|% zw$@kXri270*MQ)TXh2GeaZ+h+6HEGk`~BDV{o>|2L3(PSyL(tURzLXniV8$Rl;}!& zfB*gS$9FyL4RwNygkU!pXP^912$9o?HMzAz`uXplfB87j+0sy5jBI2#XJ=c-_&gB7 zr>7wcq+Q(m=O4d*eBaa3TvL{l66^2oWN&BfnTkZvpL1B7g zn3s!_owcQHIMDE8Kvm!Jq3_c_K7Z)%X=|)1$xn+7aC3IFw|#5r7Zel{8Y&bu^>)4g z`>%stovpQHg3N?S9~T!V2Rkc!PnhRHLBi%1Nk{MBJ`HqAn#)UbQ=&t?-JKmBo$PJx zUA@r(c`9%s^?mA*h-z?piw*aO+{Mw%%)-XW!`CkmwM$fkJkZnLSeliI%r!qBcTcz1 zFH9_Ko!q^BydiJH?jr4KttrX^f`3SGpr6N^*Jf6>@b7ted81s1F3=&B2+Q)*6Juh+ zgFS65ZEWowom^yco(Y&|0w%kJ8kTyV3HbQ&W9mn=+2qOmdLx`yU5r707~j~P8~B+mp~keivBln@sk84(^v z^)*ynLj^ckLCRrvqpGJUFB`f1iE%N}xPNFk?xIwTUJ?8+DK5dEf}E^$r26A1OiK`> zONLEK1k?mCP5!|GOp^M&?C6eg9L4<5SYV!|8MaU&vFvcq(gavvt_N}oIKW}q$$r=g zfIc({fhzz|Sk%mp{&dD4@)F1<5rgxH(h`_+fXg1F^ue!saeNviCl~^jF-X4*cR{Ws zwrx&K8BkmjCF{ZcqZs=dOIbu7G|M%_B*<1Ajxdt91LNR>?i+YFIM63)t1l}ksuGEj zhK+{tB=15J?5CgK4fJ)1o6AbFGSZWCs@su{z@iNGfavb~`1x-i`+K@2ZNj?R(xRNy zn25N7di>*fCSZ5pfqv2d_$Q7J()Rk=#+uTCq^Kx=Cl@ChOG_&&TYG2E!GYdC|MBaG z9-ax9J()ZcFsHj%ZwQZE$kqqu8YIPLZD@(0q$r_~mKI9kAoXW2_?`1dQXK@(1l+|l z0e5vaf;Px`HPpv zrl#grc8)IYUTnueT@Gjjs{{qv=}B>sVQ?}80JI7IkFW@OB(bev%Y!QiIGdH!DLE-2 zAt642X98~LnSgmFV1F;$r?<{%?%s2F*Otv|SFKz;Z{D0avsGr#UVKvfsiZ56QPxMLu0`K2AcX;=X&0Bxkuzuaj6)RUPTe5V;`U4kk={$Rd{XyCh`Q+T8 z-P^Zq+xpX{EgROYTfcVg<^!j$-hKSS(2T{OI_ndxADlmSu$QxK8lIb~>> zF4X`aOVPiTI$&WXnA*zRq!4#E4|g{g7i35H2Ze@5Mgyjnh`vxf%koO=5d;PJ5`cDC zOG-ipGCJT7F=!FH7KLts*9xdD!0cycet{UJjg9JVVFiE&9Kqkhg8cmaAsA9}n9+%q z>ww%fq_>oy)(0@U&|I!j=`JY5ShRuCYsec&5_rDD)Q?$FRwJ9J>zo+ z9^SrY-TI#v&Y3%V*5t`5OEw(8t7kw}r0oT-&+go~a`)QBOIQE6V8&FHSyN^#T(#?* zw%&`E*ioQ;4PiQJyLPT$v})NRRaMnFa~H4Mu6gC2p1#prNLx|M+TNIAcjw5qb<5_@ zTd;W5wnH%S^$g!wJGt{rz;uG4J)AW%Ug^j}3m)_1^Gv|x0^*r~c_v^SGthV@U|JW* zSRtr%{K4|U77%Xz!2U_55Ib&gy}o4l4)qx7fZ+%ua_@fqDzm1TUDNuNoUKi?Q_I$- z))rI={}(3E_ACx6iTeZ;WaMV16FOpf-CcHurbIiz$q8(U(OThpk z`xn4u8y5Gagl1>C-`Kz7ZM&5H3yCL{BAh%EFx|kemX|A4eo!1Ydd!%y<5wElxcY#J zH!3=Y?k~wDl176?GiJ(<8#i{`gsrbEUA&lq9mUD9>9n`EgzcL-4OFD#<@V~EJ9&X- zG&CG?W}?ADN3~zcqZP{X3KQft4XoV!0+FB;8BcO%-BF6Vh-U((^8lR$=p=!}qhTgt zXd}~F5|eq%GXXb?kpDiU|8U?nNy-v+<}c6|cZqSLgO`$+kY)N0)|Z|LLmdOHtxHvP zT02_W+v$XXtU;On(=+b9bI+kU6XX;p>2$QTwu?ZNP>;hJx4MWWeZKpDQe8B2#zaMV zmAF}8xy!NiL-Y1b*|?Q>OTD=R1{T`30zcu8S?0RSA?c(J4>KzW1NVHn*L zEG78~AmW|*!qW#3TS(yLnSj~yLvDH;?8x~f z5=8ih1o{O8hJ*v8EH#ZYeCgar`wk9&qUOfhisAws;Ingba&yTzW^0t}m6f?PH`Z05 z&JMXaCFK9-MAVD+Kj5L!zWN4+oeT?6$ZkJ2>Gt{xVNZWYc92~~o1J-6 zU!PP=-=z>=4s=M;*yQdnY%k1Cbi8qCuSpNw_QVBB^V9y)+FBDU@U~0Ju+`N%wde3_ zpWtS=33w*pnD`{@%mu;LuAUaUm)83Sm|r`*Vbh-N^S1^B*czNPjEafF?Je^n&N=67Xgi%gXU@#M_q)%kcVqX=ob%)Uxxe1B8*s0x+W78T zRjbx|)_R&!oV2ae{Oo-40-Q{huAe`3SYFxugQJzE1x|SAS55o}93$ z!_o(pmG0?I;a%H4csaV0XvR%T8VH)Slh@RqowU)HHW+_YH=;JuTSH z%reB|#f^JU9?8jFJbUt}%zc$>JQHvs&jbuFA#LCNy^RfKP7OR0u(hR**1`S9q_-*F zxM$<;`l_sIoZJS^vnv2d^J$$#2=cd$Z(KomUSuoxS~oaOl+}d51-NnLXSY z8ESs#!oEXik1ahA6>6=0McdQc7y4ft=V|=f(etf#eW>%h%jb@sK6>bGdYFxo%3W09 z!SnAf4l!^ligxy@2zD~Mdg08O!}2P2;7Y!$|8>Ul zb&IFYn1(X^)8~G_a>J&RM)08b^|!y{6Bh+e)VM`}D>JIZp?5)8@?4D64KJ_eCo=J@Sp& zyN`bT`qQU@_QvM&(wO*+;zkOiQd$H`9+LdWzx^^cKGxsfQdL`<9PAyQRtR3QN+K(& ztblmz|M|yn;{cg=_O`cG7G&iCof(@^SX7Mrb!l05Z||@F=Sy>GZF5_5YbS`O>&tT! zBLiI%GO}`V^9oQVaq#!Ajn!pERgGT*Em=9qL8h)TadAnh!>tOZJ;Gh>tgLJtJtGS{Mh3bn+FR>0eLc;+Bch_C z6O6S}qkMdVW0OY!yR+WX?_=Sgu>sp0{M8;>AA;p7|IqIrw`hWb| z-9FSwZ5N++6RW4D#LB9e1alka|%oAkSL6ej5VGK z7+XHi1Pld-0##Sisv#YpRR3@KFVJst^^?z@^!tC(f5-(=PW{QjIH}*Hqcog$2A&D{ zp~edn@08-KNHc$5H+_X0XSDz^_qRC1GXZm}i(ojk(x}1R-%wK*t9AV<{e{jAIz$kd zp+>!Wx|`bD3t!2}ena4J3zT($6w9rxq1r+AGF#f*tAei-xWWXTt@WibGCUJ72RQ8~ zpda!8;q`+0;tvcC7!*7ca8F-vmaF{vlZQ@UmQz$xx^eD?+_}x`Hb||!;@}$+9iP-U z7$W!J?zscI_Z&ES;kvTgO>{fIb<6rCqDL*9JOjddoR{p>xN`8ok&|c6UAS^x>7J_m z(aWa}Ze6@k^rV@squb-#T}$38%_j#$gv5~7g|maTUNVNs zzC@(2x#feF=4G+@q7+;d$hmKnI!Ya?jTEn~o-0HF7S2eX>_x?{+K``F`0&K8nX_p5 z|G5M6DieN;X96xRCgwn@^@D&EBWz>eQ2vLohm?v;NJpLtm{PPq{?~6m|MX>Iqz9=Y zNwMMnUY_nAiIwF%6Rk z%_x(^GXaAy8WuaQ#T8{m;EzsEO^6Ib84`cLvWm)TTA|cx>uW1{Cg4+trT5CX*Vhxu zx~zn#ONom&%E#5r=*1HyxeI5G?%lKJh+#EXhKtGTYf{4eJS~jhs4IY~_sE`IJ9qO; zz|yDm?Cl*vL|k7R-P^Wq#{4^^_n*G|_~|Phu-9USDnH}*&(u{EcqU+;33&Sa z?OJIq_4PHCRPO|=C#2vliRUF3FBF+Qea6fg)27W_Z(84oiYlN&Az@uzbNty&>m(No z&6z%J=FDl+X3P?OSY1c*QYLQ9SKPmMyX0a~$fi%5Hg($c=|bw2$iXNtA%^6-W;-P{ zSs9?_=g*uzbqfAWoi;AOVx`O{k$53gJ>Z~C+;7>^D!W^Ycd1XBy-we?X? zEuA&a?AstbYdXe%H+9OCsnh3(>X+r_WRa7z-u<<^_e(kH6(V^5X`soaH<&s1N=8az zLUl!DO|6Z&SD3-2EsN$)n=u7r@&A-*(`WAvLmioliptt@&8H4tZ}zPan>m%Jcu9tHYb}kc}KYc3P z|3t+*Wywqd zW#e=6aesH%(DoP>@ekP;sE-q{R5xa6fM^WHY(ryyuyK zKXUE!k6(WLJVBJigytRw6DZI<BI!26XO$~ z8{O}nKY8%D%(L82Und|RVu`yXNB56oN$MA`9ND^k!w5Cj56q>5gTs(h$Puqhk}Z>_kFzdAXpqCT%rgOlrk!U3Mi~ZLE)3U+EMAs> zi(|EGQp2Sl^s8K&)ln1+#^1v%65kztww zbMz0fkE5p>1L?GWx=?hPT5R7i)W zZP~VB$>Q(D#O8}%DjNmF7;fsO272@HUY-f~<(1XzH!lNGxWod1em@$~Wu zpyxZvGXY}{VoRT*AqdjZ27*)o>@t)45A=cQ4&(%FCpinm^Gv`1e4ru9(^-)k=4Sf( zq1yBCZc4jBUatLVn$4_4-+Kc18 z9L-OQ0?h)04bT$)_FibIlcwlIFpt&S1 z+~xgaHLajdgq0zOtv0FuA3u)uHy5WzxxIg&d|S<-4LPZ7Jk#&K!5@G6Jl0pAlN|1* zdk^}r>OnXZR!WTR51)Sh^_PjRiqz;J$5*QIvhqp~()!5I1D28WfAHsDe*1a2r8qI% z*ZRdRdD&|>l`Wf~`&6%mZ681X^!wk28gruqz0IF0UArnPtMD`fNo0U%Rgonz@%wN8 z{rADTln`GJo(cHM*|X;_E2!%jniJEnH*v-f@Jzr2&BcO32oTkQD6&;l0Eb|9Mmhud zv4ncgU?7-qB?a`$;5#hL&xQ8a4h(im^5N%+BcLP$S}tb`V9>RinTIf_WKfXsIbE zgT@X8^Gv`8 zw@5BpATD_}u?5hfZ%o4d193V|rVnmjIU&7%k?4G(1(K%<8*A%8WLyRZ1U!88j<$hj zFD{)sv}vV;h|oNdMavAp1xk@5lK1wfo4VzFcy#*M?hVUDh3C(kKVRZ%X*HwrguJ`E zH`FSw&GV`3>AkBKFA$kOM@UFWI;xPi_Y5jt)6?ziU)=5ZRQ~J^NeMBLd2@y53vYBm zGAPa$Dvs6N=@V5nQ2Offp^Zx<76{LqCoCkk#waW#EIc9tZVDU_zP|ZQnXk|6S-*s5 z0=|3a2j#mDG@ifK(R*)T)B-MOX98}ejH%Av-hr`Cqr+V-)nz;rFwX=G9>2I4r0PdSHMgLa9O#zeqJz&DrMqxL zD9X!9#|GT?q7>17?r{%-@al`araz2bsg zl=?_bO=)aG#y+vwV)j7pJ^lViOc-e_5GeG@FKX03~5H?GJ(tgvr?kLO-z? zXae#=vJC_;LJB3KCU^^E_g77BK_q57H2!_~UASM8(*M~nN$(Fi9t~~bTnG9kne&|0 zNyq%0nDt>Z5Gx|CQFMHlm0-L;j%SFc$TI=oR^*w0jf_pqEUawo9GpmfK=g}@SW=vl zf{gfxAS5YydZ6)4z~yDMdC}Ge#&?Q%QSlI@)PVF@px`8>*P&(D>+CfT;V(uFu5dnCg7xs%CeF1|N6(@e)}>$I*eF%CwOQ| z3)7-P{k=R~TwR=l3yMa+{^#HS`{&P}#s>itX{fI#EzZh{4)Aq%b#`{Pw~x*q|N0+) z|JyHLCWhNu+OhGL=H+IlMhAMixwtslS=$9AkNxtGzyH^-pT-7?%bRLin#zk%iz*_> z)7in%(bmc?FmC+o|M|cF@hfPM8=Fx_s=l;5A8EQiE|}Zi*2cj&;p6V&Xk%+RV1F}0 zR|WZ5u{cfK+^mgspFLMoQdYQeRRD1U$eq0kir?gqVXyahGhm$g`V1;qDnI z9$`$@fQ+Z80H8cn7N~28@mLaSzMnyK2KpK3jfwH4dk9EawsX-dh=oFquarJVKpvh6 zcz|aD=9z%Wm?gY7&jieJpxCqE+c($22GrKW#~?uP7**jq4j4c(rebF;T=3wMYqYabv^GvcI0gP3fvj(7uA zQ+xZymhLPctKf>dn%agYgd$mqOO*O;%e6Inq`@-*b5DzB0%k!%ij#3+HnKMu^>qHC0~$JDc@p0-IrP~yOb&<(HwEqpy}dY|@h+3{KhAIV z9>~1;4GU zg@#j>3f3+ma1rHduCFZ4%}7p&XDuNyF_D$2rw*N+q+88R^#t*y@H;EDpULgnv~PpL zi!p>@*|P$3l%|tk*oWG?)Q1jlXco?93ZenVO95(>+C**}gZa}Jc7x|MPY{PMD~0aR zyOY{btc}Lwos+IWSP%R_hg>JspZ}-!M+f@%{0~f^f({suf6)KT1WNCL z-vHRRe_{e1931M+Lh&Amn7p?aZ@|4dHZRl?866)pe{DcapeX-?->@IM9m#Qk!%y2g z(pBrBbjy{-a&n-$IVM1!37BUB=88q|Ou)1+&|XB)Qv6T*18pFjo{`g!8Yic(|EB-n zIzakA=|3o6sbT2$rH z-XbhCXBLQhMU?#m5l@JVPe@AX!wsdsf2h&ZK}}|*@SHibX3d^A|CpmEI)G{>E}k0? zzizCIG~}~q&6+i5-YyF#uK*(YC8l5Om)Kr;CSbN~X<@=vAWHz&F@pLy-06IuR18uc zbZ7-KjUagq4T4cbUBLZF3!9sOz(?Phd?)*nPEqnY2Z#GtIY~*(oB%u%Fm{k{=RdA5 zC>{1hMWcr$J>#0fGXaN3Mw9*z^bS-To2pz|y+8y+-1CKn_v>3adwBU#798n6-S9fX zE=#XjBqlU}?tCHPZ7&V1oxu|bvTn%xXpbLg)!n^*)e>=}F9->5Wkr14QN$-0&tE{5 z+w^Y7`n8KhkwPJ|U+b-@t&^*VkB>inKFk2^?d^!VyhaK+4#J{Gp1d)!b9QzA;1d8b zJ}oW@&AM;BKQ@4#@9Q#mJ+!~K=6b8Pj}{TjL!_Rg-J{-M!e z3jfxD8Ob~oFs=XudSw@lhQ?Xkqow-=+otGQW3ORHJY9NPNKO|F>|%7tb2kKfv6m}j z!{pSbhpcXx)m)#xo-(BH<4W^QPNQKcun!xHs^w`1le3{Tnk4jTp$~o-PuCy%lbl2( z?eFU%U)yj`?v3<8mKRAQ*+6n&@JzsW6?PxGCUa8b?V}e_De2ic*kbzJi6OfYOXY=;PC3zP+y_W~1j~?15ub}A@5u1>b zj_2MK>z(=Qp?R+P!0g-0Qbb-i7i^z!4NM?`EdO&%4 zyYqZQ!pt-uK6vo(v4*B5s&{MLeXM5&o=-QDclWdw6(?A{ceZ)2Z)jqQW)1=bA~}Ht z!`bycoz10%>CwRf0fByA9*zJ3A0p`3ccZQfj8o}UT+&ZM{~Y!EQL!XqQ2 zqGQ<&8@3Jd`|E2ekhqhbL8ZX4KO`p64#7N-oc@#JkY@tc=pG)ix%=cb_DnKhm^}ge zlxG6|?l05UUOKa6=FEj6^TofPCHmUN!IPN0hXZ%4dZKq!eCl-ZO-dW*%|Mm^88he4 z+Wg84)TKUs{asN9#CRs)>*9YEKXU5)m8)`c@`_65cb$Cj{GEY`r5!t6i|s#XNUhg+ zp?>d?7S9As*9KfKSQQRp5@se0vy*7A6zOHC1+;G`h+J3 zzBSNMJN>}ZHz*=zFmulyBNMF$_tkkOVCXRC$nNQG@9)gbb$={>1253AfCf*XKuj<3 zOu&Q`s3>Ep>3{mdP&~~2&&>UD%3CLR4}HgfRMP~}1qSpdm`??JiFM$FIGI`SfmI7b zHb1v1qQ)7vMV<-x=B;ZN4((howPd+^KvaBEYDPBC1dKX!q@d)%B|KWKEzPw)hW8ZLi3xp= zuq~4_1CeI}M$C%hdoZD!P^hB1rb_T|*z?8D%qh;wT@aF}wyuWlnUkH!bKgPMHv{OQ zj#YgvbLO-CmE3>`g0z$SpY0FW0_to3%1Uvd<_8)T{U{TB2l}X(X96zH%d4Rr?b_Dg ze*5L8A3u%tw$znmCx`iadwz(mC?lp{#stVS0ss7EVzj@jqoFV@E-cX3%N_J82V{R>V&r=8Ou&x1&+aK-zj*4TqIpg;s{B-; z8fq!0pLGpo0hT8EuO2DMUE-O5w{6?DecSfE7kwha!)f`{Bp@K=X8THARqpIz=^Z59 zwtdIm(-z*|-c{AL^)-=xPWD#sG}Uj)oY=p6%a$!$A>YX}0c&c%(xEdGhb3BB_J^0M zvS-hpK7aLx+iG_oJkfgo>dm`vVxsKKit@BFHny^}G}6;Sd#9^!K&7_WaL|F~Q|11Y zq}WhD4`+K@Ybz@&Ya6Z>7fxf0N8#VB^pvFdxaja8-w)VaL72|o8B>(txGu;C@;C#< z!;%u?qJjea{d|3WX>xiC3}@##I-vM)N=kBUcxW)V!l56)DpBb#qy$t|Rn-vnEJ$>z zMpr3Hl8~IMt3_o>@HccuA>s-m8z?O;FR!m9I5F$OPD*^C5?fpmAWQ%&grsDQ&`4^v zf9g!DR#YDvy zELtM9`PcAM_>yqMk~zED~#8_uzl6idC`hn38zFY?QVXtw*KlK0^AIhS@@!JFW*aV~R6?9@#fE^HQKjh2Bt03D& zhY4!j6zDQGPE5V<{o)ml>G$JDo(Xu%+U1gxi@%qYT)K37Qh7OQHCEu^eHylYrG4Yj zkpr7Hu993Pxn#-GrOTE|C*~Ctm6Vp@p?>`0eDBdonSGnKZd<-$*)qwcl1rC=e?1{B zH@~Q~j2`@_j<=8GPD^iIvu^bYsTIqXE?c%_qj`8@R(@egDf>h|musoY?AWns-6|<5 zo(Y%&_JocD?1yIp=9z#;$0mBMRTWR{-?wRlk*?f$($c~deMZI~Z3jPGAN zb4dE&8p-wRAQu-GTN{s(u!J!O1M1k2x31>ZV<-3T+P-Y5gs6y!n1qCgOc{(gRBMHN zyvx=|SN`acgB#W?l@J#f6~P4J>%+6MbMx{GXgtpZ%rgOVr!nFIaLKYrEKX-~EYe|3 zN$N~aCpKlg3wj`Lf#k!G<2;9io#@nuI^a8;za&SYA+FdEdy!6UHUaBR^CQI$jb{R0 zzh?33??psKg@v~Gbh9Lqni|Bi$3AA7-jhAGf4B6?@7Jsn5fwv^mC+5jvZ0PK!sX*b zz6MXP9X-Bx+t#H^#6^UKMHVa&x=;X&XF)+Bvp zDJ3N>gT@b-zR;H2xo5l7(nXkD1n(dwy1~UiBs?lE0o%{mN9&un_n+RnV&zJ)1!Cgp zA|@_$)ZW!6I6Nwr%(02qrz&R-Y+1QPaAS8@c$QvCm;F*A#%0Oef5zS-v0jhe})e#XnYdjM$ z_7?<_2C!4Gtp!^WMU*J`L^}x*dK-#TBYfRFqU(o=36x{%{l)~!GXaM?8@|@QeeL3f z%U3R5Q8vlW&4mGwPrG$5E>q1#LAH9YG;du!fBxdd3zu#gB&QLP7)v5VzI9`DzMt7E zE%hH{K*V|RlB|kB4AKl!Q`2aCUwcDUL7a=e=6w~piziQ=yKwQ^9ixzN^hilzr$K8) zaYm@U?n70DtLKlOJbU)Sbv1Jk{YJ(nB;mvBALuO2P4;!vzkBoAmGdW0oIZC^{(+4L z&jidf0h4}1>6wZ{|AA2eB8O)JmX*8f3kN8GGvZmz7=xhJ+$65Jlq$KaTfzH>deInCU)ImN|dn(&ej4FFyDNOeP5q4)u4nl*f8n z={>)H^Ahb3a>_5AT)n6V8;=~9`ur$&6CI6viWkqFKY!`^tvA;8&hFlR0nq<}!M^t9 z>a1uF{pXL=6fd7Ycm9(6op+|_;O6B^_`Sh_j{4G!a2LI&kM1k+Ou*%(Mftf|S(%6i zq@+?3nBWh2 zU2%C~u+Gh6d$+D!C?Yg}q12wR0WKSyn85qH%Sx(BJ)T@Yv~%M!3E}zk#idr?ZNSHc z3w|T5|JI_yjxoOn7p1puS}HCyPe@p7$x#^iM0G+CEuIOu%UVVD(AHI=JQFbae5!dS zVCFq#Wfp3YJB%}sT5m^NeL2qr%rgONJk@+-W^M1{;qB||4`&qW>{Ae_E8EA&*vj7C z)rn^UhU*9og~;+qaX9QXtcXqq@`N4Hh_i{p`t^03{Er@#SO?!UXu~);VLz$izyyLD zQ&4P!W|))z(Rn8sTdsO+YS9MQ2CkL{Dt6k7H&>QyVw$`1i*W@nBotL$)Xb0E; zz=RrsyuT;V+TGm!?aMdH`wvR5TBnfF{!NYt)Hjf7l^g5sXria9wrlN*#Zv2JN?Y)N zxbY|_*5dD5k`QZerF-wzp*4~V7cE_5PNdH$wFvkuNCHdTL#k8Ujf}NbW%sOF1RvO9 z`EpDiiit~t+!5yqdZZ_VIRixOu#Ma z`U;1Rox5=5F3$wa!F{O?VeX`UlRXS@Fjp&pVoN~o)WHP(Lk3>fT*MWSSfCeFDx#FMozrVkylQ81oB&@4N4g|?3 z^?zuv8Ty}6($?Jt6gtU?YIjoJKbUWF{jl`b?Pu;fRdy32BE|znB#;mE1>V1M>DZoK z+t)~~K5AG5m{BcQ+b87%J>E|>RBxX>%rgPcojZH3kjRpa&&?$aSqPeBT!6k` zx&4OPqo;55jm)r~IzTel9j$bD?}inNCBzZnT)J`RCDr?y&q3vDL<7B^OoI5j$Bl^|K!E-`0?vUkm2^WRg`9D zW~AiSb_0%p-wuw-fx*$yFTeisWo&q;uM1wk^5WdI*vR<8M*QO_B0DrP`qSTj`|0E8 z&|q(0XJcJuQEqa0u&;MeQgKZq%C?P+eEIv|{^RTT$RKgKOZkwR}U{=|DZAI@Nr_K zud}HZJqq(O(-IS6BT%r9RpN_6HVMlt0hMoWcXvB@KFW*G2U&DTwS$Lsh(MI|-w2XU z`rzTB9wms;qoQYac2+je1WYRZC$)uQl2aPqLbz>OTOnZueg)qr^&jYBo(b3~zocl8 zX98|-uF8uG^LKN!F}Bcu@$jD7t(!L$6>i>Cd-~SI3j6m^dwF)8pPQ4VrOEp@&!0R{ zzjynVipuQ=&)ynY+EN5?pd~-r%f;5h%*^1ej<&|*$LbFrX*_+UXKZQfz)p|Bp4zM^ zUl#{kOLLR=x^Lg<8=IP1Qk73nZ+2o3oV2sKrmQe0BRM`QJTxRIFd!%>m@rbbZlU;C z(l`zfl&+>uDaoJ+N}%l_ndDgEkW=-4gq4uSUs{N4UJBo&r>EmML!&qi#m-pZ3V|b_ zdli%LPJ}90E*oS8VCS#UW z_JKi&1p1PK+$`vR8d@s1KVT_hIl%dAiA`R3EqUZssDuukb2NQ$DRdOspt=;Kx~q12EjW_ z{sxmwe{2{1mkD?#V4exsmuCXznSceGD7S-fmK0nEtu5WDxiymj0!9S2~;hdTt7vkgU?i(8#72)S$Z1!IJfuiEg`>#xU`-fT@ z^3t-iio9HctQ>88-7NK8^bGFaR#mxkM?>EdhFE`Hbwqwtps7QUvyp|h?c)bewY6?3 zsNPe1tZQ!DjSho7%>^Nj`k{7jjqD6jt4LGhn(W;N>RNhc)^CSV5M!O6@S2)H2dOu(&qH%xPBf9QwvPS9wB=%P}V z9c>+z2SwjdjtUWo;f_a**(d~+0w|!pL1xusGd7+)l$@N6XP(Q}&ccJ2UxIdYkbIDW z@#sWG44H#%eJ<dm|5~nz~pn_nSgmF zVDf+sjZN@O!0_~=@l3$9FHpJw&jieBHQ-36*drW)6&9wZvP(o4t=8|S7zc`716|Wh z`ZIw4MoT_CJ9l)Q6smBnN$w$-Gf>cIuua+UVH@WNl^ttWiwX&etJk64BIw%6P^XK= zWA@QpMP)06ZObJ@0Fm7jm7R~ebU7%)Rm{nU`YbKX)%PtE1%Q`l0zUZ0${h*Fp^*-f)5E^ z{?oT_7V^Ky83)WTE_=+#rU^RHV{#x=46Q46M9_icJ>TYMQl1G|Ok8;0{P{v+*X*1< z!O{z^Ii3laEg)J_q{6uTAq-bvRZ^Ijo0F59n+F~k!T!PRBB1od-L0*;fdRl#A(S?T z$-5i7Aa=u(k83vV??~h3>iAF2LAO6lK%V{IXW7-eGd`ZXDR;`Jxmao(b5pWptG2S#guc2aGecuerrDptZXw zC&^j;_8F65RJOrI9E*Xf0QC2DcGks}`8XtJ+P_j&IDN^`H>3@20z@F1n<4KmObiQm zb}+EDFN`sM^iuAKiO%b^dcd z<-&p88lHCg1{t}C5$E;wR>TK*Kf>ObE3&B#oHd<))p>Kn$~6yl`p*W zwlNIIV|8|c3oMMaR{TNJKHSy(?d@y8xcqR@FVR}(MNCpsYG!UqA)Rdv#@XRYgUf37FIj z^|qM$wze?SnKTRkGZUQREns}=>gD|0c#WCf_yRI90@zXqm^pbIn1JrT)Mx^V#E`c3 z_Vx7DDZ@_<6{9(6D7{P?j$ZWhShUt4lhZGQfdc*S?`bAAAL&Xyo;bkd7{vxM{qOH{ zyUH^GFBG3AJbTJ2GdG{W;LyIo;-k~<7F}B=$uj}Zn>~Ah)FPpoGlUijP1|q_7(rtY zjrQhQ-I#qJ{gBYbYsw&uDDPoKZJ}h3$O3`T&!%K-wJ$i-W!f5mp zUPqW1*n8NvlPGdaKyg7r;(cqU+k2x_a! zib$6^9cL=aR2xdlki0w-a7_(iX8P)aY@LlCC|19Q z<$$OL2(qp+XO*L;FJG0Bxy~~IW9`+~rCC0{p?+|M=rc@X9GihxwSO2Q#nf zK&NAxX95OC8*(zMh^msc`o^}-*5=x>lCqkHI^;lr;ti1+$T@LS-{|O2Pg{LSRzhrg zMLo;GFD}TftYX7!+rNDMJU-IbR#%pj9PS^K#X{^wdAY>IO81ywe*Nk5$C3WFx{}Ka>HyMF)e=dYi}hxxsf_lA`uefxz~-Zk4>e!B!4d;kXcX+r_WYHz3-u<<^_e(kH6(TdI zO`G-|_XacPUdc#FOsEF-zt+awE6m{1mPPZY&6t9*_@8G27U7wIKX`h$mz4s?$k>QM zR*babtjzS(RB(YOMTdYZ+}GzjR4^F@*B`3nuttP@2-qPmhZ_ln?8d>~3kearu{HwY zzae5JqaOGIIpgMqkoy7Ub}$J#(4D3qOuZyVnH1JoSM=bSfWcJ;3{ra=D$tCN_c^NH zzIgJ)o?Yv?gfKM2=Wzxovr4i1GUPv2@W{vz^nt3>!vjEt>{*rky4uOb zKZ-@oU@8oH!d&#Ys5#fuj& zT(o?r%#-)lu0BD4D96%q1FqPSo`9Q&H*Q$7dhfj!OiWVFnH(c3`pdtgEB7zM_bcXk{~mZ+;TI zxbvU9lXO_~Ou%E~pX=-t4(!>y>x6px=s3ZUfD#4~X&c!#;OZTTQa*fQr_{=&3q_Zy z#r6%8ZNv%%a^hywD<@BF*|uWI;_t=8=8In{8wJFe*i0H|^mwn8%8g^vdw3?`MT-_m zERfuC_Rce1GdmYgFP{KrR*npf^!O_rSif%7%8mQ4+|zvV*3iPv*~1HZ4ml2IYyd+Y z1)-j_v9=QRwU7c-P>`RWNBalcK`;Xh1XetvnK4*U5$EC}+CR8G1QsuaI4KN|JOUPe zhX;$c7uK*v;k=^`EFcdPq5)p4s!1`ksmVCtnSjYGKwJ+BM5Yo&NSNJ3FW^U>3Hb6A zS()n(g1H=!T0E_R;jiOgeyvOJv@(2i|HkEumt`)=s@j*Km6w;%PBkz#Ffm%jGXXzP zmRFEHd;0t(#iwR=u3jJ_M-miZsd$F{x%LLn)m0P~WzS!hh1ebyMgv2_fRUzkhz@;) zt_C`9UOm1kul(@6y(>t@0)oRLBBLlp1nU!fLp#*DG$%SZC?pK^0fE0po%OhQTD^3f z($>+`fEr-Mxf!Wo@I*^OmB19>A?Q-j3wdi3QVT1o0$5%yr66VVOu#%7Fj*24zyJ2% ze;=$%3GwwXe5fcZdzEJbR@X5!w{Zm5ADt8fgT1x6DdElr&!0TJb>;NgOR~xuZ{C}c z+zaU@c!C41nK41GhT0Dw-6jRU`QXJ{BMUnxR01Y^67+MRD%{KNz4qfLD%Y;cDLsCr zYiJG@Bv+3Q1b@H;{e6|8PL`&|Z|ZmfC znU$T5{bQ)OFtw@H_`<%mlHUu>o;gcMR9JM?^&r%+g@H`+!sPTc-*;zm{}!J=XU5F= z!a^deE;xI7`}ze0;q)4;NC-DKdVYNUV&o9do<3uqu*iay=d2xFJwJGP;cy&i2{3U%hVc|uqFB+KII6^;Qv-S6eynLj1a{Fqrxie=@pEhHjkf`K3`8Rqd z7IrQkNWJgtFL-`m?%@7S!n0;z_{=%;MVIYRefC=4#N5UipK^CE&jd`+K&JmZ6EMiN zkqONeT?TyMj~r~C33$A}p)i(b0_K^32^#|d9WD|%2v-1OO#26*hpo-5_CRel&jidf z0sFHXER0it_@g{B!vd`2&z(IWB`zi`EG#Cu<-MD`Cx|j&03+cu^1YshrrNG8d$%nX z76Di1f@RVMu#eq6Jjr_P?#X@iP)+XWhPCUK3C#spsL;aYC-qEGO3&ScibcWkZudw{ z?(oiaOGW1jAr((#@fvv@eROd3K=MjwXM1PC3%PT9)~s1BA~YXu(aOCKUh0}y**Uop zG9Gv5&KRXb2ez$UBC$Y3SY+|K9SToh>6=>FIyga$^^LV1BC~t%*7fVxZ``!w;E`+O zBqSrxJ;3M5k54Y3E0!_;rxi{;; zGXWEo0#O-a6U2iv2`(%fpw}yk^Rr05<6>f>qobmTI2uZd(#Vb0A#m- zOC&WlrLhUsCbaZvig{@sP3(B&MV(wu{m4K))opL5SxFZ^7gwh7CyXA((-^2jr!gpN(c+P}T<> z2pacI|1n-5r>&IIuNc#cKn|u}?(OL#>}Uagq@bi46hHO#^b1fBnEm$j{5x7}axx-( zo$W2X@+bhz@h}OPe$k=1p*S-(m}df3y?Nuhyn^D5NAFDFV|Qt!1zc88mJ#jiV)*v? zqkGCqiZ_1v;l}MJFW;NnI=N!eg0`lmEIIsxo!+}=k3k@C>$a-=&3l@!-&37`Cda)t zFU-x#_}z2OM~@z;-&MYKU-RWVLo+LDTgV&ni8eJ>BzV{wy?y!exwgi`N1EEN-|8Eg zSz6gl%CRh}3NsTT{N3#=cqU+C3BUq|ic?K`cE_TN6f}%y0xs(6#6F7+hUtGh?Oxb5 zcqZUAViI#_BKu)F5+LTxo4(<-o{71YZ9`*goZNvcGG}*5Etn@dZw`_qrq7s-7y{1( z%rgNKB^aSAcqU+;2^dVgb=9SXMVZlo9&TXqwY9Kvfb|l_h>%JsuSa!^QDvBw0A_4~oQF#wCrlk3<1B-LS@M-snv1~@;HqVf zstKWS=YCr&ffE0zp%W9sAVzCr=qCn(nCwWQ`A#ZJ#L93obOg@?oS7IK6CN4p>S*y^ zM@#Ltit1DQ*kZbh4Gi{mHrJJ;CB;TWL&W^Tv`bc&E(F><9pFetV?~ZL7)~r~%L~_{*snz#g2WkTo z)7`Zn-#vR?M)u0Z6GwLM*tB-V(q)iKt=#;~s=w7HHr!S3iL&h3i?TB3PGI<^H7lf~ zmSg&r$Fz0(`gtZ`tVf;+m{o}*+#c;?EG9u)8P5dV*HYhy6f_QJ&Tc~tatfnw*jhWp z->)MxzN({_5>Ob{5j|kc4GfKrPc*04@l3!x6EF}Mlez`vLM8#T@ZqVaU_wPXS?lOR zA~MLKc?2dzgbURv5aX&K1D%bhYXB=GicaLO|HcGPDL4#r%XQ!;p$@DNFdNSG5X?YK zDQp7B|IqKr-|_!X{pXp0&FuOFFDlH1GO{U+&sKkj!#rfCpyp_n`QUmW{n_db~<8nTuV-7^;5Y!GVa!h zFrm07Xv7G8mz{-1y2mxFe9}A$K~a6rQgDqG6cljxZte=sUCyZkh2enpN0uS9m+sQ6 zQGr09_$bK5;&c?@A_2RKb1*-4yC*pdK_Q4nLJY$n3U87A3&!(Iz&sQ1j>qqM3zJRG zA3wT%!=4*2TtXAlvhz}d?Os2=z7I8xmTbNIy00$AN%hRR%O?*>?>TYp!cA4R`x>|9 zj&I+xY55|leRp5<^k>*UIC1=%oXo}ZCr@9Lm$`c7%(0!jwys$&xqR)p`!BnDyZv9? zxv6wh_3Zgehff?neSH7+L)*6Cg0W)lenqXfIK4WfUR+YScy`~BOUHKal|Hb3<*F6S z7cP|AwC_67{kv(qDm47z#Qq(p_H5j;YxBCLi=~z?TDpGcNhOWf@AP3&Li?J+pUa#) zA-!Se)(vactXa8gOrs+IWCcTYd+CgIwJAstG{n3HUjgHPg ziMSsH9jHbQl{f!GPV@E?XE*DCuP9mF`DZ3T_yT&fMv!YwE>V2x?Zq2#<6%z^A>Dg? z%=|UTxnXTHd4CUM`o(WNfHFwEBVDx~O1E5TEGMTlevC(w+3@hlL_u6)q)l)_WG-{K z_91PP+^LYmRv*UPl_r*E&um=0KjeV?yT6YWjBRVB;Z$SkW0{YoZ)9kolbw}Y9cseD zQo~`;+$4DOv0;a-vHHTyM0Z;gU!X2}w47Wb|XMS>eP$U15x;rQRF6A`~~p z8B&Hi$0Q#f8SD2f>+A2VNei$wdhx;_J~ywB6@Mxz;l@wYI}ZOi+L0UWVftE2`_+fk z^z7Wc{DQ*5e1u={XeK%$fBZDmkmcuK@bc-?XI3#u;6%#J&CAPU<9Q}vR>^9NX98xn z3>O4o$%z~Z$hZ9i8wJ;cwgwg|_&5Fk*5QBAf9M7^Oz^+zKlPx||DykJ0(NJ^zlX~* zlXv#~S^u#G3=R!C7?_g&b8^}cp})`|BI9lE@2K_p;j(f6;G~>Pwr}Fm)UcdvPxW)h zExP;J`xEwDp#K=nGXd-GkosP9?yT9f=giw-1d=^4@gfbLo-aM%zP@I?4NI2^&z(DG z?))R~t=+sS1tBV$lViW^?(PUbyKIT@yt(s)&gfdYcn1a}2_JGgTW}^}%j_!E-o8Ls zWWKPRo{fip5VGx~5=agy$r1f(O?`P_*&?0^m^lHO3Eo7yL=D==^bu-9*8w8A!%d*6 ziO&B&H)tMrQy^3uR`{P~+|M`^Sq}om3FLp)e|S5Yrva#1>hN#+&wL3uI63jR^P8;$ z_J!Vs9Htz5$sez?^Pgt|78ViN8JLs-h-gYmdKM-54h(>+Qu*$Sd;6D*3(pr45?=Ps z%Qqw{CMGs6A(2zQ!Sc)c-rC!FCg6_tvPi$sApbxV35umMVVvbki;_+io(UMnJUd=# zv%pT#+QK~=@?$VnW?IN{@Ov2%A3+2JjjV1m&bb>vPZ@Ibq2}M@^rW#-aJP;AftIHn z6X3Velh+9J!6f79`a^6yC#Pv?0@&3QybFy@NXE%q7-H-0 zW%cT|bU>ixy-WKJo<6qrNMNA7-VKB3*m!($r9S4*HH@uaJueAx)Rnt@^yHDlw`0Q{ zZPlNKN5$atZAx*{wodc2^T`WvGF7^M{?uW4W%Cb?R+<(#;jtcT;y)N?rF+?!NBcOM zsVN_pKB%m8N8i%I`h$Ncn0`A-!(5F+V?Ws4G4!%im*bg$0S%x6W?X~bVrzpgQVIh3 z7VC%sgP{jd=qx{v_C4r28SH}Z>@UpxCjF<~VDhTZj0k}t(bYrdFZ7G#e>C!Uq6dsI zo(Y&|0!ASb=zndTr}1k?&$rt3q0a9vpF4W`=%KsmVKzo8cTt50`*(M7h=Eg4w6j-5 zu#?f%3un$8mRGR@SMpsQCl^ng7j4-Q_C*DOcBXOOHhS_h2lgJgrL6kaQ0J|Mt+P9Z z*CzPdhR6Ci+|hF}ym{g1-aY$N?kLHsyhIwZBjoK_VIDRPfzHpaDcw>$efIR}6NfLV zDqOhvLf6#V$pi1-QpPg@)7c6dU-DeBhPyEb`iFVRmGp*yJN(%(&ocpo2&xYGRX}F8 znVdLit*gCp<^pr=wF`uWmYiHW*QUG*2^1Ao)xCX#F_)!8HeFO&F>j^xzOC1`h%Q=v zNM^?#!;Fk3fmYvh;?vj9 z7dAR9JY$Ba#9z?nY}hY4Yo~WeSXe}Ce~ED^|k-9a1%3uE~xlUs0Y@P{NTy)-S zX%#U28d>!8S6-O=-Cv{*iA|ZxGXYa%u)4e`KPM{_RiZLb2lX#ph?1EBjdJQHwo8ps5Dhq}YcJIk{ZVv;heJNpN_nmYPgvT~AxOkHE* z;*wH_TNO@wguB{VS=l&xMizFA@JzsPr1MO`WIgjtz_m4iSilDB>u9SjPxTG)@lPyk z?WAm8w!I+Pmtu^)eeLzFby-n?A&!<0wQVA+=pG|r0>$v&(V^Dj^74$x5SI@g+A4~V z%tG=?LG(~gOrV&3_~%c3RmB<6acL2u&L)PIuV1{@3(P_x`251+QVjq6b7O>)Z&(zL z)0Ff`Uwgyn4^?j1Mkl7EXJ+U1^bQO)26}lo`-H(`loah1ALFO3rSs&mp?_#}VsdKF zP=!HQnvcDap1FNca%Og{Pk3_RTLT@n(+@m-gCb%EGxzKtw5b`L(;d$5|Ce2N&g*r+<7&9TL46xTCJBxV$|*AUVg!@%|Ng zOGh{F*p$q&##ZL8WkwyUMYNVidx!ZwJa+22RZ&F^&jkDprX#ov6TNX~Yhz~g@!fPY z#s!&oHv5c#>_6*$)rtv+FYc#=diZfo7w%wlq}b!|k>J*lPimaK~zG2)U>Vhsj6EM@>`ox3lXG*Y}G48xP6ENr!sg{7h=3*`KaUE6khrFnDu_@+g#oUB2M~@0VDe=n zi3ybWy-}Y7_YSVq5&V=q6EGf3W6S5i{r=n6FCZ#z2iI&|c!^L5*W1(0+37^AC=ExYHrYIF=gEiaG41)lMB+mp)Ryi8c_zH-AOMzCVS8&Qq>OV8xc_!e!vY8;9 zAtune`tt08!YFqeBQ4cy7mkCfmxz4#9Vt9>e(8u1Xn38og}B?UxY`k#acva z5lbGz5e_4OX9A|@QpYm^Z`rU?a>1NAQ@<0msnci9U!?j*N0*Gh8jCHv_HJDyA|y0z z3d#Ta-FIL@7FweQDs5Pla01!$5ih=)*@@Oyvk|c zjvZ@OEn6%y{X58M_?&r5uPENS{|HMI=4kP~?K@UUE?Fo%Ybv;sr%jtRZ$8fij6$V6 z6EK*H$HA4$+UFm?{P-EXyW`{JgC54M4(^9<;xAtZ>Km(?N4|VP7h;z~;w|tm6QI%s zS?|Q>M)!N?PaZrj^DOt%*9pjnC{=*T(f#9ClKRCfN49R?xO&AF&9u=^Ob)127luRJ z>ZB}tb^oEQ>(;DZwtUG7h1!ppoo52hD=4Io1D`>Oq0+XU(i>K;T(Mkg-60hNC*Sb+ z)Xd!6TuweZ+8cWN%&whVx9mQt^xE9sJs=`BDJ?5EFOSK`Mu&(AF#Lm?Urc;lWJF|q za(Y%yUO_=okw89zkE;(k9Xt~-0{H}zr%e1-fO$CsD$9vpYgNS)`}b|y zAh||TTue+mU~w7h4;TlCV?`6-<|7L*BZY zSC5_Czia!lr4phdB4QE}A~Iz#;&8Y^KHg<(q$_{)$iWS3mP&|=i;7?Z@%7SJY{k(jqH#H6cPp^-?6dL@q(9ERD1=o$YKq<@-+V541(lc}qIo)rNpD=|9>OdI62g-R%cP zpF{eQl|VTWT#_He7|V^ukKB$zfBi#FGZT29`_8&hI-g)XdkdO`8YKcy2fV}Nc=`p< z*u>O>_#Mek;hBKxp^W_a>DS*IGkopMjWljv<(YubUR2Psb@|{I92OZ%Yp#E=Jw3?N zR_DIbjq8_AA3J$oR^^?wqbKAni4aR`pvvFT;w{ev42uHgf5;_|9CDNqEh!>{fwU8L z0ou182<|{&{z?6vl>SqPKg(&3N&OehM&ilwoRD;!=|6kBf0F;J{?i)*GeJp+RE7w{ zuptmDE4r*yRPa1 zYFCmY2Z_di`t{deCb}w8qk|k@smjaBD?Lc-BP$DcEUJJy`13En{XE=KoEYwF{or>3RQh-GOu!xWr5WKa zdQTtSSGscc?D@+IJQHwMW>z}rf|(L=*j+3rVB=T>o{@Zk2qYt&$yr7Z)#gI^4^$9k zS3?TW7v|^E_DN^r`Ty^764K=ZTF%aY z>VfJ1(EowK{_eK6&Yr;$RtN-Qy#F`-@9*Q8fRAilw;bSio(XuN)RxP4A8WreGPSiu zbuXZsdi~Ds+qFSbLPS((uF&G;n@`?)p!Hhc*v!@zHf{$Ub&f}NY>-+ku~1BG+1l-A zRUT`WaK`=G3W^yVh*lds^k`OVl4SG%>fecK|@1uIwG{T`i?$ z6(uSD9xiU~Zh%j?y1EciB9(igq;{&#*VKsOUd35SF;P*G5fNcQK|#SGY%uVI$jonT z0uy*uMQLFUS7t0BIyyRrlne(6PBK)D0SO0uKGmQa%E`<~&qzs3N~T{89RM?=sxnPY zL~#SE9}orQX=2M9>WFQmDp32lspqenaDOV7`3<`-e*08!-fjVNF zz#p8llvqp72Ug<`Q9$dHHB z*Gu(t@UNj_W`Ab-g+~IvT@%j){95CoqWsyz9gJdxH5ADEdjhT9&E4O= ze51Vop!BMB3K{L+EAMhzpCYw@yF}kfbD%c6G%^SGE?pKUTbQVAG0)3q(Xk z7H%};nSenQ?8J^%LOwUQ)@LVXW+w%^+ge!>$FPf=#|JNOg7dS}7YABnU1e!uW_)Bw zP#|kK!Gi>HOu&rmanBbwg-m%X=+k0R;XhVR|QEg{IVxa;EXEbajU32wn5NFZoPh`TFscbT|*GVzH{ zCNdHiV0~|UpWiFjsh+^@^W5Lx-#=Zu%p`NFt0&!Er%oNYu3``Yg61zNF+s`n$;oN$ zhW(DX4h;M}GVfDUm@8j-JX_m|A^;s|3y_Il(N^rBHUL1}!P%%Eh1Tb(rMvE7Bi?5oiFIJKz}^+@LeFpX)$G zn@BC9zXeBqUjNDeCvCww8u3j<)u@pm`JlM7qoJfEBe}S#tyRoygIW=0(4bt=k!Nyx z`^FWk_G>$pw-F;Ej>n}^%G>3EH;*6PwSMiY1+(Yxd{>A>cks4;mh()&Q9KiHZBBA_ zerkv-&jd_P1dgXuC}eR0iWP7*W;C&|2O&9AB0^5Yy^==@Q44hVGQgkZ@IchDO$=OH za6_;WI5{*JHF^V!CS?t?Q$R~`A9HGqwkBXyrfm>9P$hvEr~~OjrU!W@;N)b4n-m>C{_FRj2Kr?U^}@pR#Bgsn7YAEw`-teMm{=@O%{&t@ zkoLKaYi)IDerAdQRNvvDp&=o`!6A%iwwWpfc_v`yU7)#3lUJ!g$n+q#Tuf{<&P1U) zp+OD9NtN@T6bP5u!ZQJ56IWbRP(arXj?WfxQ$=2Kn7fCC-h=B}T3Qbii}K*&$;`m@ zgIWaEO?^dnT#%=W*)yH1T4&ClJm;C7j2fUsk~g;~6fGiQdWgT1_3KA>FKV4Wefs#r zSb%iK#?s9Z*0d{{%L^jC-HZ*N+`oGE^y!l)j$QHe3jok&eM5aiRBlIGOadoLlZ*R%pSp&sqR1?%xF{~h*TK|S_pbK2Qy5<3_*HXjdk1INy86aS zK~j}4D=ys2%>4Dkn>-V64qSI}%nKME9!5oP$_Aqu{=ed)qJkV?JQ9~Un8YFHI>50< z6RWhO7(L*+OHWNs6vW4ooHa_#uf)Pjj}5_qhn4J<4E-pOay|bgK z!AsryI)NP)Rpu?)(Ld-^O-iyw3=E`}pD2g9nf8-?wMm`qe9zFXowmw;j^b zzW?;Kp&1J>wbv!sKDv1F*pWkr4({K#|CrW|htFOanpoO8x>5v*R-86*RbFaBR7ilY z7vh56_~+y29~c};g)$IRa14R3`kG21JcNmYxVZRu0a^mfo1q*a%DHEb=Zdo8{G3dJ z;6zJdOB#|}D9;BtP1u1WvQ~jWB6jF`IXO8rJT;^;VFY##8U?#kc(>sTfI!&@%$v&t zLab65>#Hbd+Jp5_CKHh-t{y0#U@nD7KJwC%yLb_2rp>`}#XSNPA@xVU@V52s`B1xRexupS!oWjhWMXUqweR zDzJa-X%iK+R^%3xW~anOCB-`0`FYz|I(z!~Dij^4BI@hw!4ue!TV7g_DhP9P_6l*g zv3K+I4G6-u?(C)3E^BG1F3S#fb$uTg?(gRM-Zvm5B08FGS0~jWp-Q;5UQ`07YN`OS z56oaeVp38v-A^nnL?DlQ*xJ-s1ENo)X5{5$XJ?_YVik-*cbM4UD1DAltS~z%$2PB( zJrC%BX999Yq9djNHzk0e6Y>-BTydGNaspj;4M7W)^ zhfe^{1k4t1EK0OUL5X7W{D<`)`Uf2_&uClxpVxm+9zZ%K0x%&aPsIqke!JgL)ACUo-nFay3nJ znD*_WvI5qqLo4#NQ6$-1zE$l-0Lj}s2iI?Q21=DeR)M5eecnAYcKqiBo|!?&qo!-r zQn2;dGtiEr3j0s%KdY=}JZEy3MK9jHQ#6&e)7k?gYVaG74Fy=)^3w~gO$@|(+a841 zb;@v65oe~aPr~Nbn!qcYm+2ZAWZi$Nx%Z%9W(9y20r*GkX%a9jlW;1r?c44An(`+1?O^{3IuT9_va9w?_>gdrUM~)ghZkMAcIv_5!489;e?FLrX=;;?@5}MbhQP6GbXXeP06Y^gsTR`D3VEZv zEKzsiq6e}L85x97paixwD3*0a8tUp_*|B_qZi}?J4TX$UEIG&oPQ83~?>jPgoQm3H zU1@Vm8%jEB>u?j<=n9#<$M4{l1xsemn4qRQJ046Y_3#o^qG9@g$rrw%JzB22=FOfn zY5e$!*UJI94lA;tsJNINFOzo#PTFL4blI#a6L=Q8TNWtwDui+=Nr-lHvv2l4GnNFwG||Ug*!VK+S(Vy7~g%S zvBTu$^VAx6c`5S?9%50Vli~em9^mil=_|rukJpLN>0nl&XOx6se!5DC|g_oyJs(0 z`|d_X=oef`LcL**IKQBJlGzG1IrRFHEz%_Tt1^Bi#pr@+yL#udKrSZ%8oFJA2i` z+uFj#Nypmk&c(y8!E+ms3m(wo5}CZEAlCZqc^&(3SMyg_PrZG5=ll`B1nZYiV-gbq zge#ZUXN9@iy)n!Uv44JQ&yM{kj&56V-PiJoUU*DwY<#j@R*@Lwp6_j$;AoVSzjFto!m$wfu9eV!W`1%hT8tZqcU%27mt+$`wd3a{|x^>@A zKlxJcwvMy6e-NJjszmRwXfLxnYa&CcD=Nvp`;)! zIyfL8(9g>Q_#m$Co?gCw0l}d(f61#2=VEDIMoMyWVq6pyL~uxGcw{8*AKPJ*cM#t0 zsxnM|W%56=3ld2~Fb^a*|5+7sxex`!EWsd+B^fX|)^h~rsZ1H3LvTeQe3_Mr@Fmm; zc8^N;HY`!*L%MLOM4<9-H%C-rUhwZnqb;jPtZtjS0#!UZi><+hO!yx~9=r<#0=^hz7cH~ks zOIxX|HT~YguXpIKAG6hB>2S!0jT|y|{G#P!hEG0aWM(OqHwW#T@{RGGiGQ1TaK?}! zqel-LK2&w==;`w|Uwrz;*j%cpemi5xS3j&7{8p1OHr$Cdi2A?1) z7;gTPP0BL?=OzdeV$%!Y|1B;q5h8!$xBvU8p`^N@sX+|Br{albFf59 zr5%6&sjgC3SW(vmNT_B}TWejSAT=@^G@v{au&r$iQdr;=Cp(E&3b+E9r~Iei0M?k{ z?r00&7FG;q29t*yp73t!(ErCDJw2jWCmXDPFo5xpVi+8cj2=qq>Fxda^N*j};vKEX zyyd*s)PuElHp0i>f9XwjuwVu-Ta(xjTo3Bd|I<$&ifxU_{ia;G23IhA)lR^lcYWw> zG`dRxxX)QSIGTmb6ciSa%Oq7dPtx9){zP^#Rz`B6lh3tXA#1KQIc~}1`0H639C_F= z81+Ihys0KHw}5JHu1K zwhhQrP@({m-UPOIyG$Yy7MIqw0?H5DZca{kA(90St#VHJ%AL zD=Ez1+w*-afYgiga~Tt$XyBi}|MD}ac%>*6NsJBm_wsc2NGK;cWBQc*@%P_<`SC-4 zm$be-BPuS;-^bI<-Mg?97nmqg>%0E_$L~LX?Cr ze%{{RK5hp3$OSaFLb5&21PppmAdIkAydiK66d#7cB_yU_hPg$^UC4-{HwHGsGXXnQ z6Ivt9e^GUIW@4C^z2Vc_n#ZW!QCY2SOK_p+RIsPHp3 zczo-!)(M^o7zm?@i3xF0K>_|KG4rK7Vl3fQk;K+>Lbsv2a&h7KJvbl5PJTjd0|TFf&6 zS5}l>T+K59=N81jJ-lk(j9If6tlEC~UB8 zI>gV@#mUj$-rf%LnuaI>tQQTDox#w9<7319ym=;IVl@L3tda z6)R`WnK^U%jOjCGeRo=rnv+*pBJAty@Bh&J>aNDVjo&X=G=I+QIWuR>oH=c&d3Zu* zUO{mQW8(c-`tXYS>eb5@&6_=Y_N*DxX0F%u42e(6=9z$NQCz?^P;#Qkjsgf&)5G~8 zQ1R1+SOsfJIUZ@mOE@`d)tWj7s}I2FNkw{yr=SB@sz*$}om9Zg8aP2o4wnZ8pd448 z2^bf;i^@Bw^?#IBRF#O@`}=XFiGLMwE0iL1bJxG`gUI2+j?Jq!?!TMf`=OiU;PQq? z6mo={2700|@7lY5{(_lPznyzi(9uiWs_Zrq({ArTqvgr{2Ue|}kJ&Y8lG^0sWqs@( zA&U@a=9z$bCSZDASm=F#1Y;|M!+GBqb{!+{h~rq=)A!-s($qDEz2UzR%Ff`d5ez$Y2a|BitHzI)B=xih|1n|VD}-bH~ARFg@X*ysZd zrpNc}UA}V8w5i`sm@sbQQDF~oU*M*$uceds$*eAG?%KG1@q(%IzZ ze*m+mGTu@liCDv9N6RldP@y)$U>F63hJ#}ESh6RYX98xIfwNP1CScMeJQHwvdW?_F zi|bmaP8`|y!ycXqm=K2YLG+WILn%mT6q<)Q3D99U=%IQT!cb8`UM?r4f?5LiKv^=+ z1k5u5A3c8lO*DhKN+wID>*L1{o#M<8S3A=uS56*1cJ%163wnVep(s?L8iDqXfj&iB zL#m&H*_-e0VCamGQ(h4@l3$(nmiNmc-3(uhmTO< znSgmFV1h|NNhER%DT5peLebsd)7vg-tjUY_@=UHH)+UlyGP$CotN-U;fBw|dF0D(C za?&?+FKZzz#%fU&kj((H(e>kR|NP^ZAN#x7L;_EEnhsmck$fAtP0v<76PH8yKmr+fBpNvej4cOmgL92x8j+A zFKfRrw}t)S>mP{1131YC`un@8a#90$CSW29ts?uMX9A{@N1h3oAv(nKOu&t4Z?(4V zI(YcF_Vr6Q9_zg^GPB~Dfb(*&Ho|X74anC#6Y$pM)2B?DIQ>9EBRNXo!lmK{?1AH6 zI+@-+cYOE8#Z%PAsZ5%_x1g??He*62Z?AE*4K#at^x(E-b0?2i89RQ;Ov45d5fzt^ zT&74fb<2H!ci*mcOJ=F5jvG5}+~kubl|XAK$b%e3XrWedO`eZV?AtJJ>ZI}GMysf( zY>X-(p1*Y5msV+;uYXaSzh}f@qGXK#nUFJju|^fMP>5BXHG62s6z+- zC45A__B<1C6VC*!tm`47&Ostio(Y)vi8%~jBv(kA0sSKZP9*IOU}lvRL)s{ow8&)b zy&rnIS{o~c*|}9snEj$EM%4grhek=8T;BcDZ$I`bTIwouQ$mB1MYSSeI#6mRb}CJ+ zGI{Use_#V3YpNBdrv`a?hL>|#u|);Ia%^sscmMInZ=VLb+UjeC83`dCZmz!hOnv0$ zpd_Y6s`%|6zy18Fuf4gxx;Q-{*u&M;-dT_brpNR&WP!BFy8rd}Up{^4YHkvh<)p*~ zc)B<`*m|WR5tL^FMmbv(1JYM0z%XA?l$976MCo(yiR#10$Cn_pnpqVukl(~S6EGF- z5dtx7F{lC0kQ%D`2)K~8A3PIqE(L%YQF%!*T^blx5=ldCQATXAhXcIicO1*={wgPx zQk|C>;q7dqt9$M2`PccBfy^@jJGlbM60|jq!ldx`cKWX$-_yQ&<t4(Yc>#wKPKRyKAHPT}2@5o_ zu~5k|9nCe?0cBrAvqG3^LRx>>=$N4>{YFlpgy;ouGdUo3o(WhiG8wng(9PlW619n= zhmRc2N+5V9UslH^%-<&P@Xe9?1Lce zED0LLo_~^y8%YWma^FCIUyr=CuB@P_N&+Bu`kJ8YAbCf3Pydf_zxQ;=n#xMDGSZWC zs@u2(KVkyy=;`_N+pnK`K_=2FuB|C8%1Molj4!CeABPx%2zWC;|LgakDfbYFe|4hr z!knb=U|;W`#G)#M7%IDYCg6Yl^OuiZJQFZ`GFu5-nr8xLFfL>oF#Uk|-G7+>sDprM zK&pjOdPzmFm(TN`@-@Xg6L3pSm$bS#H`&kI-__04((ui5-8)y#Yl3U{)F}(>QVJ)X5X44Sh*Hb~R?FCAfR}1De3f+EDk7_Jz|L z>gp#>oH}pj)*&zKme&_�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

      {h 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

      qSz@qb{0SBFD0w#EPBrVW503vmi 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 z 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

      n%v!cbc4Yhd)vMQU-2VNs(;64i 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

      r}uB&xN+s`^;>rzy)ZDfv|+o4xUD=b+{?kn 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�>&}^f9fAf0FYlpOF;{V0`q%0beEE6*yQ6YG5P%NdiYAxOURLYV_m$+1n^A2 zU@|5D&?jha@eF9~F3L%Aesu4w$qe$hHvEh!kk6wgF#b5z9r8sF=r}^3W$+C9J+(vcsyJ{ud%oEloN>vg>;z5nfSA89`s(I#de zIy$Pu@@;fOqs^XQyn67so>xFKAe~roHZJciP4M$JG1IfNFG{z(|3-R`k)~=wWeunj z0J4w#baSGK)~$OU-qsc_PHNU>PZTe{1J7+h9%u$jN_z#Jg|XJRZmZdcyPChfCxaT7 z+n4+jt#w|-Bqad|SJ2ax9p-AMYmgUWuPt-x*jeezN46{bT54#8$Hd0QrwDqhl7id| zye$(QP1J7OP?q0+?E1BXJQJ|=U6dGGJAx^=JuA$^#v#!8rHsOzTW8LlIdk&pC8e7e zuB{r83@rWocH7S=>^;{L%02}U}E=xi=6 zOpgu@2nh7^@^E!=b#?dj^7TUu#;GTkVsm|YekSn2lH#JmLV|&>6doA~CT9kthf}R~ z6bIB)pmArS_&*^bkx;~vIf_80|EvqSvJCx0Xb<9X|8d5O4dPR0Oal!6^ts=!=9z%s8JJkwG4EPz|3P)-M%7o3&{^=} z<%<^@uU@~^GcvJ&g`fbvyQ{aWHZ?85-^10--Nnwz%)}HFZ;n720LmGS4nP4k*O#IC zIw3xmh=|Z!`W@sJfTc{USBZwg;#q;F*AVCScHH zVH3p8J&=VN8U=^K*iWO~A6tX$Y;8Ncsqzz>e9D9qulxYyqoZGc1z2OIyQ3}E7LG$G z%;K4V>7)(XiZA4_+u9my%8HRB1JN%@Fh~ta5h10WI?n`LQ`3nEs8`Tl-&&Uy6&T`Z z`9#AevWj@XsizUGTVUtz9T{vbE-%lB3~~A3p>g-tQ?rmf5GR$ESKx#UrfulwPlBrA zjOe(uh)`z}Lrd*f+IoRm**UrSg~g?~{PWL^5l+5gQDG6WDd~~E_J*2I?%uGCPE1M9 z%+Bip?r3A6mxr@YSZrc)QnXimjGuW?2jdZPNu#5<)pE7Hv0*X{ky8|TzfV(xEomS+Mc z|3g&q-12AssJpYFrY=_f`n3t?L=#YV$p$G&Pj^#$d!d%BG`TM>#>NazPA^cQMnkoO z^c7bAHu*aHPKQsJ(%D*H8gumoHLGzxkedW4c7}MBX97k*!B7BU)F;9WR=@|#gkoic z8xS*jKeA=az{3x4Q8!fxvI0VqlXnpG_qNxSm6SJj2@vhD0zk-dInM;lGXaxsW6)V0 zl~3Vl2qmO>T58V^2@8oLs9+&3CZNTQ&W2FmiDXyH2X(b8qVq+tIO*lz1{Gk@gleN( zGHd4wvHGjYJFrMBO|S@ZQ7vH3a%n%}gRBi+JyN)O>Y(J_T|0O0IqFnPpfJP) zT34Hsl@#V>Z}95zjVnj@AKU|?-rf79Gb>Ar3mH>rd3Hfzl)H_Qx{}PrA3)VhM85lv z*p~yy5gf|3^~HH*rExwsZ=WklUHoDH?p=_BiT7DHu%w9Tx2~Y5tSZIRsl$@{_evf*`op>Fiua$Wy?R655LCLr%?f^Z_onpO6Gx97JAU%q6*)zf zCp;5yd3k;91V|C>$+6P$7oaIXBO)$gPzmD|Y6ZeKhNZ&h{BA-8BLOF#;d*d@BS%SC z6NFpD;7-d*%Sp~gV_Ri_nyl32TiStTD1WH0B{0L^wZ;n5=WrKvc(46VhW`gZ^ z#w?L1)paB<<=#90*1`QdCB#K|CSc_MLB%^|%Jdm?mOeH#GO4Jlt}0jDwQbXCiG_3K zOof<@snci9U!wFzM;F>xQDd=f&;IR8gn1@lUvn*W4LxInH!oG6tE#EvEU}0RwTl=F zIB2{xGt!e2V}re%Z7fWT3=Ito42xJPcX1&w`*Mj0oS}n8hxmEA@Jzt0ymO3a0{%E0 zVDReJ`Aa8{9Nf5mm4x_GiIpqFZvqpR@UcK-{7K+s`u_IWv!`}%Tfag=Li~FPiDk=n zCc*7tkbKBmOXJ3oV~4hEUL&zwV(HRl%a$*bOk@=bWuRC7_{I6b(^FRuY}vkJ#j54Y zC6-AnTk-w%gtXlJqS7*=VE)wc_NnX{$*t=)tX;Ko)$(P_moMFH9-f$$UszI#&xdCM zCLUVw%iGu01Qs?0XEFvn8GKu3u~HuJIUEW8_xtxF&BDN`g%Jn zQp4O#wVxjqX!PKw<9ii(8L11WPM$e`N$#-?Xu?7xV&dre z_V-n0#Q4~}QNAf7ed){(r_Novsc!4?!7n&0G8Tn1K$&Y#5Aw9td8lyX`sFhxPF;|` z`_9_Y6Y_{?im7pjDt||dw~rO&Zc3j!bK&x>7iK^hh511f6h23MM18sT2AYrV-nu1y z;fgfG_AVcM14F_hqFB6)34%gb1D!Wo&*bG4pXl4Wf@F;GC`IAj=`9daw*ytPG$%SZ zC?pI7Xc19Vr56{E-=B)71jL}nGXc|s#M9!6HCXY*BK2zb`?sF<0ff($CgfE-Vd zeMCGHu&;;VlUvf#*Q8}te31a994kGw@xT81mwyP#*BsHZilbv3GKD_xwQX6vatc)9rwUugHxJ3Gnyx z_4f9`@bwP}#2&MgreIIs) z9jGy+2$DjJ;qj5t{`Qvo{CF?Vl*Ud5<5F8&&E$OpL*qaF^3#`*{+`B+C?`Dw_li!K zV`BSaFvo*KKmPi+KmYvW_)vFUf~VPQZ5``cbY}Vri5|owz z2AFvKK;+dy70whe)1JPg9g7m@>!?d0BCi~r83OZ-%9c4~4(b%b={fM_sc-=^cqU*1 zfyC(V4SRD>{r)u>*-Nq)q^&F3iHexmzBqYbPoTBCx%=DKZxjz6mRz&pW=8urIrbxh z{#2{nSa(MgJzeEJ>sN`d+;FwD1(gll^++bP`1_V5#M)cwKDcvaoy6iL%hs9GM-4h9 z79RGOxQA4yxEmR3C`s>IvjjOXak+BTFyMox54X23*&}_R(A(5d<;tP;;$p&L!lH*0 z%8H8$3-Vcf*%Ka7Hdq{K`9|j8&XtQqgv5kJwt3}dQX>}ypxs>&=@kQQ!8#AlOK#ur zy@;^b0@01u3BUkJ;+cRcS;n9*(%;`aa^n0&DHY{A4_<2N8kt$y*gH6(pcn($GM)*T zilb42jeQ0xc4|<$Q-@MN=ne(jG`az7$2}{WfYKk5)7}STVg|um!cO51bc0%wa{)T; z!sc)jfLB1JK~(003z>qFlG64cy&LWAEx<9at8J)7A<=K*!GUHlfv1$Tb$7KgAXDI_ zPcr%9+JStN>qjNG?>wvGRM|}sZMc4t$rt)B2z)4Y`NTe+33$QWd2@w?7Oj8nE@Sa=9bt4!?ARBc8BO_Dk@%CC$VhN{MjJu z5)xguTTRah&F+?15*_Vbxi9bEJhy+9_~Hd~X3d;AS7_0)!_PF{8k(3}&~j+&XtPqe zbzX9#_(GvMm@rpFe9eVN&oRLiawuPWcfpIRCw8x1x_H6-dGmx8e7}0<4dtgV-n=(5 z!+PofnP_*k!qNSkR*5eX6BZU-wt4qurH5*oI(qs>wBV32Z*LEgJ$PWprcGP6Zrgk8 z*rmGHx}L=I?Y0go?{?*LA0(BRbB zGf@Ia%>?xNZ=SIrkutf&txCTZ#BTi)RAn)VmBE#7xETg)<;1TO$K9Nk&fMN&N>n0pfOU0#kqJKa-Pg zGySIt*aDIMV*(6JDelji2R4V@flA}P=|8TYkmFN=GeUPG&jft^n)DSVPrm>hX7NnG zp!&s_SKZ6 z;M{i^j3aq=ilfMTQ|~b8KlAV8|2sM+A~d=OSqRpejBX?fA}|4Ul$?2B`afYpNdJ+% zBL_}am#IFd|1^Pm5}^Oc#K8a4^-M%I8uWx+?eOsQ9xOg6E#aAf>rCcL8n`)J-y|Y7 zXT~gaP=M@r=Ip8By5?3q6R=wYqY@^Mf`krwz6!Hb6QaX{0|Nv6{r&y?{2CY-Cu@Mg zilW}y!kqNv_!!~=4+{+q!LEX}ncxe#y)vj&Nnsv~AlMu%Rwxvc)kzZXE+UPE;oZ9yjj|DVrTy?34l42k|J;go(UNHjKSfNpZ@-* zpQ!c_h<}ZBl|{M9;laM%K}p3mIAW+C9{%#rzyJB`*zf=zVOLX4X-QEUh|}>}6&4?(I80 zx9kp(K~PObb0aZ42pTH#lH%gR13oxAn(FIlK36jcEhn55SY#5nR+VNZ#>Rw42D&<0 z=(^yvZ}3dOxj9&f zw45oyz)}cohg(N}9xlA7sF1C7=DMj&2n%|03(O_RO1W!k0xKiJie_760`Oruv;ou> zliGY6So#x6Mr@Z|&>K##pjq?-&jh^Zq(*v1Nk59 zbk&r2CSdYbJQMK$Qzp=^?0lXH_+S71s~{&THov^OuCb-P3jq_5JU{(7QkxR&;N;*n z@VEc@b5BQ8Moe5@acyHuTZdp^7(sP!ZC<3UrHzfp(8vG!`#?*tpsp-0v$&z8vAw&0 ze5kLbtRUOp($>n#ZFuaLe+*YP3=H(PHP$ya5y^6MLs3aiQn))Dx{b%+*r(4wjSh_r zk2Tb^Rn}Cr)Rh&~W#@(l`F`M;fDz-P1PK0?i(3Ua0B0F76#7sK3Y&m3KonwU$c-L& zWFn_vhx7<8DGIDM)~i=b3;R#44roTX-g5GdscO&t0iT&$K=yv;SrXHWD7}P(o8h-}0jjsKg(gJ`l zVfW8Qaxy`RayX>4VZ;d&3RjQ-pz6D9@N{JT1~E$O67e|#aF#4&+xvn7%9PO@d?9-J zi3!kcl(&?YmK3x4e}X2(;B3R>7Jxh9Vz09_{KNqkw*w%WZ=9i5lw;dKmvO~ANOe!_ z5Rh)fAsf#Gj06lhM4kzF)4m(8TtXAlvhz}d?X;g=Kd@!RqNUqav;}oBPD*FbUpaL| za^Fdri}Fg!4^{8U{;+e~mK94@9#DDJ)0biU_~Z{VvR5x%ICbWd+|_GRXHV?jvwhtP zi52V5KYR@q4S%is@(S`w=Pq16dh+O*9}ez3vSZt_r4p;wAH1dh7T&8f>ec1Dm(CqH zcKO8K{gQ_^u3och#p1;)w;Z_sTwAZ3%vGV`?UM(0o!+;3+n%i(mWi)iv1HlC-KP{( zwcowRnGGCYQ@G~UQzs=i?cTm=-MV$F*KFQ@LRMK#Ti3_}QXoZlbvLIvJiBsy|MvB3 zH*DU0{L;+_>e>e8wl1DL6EGqN3NN4sqzW`B$%dvzmdEd;$btnJ^ctpd6DaRb;Rc~} zBSSHHIbF!jzz@{J*V*}7g&+40zfGWw#Q(wsIxzv)!*u=cOrZ20c&jer^5O1^-<+lS zdb5Tnpj7Br>BSpxa!7%eGBP%5t_^Z-6!p^ey{PDECpmJ5$kX-?ch!0*+;OFQb8-O+ zuSv`^0gsOje;)3sPVlxedj0&Vx^ZZ1QffvHkg4+VdQx0D5coHeEmF7SCsB! z_x|-u6R(ii#MHF(^i0U{$x)7d{Nwn?p@ys|FIzn=ORumvT%QI$W0DgN1jvd<$H#{| z%MyI-Z9jy@0-yx6PWgq9BS!-{CGm02($`T19t;KoUsPONT1FXo{00*fMzMUlQQ<|^ z-BoO0l^{b*Kom4*lxG4ayTIBG=t~#rKQka$2N>9iK+bj|>(9{ASg64E;*f?8MiihCSY>d zw2i=a0CoDb72%nHX>G704nd2cB1v=o2G!nyUhL@5tbp3h3IC6CkKUn315G`pW82ng zcJ_4OEEsz!I<3X!kkUozT4CMvRE zWqc=6IM|^9Pq~_c4}v5SoVw|HV$I4`3x$Qnlq&(yhc>W6KtQtVd%@PV@POH61l^)M z6R?QLdDXX;jxIpr2n?quPv^C8JXqfC={hIYE<32IYhmx~>ItTn1jr{Rz+quCTo&mU z8sr}s6dD1Lvb1#0I82_S^>pDQZLY6E6;5srg)ey&9Me4z6SA+gFo2`qKz(+o=fMdZ z!e1t0V*DD|#bcX|bN*VuWmmCZZSo%Ui{b{9=|_tytL$NN?h6aEQ9}UTB=!9N$=NKP z37E8Ps3-SE`T*;TWYGtG0;Goh1Lbk4eS^b(x_Sov3=6RpPmMmo{_e)A)}hg!oM4Bl zE(gn&kr9xV;92ubz=FQ|n1GZFKbyBVF3Y*t>AgNAdHl!$xtnS}5wQs=pFySaOKdiwaIfEpP@ zEI$y@7}Ev*L1WVt2uiUlW1>El6P3Nm)Vouk)h`IFCI8@ z?!>Y~QK8lvQW~D#zA(PEah}H7j-GEd>O-C1T{(aJ%<&^C>0vfTcU4e@ht=I(9Ae;9 z6z%L)5$t4i?c&+9N9FF?fh$=>$H~PLi@z;9!oH{=(9Sf@+eT0B>Y@FI?kFm~HPm@) zVe1U0-|pH3U)%6lABX#TE{5_KkMG}i;O>0|o(VV|7y@Z&j47Dm<)FNPYSL;d%UFS8 z7F8%_WfFHI#jjWg1dYj#BJmG^7IC%v6dZE_F3li0P3i6G>LqzwD_)@)G!W)e86!$v-6aGdZqe7a~iCUrpe4O|^Sy*hb~KHrzknzm+8d;s3j8%bEs9>rEv^L)(mX zn}>(UJ|Iw|i~9xyb#?D;8rxEWbT%y9zea5odjPsSj!Wp5?yYSq3&_xvu?tsOzhcIM z6??8ctV1y<+L#*~1-<=kCMOSD>uPMCxzJo={lW!8OHZwzYg3ML?lOSn_X-ALuB;T^ za!Fy;yw#Eiw##f2S+e%X)m{4xGcvM)v<-N2d@?a`7yOk1^N)&3&~wyxi~o%%m}dfp6JYL-d=b19cqU-zKb{WEgP^0Wwmj80 z#K%9etQGmO3G;!rD4q$}(ZR(#Dyz7RXvGS0(vnM>nxlgS=U+`?H%U#mVQYu|3t-QL%ko9q5e?gj}-P7Y#HOfT_Fz>O#-swktv zp5G6A9;6X=2}I}=lxPX&94U{EH6bS5_70j-|pppGT zA0SE$aew>=KryyofXSN((CO1*IaGQRo(WjW!8as2K1nbTBKuh7{Gq-34jsRET~S&7 zitMcm+qZ38DstSy$ul6V$9d^)RjI>=j-5IS1gh%_50vDNUpakvyZB;}Q)ae~ZqIlo zU{Zbz@~sFTc_v_{ssPdgU|G@&M;XJz5S+hJ` z0oe6w*?B#)uQ(#XK#edA2D1;;8O$>QS23c%+OA)I{ps_^;l8%IlH8PtKp*b}U^X2m@#X8c@2jX#qBhmm+*niX{%z@Fdv|Q# ziu>=9Ja|Us*$XY5N;aX&&shKEBQW)zIshKz?K^lT;HV(tBk=X5ItApS(M?cPT$G;& zwoyhhkdho59vU1R6o?=K=K|DofR+FhJ#sqELGXddw~XY76woqE88>{zWWv`W44~3d zo(Y(qOI@>_g0l2gRL#$yIeqFB{5N&l3{k_n^78T$#`ImHq50y@rK77C&YM1M3Se|F zVaDvOsg)&+3B0yG>V>7V>e&OE7R;KC>%W^iWy;j)b41>k<>q7ox23w?UEAIJwXEbS zVK5O-`;L2qnRBHwQW6uYD=KShZOpyG3@&e5GJo2PDYzDYr%anZdv6$UWGX5uYs=MM zIC#A|uu631)M-tg*zN#NGfw z1O!1*v4dh46zRS9UZf*cdhbnouX}GgSdtu*obrA5cYpVp>)oh1=ey&MJH{R3{`0PL z+`yjeUGL7j*Ia9^IiES7>e>SB2jl^HFE|Vrro&Uhi#kHd9bxonl-gO%` zFO;1*i81|ha-IoTRZCaz8hCt5(2C&eOMH54Aeq6i{>p+s~yAW0EV=;vCl4W(K~;9|Ib^$U%6q$ zg8B33&HW*q1wi3yp}UV|Tiw)DIPlZK4J$WqT(oe}f(7%|r*+{Rh&Ty-Je~=7s9)6I zQ5fax8T2wSAtpK|@l{54Zhm25aWOX|Fr#R?0p8zGjrvy<{h@skD>TVz!VL5y49)O) zTp4UCMGFV?Yli%1lNQu&a8klUMou^slf(HLdm@{!Y|bK+jKTxVyH5@`B9f@DClPC4 zw50Gh&1ue$28SKb1l&6SB2bPAl+l6GUnoohmn8mA&kAy6?{G5q$~pk`&WXG69!NmZ zVUQZvgSrerPKiu-XZ#zWg!Eq!UBTco|DJ9l1HnCLDU85oZ_cQ$-@yFr%>o5mK27?9uYTx{Qm7 zDVR-oD)&Q$&DkQ;_>t+8YvK@Ra_S?7{7{b`cos?LAg52qeE^KXU_atd zOL9CsuGo zTv9r)W6SoX->g^$o?i4=8rOg$8>&Q55xV|B69eUaKW*Eve!)CBX(=h`S+gXM6&4p2 z6&4mjKHB4C^;~Vwt{p3v&XbdomXgFRWR?Y`q@<>%XVUdN6EL;|s{BU#e@i0@b%TP1 zo?bygKHE`91z>MNd1(7biXdWdbO6Sw8lrys2R$VHXSD*LTqtJG70M+MYfu8rcj`d( zaB(Ayf)L!em=RQS`i=F2gV#|Sz<ZY2ti&`N@Y-iG3|=s-{JxcUM5 zt^haWn0%QSF;1Qd`1yl7=ar8iJE?G7LCZWRFE1~@pnz8EUQF)h;xJ^_-a2>c=+WcH zkDWMYf&`h2%&crC@9k=QT@Yk>UtjO6;^D(b!KR^Y@-pdFT3T8<$p!5V)rAS}Pj6k; zRyn@^z>#Cel`oivN1;b*D#=Byl_i-GPDWRC)J`1*k?-MSr**AB^cxeOl*0BLo(Y(y zYHJh61WF}DCeR10mL(*I0Tgn9~n_ z2~J80wJ;c32qRJpZytdI;NAnM1S=8?5RL;p@F&-BU%Hls7O4{&_d6$Mp~cVSBtw*v z)<87Yhuxa}NiQVi8pSfH!(Zf_5|gchPT~%fu}D|4a}(JYz$o;J`Z@i_^^_+(uK!?C zYpu#j2=n%ee$B1b$Roy5DqbFtDK2U*O^B>|%FV>^#?^BQ2M?c6(z^NZnI*~nx>02dHY?8r%;`UlB}BnPBpkGc6y!lG z34s5fO{}4$eg7$>cQ^(9T>MG@*%hqA$7}zp{~re!my`Zur}%ixq4%*seA0hl{JY!Q zI(zyCsbmj*CM+Ny^`B<~_Trg<<)x)&PLY@_Ih$t!=9z#gjhbfy&dLG>GS2va{_DU0 z`|rODcGcy_`#t+)BzXKsP)bhG4E!g_L?pd@eo*_s=ii#4jLilh0_Urq>p8E2fIL~Ldwbj(m zYUrcJURXqA6pH)|4ULYy|K;sqcU@6>n9H-<=T57tpL^`=?i~;sPV&LQ(Xlsgh6F98 zIZ1)G_pfWKp3%5$Ztv#h9~2tF$~VDxJKEn~Uz{51Vq$Pn6T!8IR!9K!4-5{)o&gu| z*y!j$OrE?b@nNU)RhxpYG&)?$un@~Wz$w3yI*@+S*DjeoOIk*H_L}FxROd2=9mKk0y|W@iY*mjO-nm##R!T}rcK*6&o?bp6 z%3u{Ldt;s%-@K*!{km-%=1NI}t8>=E?IupH9>{j5Q(SjX-uHuIGbLxuKTyN_Rw(NZu>Vu<*?Hy>{@&EvpyFfb4eWO!-q~ zAcFyN1ah2FBWx4ed<>KhZd*2Ymh{YNl9H0!V~dFBJ(G?>J>7x9CEYFts)sktmzR~E zFOV0f?04EuL<(YtA=ch%4rPejn)z{!#C?h7qHWcR{{{4^N-VFD)HI!$j zhxzzKRZ%%GA}J5#K+ym*};#rbPLBx;s1AIYr0CzKl;myz1@H`+t0RJ3P?Uj3U?>38CH| zZZ1wd6L3U0&jd_NysU|H3&|!2!4Rzv@G(*oOKJH)<7j$mff-+6@Fry9Rfy##QKb@| z3AheH96*GN=;^@m+1c7wU62~-AJTpAl)$WPGH7&K%r%x;B$H%>lkB_IvUtTW~c2pI` z_cyjL7tWELD>YeS%H$c6+rz>1nvDYj z$y-{S9^E>3bj{q^va_V7Op%b7A~RLaBm@4Zq$Ik1Z-ud~%bjE2fA@`~F0{FoX96ayZ(R+(&XS_s z)YwpeZx2@&XJ;1|S9cGd2^isdiq;{(M?Y92rxK2x#92gjav>on+-Dsqd`R#ef+di9 zu9w3DGEDuaVG!R#h@IqAhJYak>5q>CA%qVcBqxR+cpSJ$J-E-&1v$!;K>{BzVHv4{ z7(t6a$y-}U3K#MSaQ;JruExrulA88jE+~rYN!~X&H2VIRv5_HgT~(IlWM!u2)pi4p zfKLZUrKoRc=-r3Ey&DDuysNdLzM>>AJw7I}s1g5hz>4xrz{BnT>t8rN2)i5Wo9ily zUd6?QxO=)g+S%FLJGprHfnngE|M=}KiZnIVy)G*%&Wa25_5_QsgT1|@lOxIBy!-vv zH+{VwjkT4}(j~3=~Xg9MV{7o#JZY;w>M#LSTSTjk*x_hS>xifY8tgoc?p2C^X** z1`k$oycKc`@tN8}Hwha6yd@Ag#MBFYr57~(9A6~>H)5dzrvKc>=lXo=LZB#~3Aj(# zjL=nKL3TWNS}7KojG#`=LFZ3zRvoAp4!s<)FA(04^Iyp6Qc))S9Q*w z0oSgwvWf{JY~6i54F#ErrZzsV{yz4W&mI`)UC>ZbQU(<1v`HW>j{_~a8OdIL!2!Nr zwst0lS1)RwR#8+`Qc^x^>DebJ9uzc{X2pkkdHediA_Qlkhr6pNDJq>(xnyS7C(04@ zG?!+^MtHdRy4afEyLnUhjJleN@~KnGYCIEg25BX*sx|yzI;jD$<*tCf?Opb`gqeQSl#Ca)bUC7UUOT0ibzE z4f`RiS(uznO*9JI+mWYK!sa^7R&4FG<8r{#@mODV6Ky$bsL3tG18qep zfTojB*f%&l`nI<)BiO|&u%BlFW+0|vQEz8+ZDsBY4-enas9;YI-@uUY=(spCo(aW7 zN!?vdb!Bj>rY9lxk&uv>M1}atx~2mpwhbH~yQo}$MQIVzgMiXUCG8ygw&)9^{5Fh0 z@D=3$0AbH?`UM4DoOO;KIOh_7JRxA9!IWu85g?%2*>{E)pmBL7U^+Z}wgP=zKj`^z zOAuROXaQ+yZfX2n{;30_I^vmtuifOCfMGeq|0EQ4HI%%1;qCPNil)|u8}}dFe{9M# z0aF-GyuITQ!GlKU@6WXl7J_*HhY5fVP(m&Za(sU$`S|`%3mu9dGq>Eh{4e`II>LhZ zsJR909CR4@##ZR9RbV{uzrrw)*l+ zz#Dlc;P!@sI8XCO4~?FiTiO7=NK`%%QIrJ-tsr+V?DOXOs*=3SS4kl1K}$+bPNo9M ztV3rf9nYJa>R%Iw8XhVCE)9*9bj*G7HF1AsXR1D?6( zD8K|8qO1(LZRD0?jk(lFPMLh-5E7;}eLPYd3ewT_bO(yWvvff+&A9@O6buB@C)n<{nT6a+d9ONH6<%qh{uGXYc308X&M(ZYn}82cAVF?r0S!xsZzshA0n zDv^v-ncG<2ws-gU&85N%#O;Iz4=1A;9vmwVunCNb2z7O|^{hkv1mKtn6w!peiD-z2 z2b{Bq>x;6Iy&TNVqFWJ{N2Lj>FvhM2$UV=pXjD{J^wP)1_@QHQRW;d|<)AwtIW8Ca zlnaENHR&M^rg!g}BwQBeWRQc&%V zcEr4Y)8CLCIXNweBqym*)Gq9(4LEz!475OEIh{p5iibd#mE)s#5N!uZDFA_4>Uhdg>?URs5jPHZgi1osMPoNk$o!{qYB50S3T|R#1skj zt|R@&|N92)-J-QOeY;#nQc_N@4)qp+04xU~C|wWnP@cM$t=fh~^3pRTrMAZA6ciQZ z=kiRzv0(ay?Mh!FHbHEEY4>(6oWnB#voIfge2tByOVq>`1gRCz1Pq%8ZpwBFLekWM z{^Rh&GXXD0g3XNSGo_?tG=BhgA@ws9r}+g z99%uT0|J8S`7;Bwx3?qq6QCCxBgyFB@}#b?GT*0f)AaaynwbP~c7*LQ#tW z#{|eOrQt|Ivw=<{y@HX9r~T{(oSZ}?gUVdI)zM!`x3zYiuDia0rU z4YnX|bpV_lOrYHvhm(a&j?3_W%wo>V$1?$M+k5)dj%)XIulv4;j*W|l{%1yHbtb#J zKh-~X^ntG#&jkGQp5r=d$29I5S=hOHWAe0=XZuD*Jk!(C(@|H~)HtntTJ4OY!o`QC zwn$RO=GWVu9~d5KdF$$xD_5`Iymd?e_T8HouNzz1JG*(3yt}8hxFpH?nVbEyr_aqT z(5yg!;ON3E7{-Iv-P74zR+JIu z?mk#wo~)g+d*9qIzL32B-Gnc{{AvPn5Ttc>%gRqWWbfdH$dTjH$zM;O|ILiOo@*yT z{?){!YIS+9|ReA1K&Go)9pn=T=*Y-(u(YSyrW zbG|maD))ulkp&YbOq(`IVxrWHY4exu(7J16X4NCCeY#-6mp^Tm_~OgO3#Uz$pZ3+4 z6XaxOOx>;xreB^37~U>gV2EIiA&`)qQc;ZbPzq1bD)9xG_O$v)@KF!Sovdvj!QDTlNK8g@FMzJUWyE)Fx)Wfg3uCWDa#^R_3I-#nq zXyC(W&sb-v$4v)!m~lK4Fmp2E;j!~68JpxY=9z$LeSk|*Y`@ddo>?N~WB9l}U{+#v zfuyGs7D{s`js_p)R4;&M0#=FjwJ@{y4G9Yi4Gsv3O)raiY#V>ftQ!}VQroX zm~3n`29>AD#{M<6w-?=4QDOmfyuo_VPij=4zO$|2wX@Pm+TYj~$4T&t<1604bgVCX zsrVz8`tngoQaH&eyw%#675CE?{KbX3Xb|QXBOf{?z_c~yT;2VYx^uBA_8a|yV=r^V zHB{&37NX;LV;!);GC6IqwbkXtfCO^cG^`i>MaOD#&+$yaR1EQX6F_Zm3H?Cy2{WWN>TB&#yRCNs2urjJcep*s!;bFiIfPq?3@Xz1p@g3NImA6 zfQ#}$^jJfoj=H+~#zYFwX?cGXd||vTohF^&2*9F& z(&57gkDfYvUiada8~TR#A3pggM%n%1>{uULGc#L98&l&)XitounlPGPx*U9=1wamG zrl!P41bMqTIoR3S+S=JO9$(y{1lNQ3BReBCB{3l`DlE{~+uO^_)05qql8H(nM_s?{ z%rvS$oWL^y*8oIaUV&nswEwpxADusUw)E6V5|dHqX7VbFdhk6}fC`0#pz=*TymsaM zxsuZ+O`Ht2U6j$e`kLx+Rg^JtV}bgPZ5!v$m4R#$n0zNrnk1=LRaF6|U!Dp0b#;Z- zrgf{A&Yv}H8e$7RAwEIF zOH7-wKtcW7xWOmX(*4XQ`}M zHGi(0}Dc)BSGAH}WWbWnaDlL&WpTxc|sq3dj!R zcyoA|7+1O9c_v_<33#iaPk3TRZhm1AH!sFYpKET|ynXf3rArnqUb$P_#5FJ~F)b@E zFOQQC4fRHxKlJ_P_3O6m*L+~*@5Um)z7;wh4 z_frBhYpe(sv0qbE5S#^^oMSN*6Lm5yJbW%drIY%S#{*R~K9ZdhYbl8sW|nzHn4I3G z#&u*D(-7PM-1T5f6n_Tvp; zsd^sI1Ux+Qw$4#)=hpAO|5-0%XoM>#NDl2bvTeZCI~c3A=jY9fmoAtsvrso)FhG7| zRw$4YH(My|Kd^4Yl6iB#k(He(ccOfV3bqj$G+oUz0gn{kQ&_(0yM=S+%#oipf8F5= zEQ!DeNd)wKhX(rxdxF(=u3EWl>6#r1mx$zpQuvXC5DqzPPVngVcN9kWcm&19Mg;nJ z`%waKG)j3SVtpGLLIFtZjiNq~%rsXO4mi2N!9noI^Gv{EfCz#xEaE{1 z05ai-gn~7SQBVX2jTQ;k=+94bk`Z{H`^mbH0Wr=b%A^>C8g9aFhdYd4&xREbOg#_~ zqE7%s0lOoMk>CwnC&KB1^}ykRB1+={6rKq<$;bBj!^>w*9zUsgLP^J|9Ic|F91m3_ z8WxQXl}EYpOu(vYO7PH~P&cqdg;6k>#l&Hr(c~89Ihh#hX{)O%9X+W8u@fqchK5H* z$Fg~d4uT>NlSdElU)NC8y86t?10-XNM=6$}g9!Z6-HyEcvfQ{AVd0S=KqGh;XcQ7L zWeHxzs%JDcAaxs61c3C#3Y10|-c%?V?$1c8c_v_PU19o9pbWxO{6+tHCgA5+)s>V^ zDXClxtY%aTWi)q3|Ni06e+eoQgT0-eTvAm~R6412&L%Gp$K(8b(*L1f-+lO{qafDD z+4kOr(f>sYn&jaa22h4t{#5WgI$k` z2=xWAUgnQ(UQ$1P?KY8@X(G#i{o>-uR zr(Ym#1bw28`m)R@cVmNVJQFZt-(|%GdD+=nhz6vl5v%}>pt-0jK?MTA!2=2jDkF$^ zLIx)#kP>48XK4Z)O9#&cOlU1;T8P?mWBshJEBw4|)#ABxmafxp7Gm9DNsP2Q3%g2k zVtmam?BBg*)x24fvU65HK^gPUV>DNb|q7xTs?|=*sc!>(?%jlSG!k?7V$(4X+s; z6X_446$8HoT-d*5?W%92XH1inn>Bl#MJ2_4N=u;sebwzXU2l0N;AK1$a1($-0Or`!~pce>XBBY|8dBe{lDj&Uv#M!hls)K#mGR zfBgL){{j)%aChY^f18K;*R?eCbBGA6v=~Jy2S>*K_~*a=8&m{??S=8awhwMzRad@FB;}-K*7@aSQ6d+8bKPCh8I^r)FCJYw zvVHx^Z)Bw9q-0mwB_&XvC`r4z662~`c_v`B-9H{Vrf^aB+@;(1jZ7_VK@{u?KrtH4 z>DKz3O7hVp)-hgA+-P7CG&z}In)C1c=Lt`CizOxcz!oxyY3qupjF#yZKg>jyr zm70_gAO8|;;ju9>Bu4|&9cpec%_}P{%mGdZEKP}t2_RbJq*M`(RptY*q#V#z*zUmc zBqy^^M;{MzR^5*(fRlj_xDRs1(pbs>Mjw~s76eTxXUYvKNOp6Q;_~t8;B*T#o(UL_ zl5;(Zdi)J;>YV49fagh1pFVZEr1ZQsw=Jz~?3`Qys7IPs_#;Ctt&=O}FOUb%_mt_9 zvI{ofGB!oByA2Te9qnCtw=bw2-nL}!Y^iBeCQqI&DZgOX^*fKBn_F44`Q6cGdr|$! z_EmFdNlrtD=`wSd9o4&m4i=C@`P#b+4HSReyc8Ioncx|h`ey0IGrHFd9zHd-g!Xl` zwsmyJY3|v!ddXaQIRrQttl4}*=khJXN5;=gF~6ZA_>|!)JAT-(diC1x)@|9l_qg`u z8@CM~85utV+@AGlYtMSF`O|^pCzLMoOu#%7FcC6Pq1EzI1U?d0C<*hC4hMB$ zBc)>QBu9r1q%y*j+tY#K+!!@|x+o(VX>$HUFd-Nnhp3q*r2fK!2weCYjvpuMgrHzOfB1aeO| zOG{hOd<6xE!D=NZ%E&->b45;eN?dqwppT#T^C#xEPN>uy7yx+}Y*b-iXI)7SD)>i) zhXwmuJ-4)XLYXmN{{VcMbb}tDptZ6vGbKJgI^56E&e6%mjW&2DZ|f%G6s5vj8>>rl zQewh_{QZ1=vH1rC1O(FTK#nw8H)cjdZAD>rT2ew>OjHEV1WZ;0nHFeBdK$M-cqZWd zW}<>1M{HRsJsP$JH8+%G#lP@&hL`-ROJ(C<<+P;K7Gy{JyO|prUQj>#q>yI%zv?eq0WAo$8{jr08HT zM{9FaQ=SR98WR}$O|b(+7uXBVAv_asQgK%&`3`v|U@KckSC59)@{96Y^v$nrT_Gz! zeewk4M@*Oqg#M(}4~)&NY#kaJTN6}vDkvWQe(|gsGBc)4o-|>S#8fHiSx0W%Gq$j{ zhoh;vIpVUi%6hpuvNLeF01NR9nYpWeRJ*AE(A3(Yp$=}S*4O%b*M75fmh`llGiON6 znZIbmPYN2B4IVr-w`nAKeO=MRJmf_1JcQBieE(qy4#x>KQO#~hthYRJ-4v3 zX+*V6jKG<5JQFZ;1&TwCKO#3q)X2T&WK^ala1CnBCM@`#|o0Qu-yAMy_~RIZ?xVD*emq#Y#+ zzy%05URfD=*jWiDT+A~8BST?4WIY>$IBxtD2 zPf19K3h{Mwv3T~#@Ww5Zhzit+Af`nUw^o;BCC9&viV5{_v3~YQU-!JWj)7Br30w#j zz`t}h*OjKH#79R*hj}`hnLabTdO=I;+`04mt_7vVeSN?hG?ZkdB}c}-4D)n0Gc&%U zr+ZFQLtXRiS)+7tj_^#t?G-r*L7uKQHs;SB8s4~~cj^2&ZSC_^1<=NUzF$#GL7bnv zgSDll$>T?NZeG8xcjekmgZsv2HV)3*^ysP0jtz8ocCfKBe`fUf$x|~63mdBP>EloH z2go#(0BEi$FUrk)l^7cZCqpQTHoYK>6s8-%r~+(x#M0SN|C&0bz5+#1QZfQCuSgCD z1?0f!5c_5wh=Rc3!0PY;g$%7lV_PO-XXNFF?}=vuCR`>|u&V!8M6JQHwVg==`A(Y0$APAe!X?BBm@)B11MELwz2x+P1OuUPdozAwu+EBxtg{qskT zoIG@J|L(1u)~{Q&Y{}w<3(;lSx%i<~%HgKIiF_8mKT^60)@pz>Y4V#xw9{VrL& z{IZ9rHZ(cI>(2FyhmR^MDIEWK@0Lw#c_v_gat{WD>I1b3u| zNj?4_m_WO73Thbuh`t5Z*wzoYHh>6l%T?WTD-{ET#BzL{a2L}T*4ftGYPicjiR82e zj9*WfZ?o_PfHfJft_7=wuOAJA{`)svvWKtW5r z05%CSEX0jx0!~OvOA7Mx_qVrn^$isE4UdezeLvJ)SJYLVUsREsmJs_Y-o-J<-`>Uz zApjtRVO@`m4B=aA&abK{N>7UPbn^@MvUl?I0SFmi2G0bHQf*Y;kQ56fAp{l%8UaoX z;K*slvMrU_AnZ*4N&6TRC@BQ#jkp6h$hi7)5FoC{cn4_KxO#umUp547=^xdX89Sfp zH>aEbU+RB%S{}y)I<8x62_d!=jtR6#$GY&}^dBFB=*8$!b<`@(e-%;wdYD;7#KqaWXIyGh8<0 zO#nyHk@E9~vnh8{V94qYO2YrQhDzzI2ghH)grDm_InT&RK*(!o@PFt(^Cgh~jl2GT z(0>d`4pV@?(LTwU{tu9IjXKZ(o#-(xhm#GhD}GSif#l#S{KN!~dhw!u(GBgJJuCu< z8}LlPQ7D1|4~3{#^xDiq`vlJfjCqAHTzxf?aPo3<^H4^C0_<##iC?LF9;*1G$Ugv{ zXypI@w8hc>hqy8Mr@{1&H16uZ>IeS90Kk;PfkxSQe_`_Fd}K5}K%@VIoDBsC0LkIR zxr^ z(e;aG9(p<3#Gw8yI1YLRt@#PAmo6JRgt}TkxOVa0%}Z*^-qDVxMmYrqg(aoJ?%Iqf zZ|BF)(t;fxswkfLS>f2uEjN95CSV(DJKx}lsGi=AvPciJh1-oH=rvbORSD@U)uaANvxFXNejVHkkE zm&}LphRu^8GRzz&0Ze^nL@)p#{Yi432^jj{Tb2~$Z*FPq=v16xbK#-V_ojw7ld5W} zs;Z#h(EsLS^ZV-Oz5VU1-Cb|lSzgsT_Qc=*c}RXf$coB(1)W9lcIszuIYoI`JwC7e z^zPNO$AgmX9^HMJl7d3Gf}W{R9#T57d%bR;&7J#EFXQ7AQw6=% zDPdlP{x-=j=C{tA(beFYfVcjjeL+)68xUhV7cZ!HcBHqxbEw;GWzBQy2M-@S$TI;K zOrPV>4PHi+MUCQ=xb{n#TdWYlm`p1RpRFlc}A#sh5sw1AN_llK3v+VZCU;d+bh zvJq`&M$Ll*bWXwgMp9v)pswzzePdf{*rS!Rwk^9g+=l{%bahKJedylWrt*+XLuJRP zi{CDikXrQp$;)-^z-iVsG&Ta6*=GLpE<2+;YbMXKy7TQUDam>JznyMhQH=x&Z1BB; zzLzH#ORqhyxn#!D?LVwnUMDkW`EJEcTc2lU=3oUtdAZB0(em^yXV;yIF&B_wA{ zPF#Hs7(p{z%(Z;mGgD9L&G};Dk~vGZ?f&7rZ#QoEX4>Rky0;!Zw{(F0C}_DRyYIBz zmvVa#991}_qN1vbTxzz78fP-8GYAt8aCW)Zapz;K)iJU1&dJq?uxS!3sA zxan4FH zFb@ja)6@6oFO9Fui>tvy*4@)l*WJ~al9V136_@-f9esEvU~XB*I*Mf;YkA)=wO>A< zSZkJ-iv#cqv?0(r3SeCP>mMZf=pTO!4b{cF+GBe6vPnuEXb0*eNYC)_+h5rQlm zg3`5E#@P_GU7&0d>+t(;!>P{J3>Crlq)#1$)M50OU*42Dm=SD3ymQgkghpFQKgt6S zycupWy+#C|9EP3=NeUDfj*@+X-u9Zyr^w#z#%E&}(_X|j8@6Y4yf#;`VRAZzvJ2V8 z=u1Dl+Ug7Pi^!g4ImGl98vW;p33J;TYsyQo&w^{2lR-!hJBEUx-KV~`vVzGe1s#n^ zu&q*q7+X+vCHs?H<;jPpQY<-Al0_J8H1AWs}L&J{NP*WGLfBF>t0j;3v$1?$Qu6L*xmvB!7 ze>nYSD*`$8Q4;~BzBn<#d@9*n(0H~oKuFiZ_0I|lp>A;{wk(r08($NYXLwr zA%)25<_vWD8fb+;2e>d{Za~cB92YBX18}M_tNXL{fyM`_Rn*&FS6*7t*d>6qGrm6H z@*1qHD8SH_9cF8ENl8uPx^qTLH~B}|@H`W+!}5J5&&=$DUgZ}T7v^TACBF=`eDqjD zNpr{g1#@O=TUsB`yl3kcS@^o1!aen<|5%(IVs`Gh#@Vf_W=XENYij2nneeK(0kuk~ zmI>9Os;x>fzk2%6&l_h?mpb*RsG_E}t{Kr3`tGq)pi6ySy3O@7db^g$OkH!Gs{Amr zU)s6IjD+*KvEkLTD~g&t6EM#N3?^NwN(L|?b9dFC?k1~4OyQAo6dEqbN39(qgR82k zWx3P{dSF>%mCTUjSW*b4Uv_0}T|Kg&$Y)-Ubp;Do1*JHa@JzsXFpVv5|M}rv=O#pj2ZO5D!#y+?Cp|o`mbPF2@%taYy&E0uZLTlNj*AQn^!M>}bM<}oDkZ7D zzNxk2_dkFC{ns}`{asD9#p!WTp@Dwh?(S}0aq)4nbyypCCSaTqL_$i&%t@jHxW9jZ z=QHEy=9X5rJQFZ!EO7wS3l&SiKj=7gnraD_!A7Wq)^LwSge`P2K?G*wO<*t30`qE~%AtRIw9WIUmD z^%b!J9+sweZ)mCT9uavy#C~O-Ws#=(NRC5v=kGEDt2bkB|CKmcV$2N~Zc)1EBv+s4>*o*xUvS zv>s&;SQV`blC##>RuytfMd5_{gV1uoAISNEDt#a2&BZ4B=1WeQEFm#z=0^Q=(B$z< zz>zOwV_<#NR@^dh_Ivol64}WUh>G`XG!*NZvcfqeCX#1gSSUS>#9y*5 zlO|1_|G>?~wW8{EO`*!(bsIJ>l$|+=G5vCKiD~n$JU2B*kb`FeE-5L3uP^cGv5m_X zELyx`+4JWnCeMrErY;2&F|N-eBJlK71OVc~gM8dwU7Vbp9Eo6@ zIux>!B6+!4ta@-_d=!;3^zrs8FXKiiF2+a9M*smG($dpXQBN@}BsehOD>Nj=9z%utwY&>#s;xk4S|l3n0zUR92j>N z-G;xurDb4T^~7=0sKAwh)2eti>ymO z8srpH`6wQJ+f!XrRwo)AMHeDJCLlw1r+Dcbc~j@Cxp&9L?T4;q4!;5OB$i1cG9)>| zO=ClG+CLuHx_rgLxwDsEPU;&b%4GI6;p>CA#pcwZ!yCU}zHt7$S+iv16)Hz?aVMb& zY2D_%V$6!y0#-25+S1kW-m&%O? z#uknq{vi<%CqYhMmq^s#p8vwj%|8bIjUX_DM#m)}M4OQ*o)8300dS?GwZ5{Lk!a=Q zVnfX@D8TS+zEETk2w;384MfmTRz~}7X;~?W*|*K+6Vp4!5W~D!I02K4H7XRrX)e6& zOnpQfj9E6v#nobo8M>T+7!KTSa35 zJlGyQ6Y!<87cRS@x+y&j?9qfj5r{@c2YQ?GA_Lvc^>tKKPV4#u*TUqKBOnkCy?Hy< zTb&lNY$+E4J5QN&hi6@w7n$J&&BHACDl_3N}4(j?c83{LYEJW zjsEtbH9ykB&cg7#ilU-|;@SK8ZCo6eLa2l9-+cJ9F*DG~%Jinjsgnvyil?u<;Br7H zL@OHjW#rulFqH93z-LaMIQZlKqe|LO>|A_;UPMO6L1*xYMb*JB){n1f@l3!}{s&$) z#MsK=KQ9Hujl)+kzxwJ@!vno}CSZlbhmW3A(|h#X%HGA@ z+n)e_qQ2hRywoT+6T=%<&nX-{d_qa<=EG-}B=-Zr4^L3kn)Ncw#n#hm1XQ51@*qx>A7-MM~4TltiV=JoqV&#l0M1SS!JKhV4sR7JSjSeQM$tbP8v z5m*9jKs4g+<>N~enI?X3PdlPgm3i^uA;Cd`2qB^c28V=_y-r}&A!IWxEVOb6 zYSaB6T~t?B*R?K&CxzrB#{NxWQAbXs=k3!!Z&|%;!Gi6!9eqqra3xO6GXV!aIgInS z+{|eblV?gvN-sO+=Hnk26cR=hJC#XMR;GqOt(q$*HDl@|i5aNkzx0Tmi-(V|pC1m5 zqLvV=yO&OFUJRPADH0PW&5)9sv;4S;g}n>(14OIB-tc?Z)c0>(E<1hlBDpZs&dbW{@x6yyJ9cefwo)y#onX>T z4r@aoO0&(2_i`~eHq!n6+a+@suT(5+0kVO+9#NZ?;K0(PcqibP&+T3@fA*XOE38@> zl(CwbhoVxi@Yktcre=3^l(zCrz*D8?u26mS)WXi$!yAV@)MV}~ysL6#>xvbNq$Ovf z%~`tb$~_}<5Dj@!4h&pgoi8{}N&r9}F;h76=EL$QtR0|lgw92e9Kd}L zC=~npF+2yxgBOA*{>qV2oRc+?(t=on!&#iy!(jXj;fr45V)UViB)bEa5Zr?;BIV+% zxgn?ngA8*JK)TE4a>Dq*iBVevUkM?AX#`F#{uC@n3H6iE*e%3zN?)P>3VaJ#>8XY} zAjd4%fV-ZFA;C9AOMNrpjsaC>AVQ4KiP;cr1ahsjbr6q%X98~T5ca+Q=kLFajrKM- zl^18GMEQGy635OdIxhBQJgDkB-VVM0$A`DW16|EEWrZ0Dq23;DE>6~V!7sps6NzlG z!M-TK`g7YL$^7ohPL^bG#({fJ1=R#lOo_A-)Z0xr(a z&PW4UP-0?2LUS|XdCZ7G9zS?3P&^FS*`oZMtn@Uba6ry=pqL)evDm<0lj4*V=4EFB z&6f(E)MiY3T0mN$8dwCFysQ+U!JLfrG^|>{)wVD^0kXuHd!V8M5C`Pn05O=!k^*rP zV%=b3d;pfCgDy;eNyas3H(d|qehOs%q+Iu01h_kKLV!ew)h+%QNEw-Jd@;ymm=T zQ~k`@vuDoV;F*A5#>K`$Fqn|bN;|Nu!hnE+ zceR%#1^b8bOu)z=m^^jDTqB+d*xkdkfl&#Q1_C*VGj&l;T2fr(i_p-J;Nak(pr8g& z|Dllv!Xl;0p+&hFuM&}+K}Up$aO}?{rv{r9rK6y~MfuruLSTJhv$Fxn=7r%)XLpQI zQb1t;l*IU#ls3n#@>-_1R2QJEv<&}>utea50F!{$5Bh+(1*~Fx%JK^QLxL>^NuNXk zdlr#LtdlhsnCF0sRIQid_0)mAvfo(f9@PN2g2ODJ&jS3P^+4E@B9~;Au!uh$vc&^& zy%3lsY8yagAR)L7{U8Y9XT2zZ$H}P-0oQ}|;yw|TK7vhzcR1K0gF%uUgK#%t>}ynH zjC{br2Q$7MDpdAK-r7P^;H*c+Mn{GOU5%ARB{l5`TG0r&p5%RlL!uswn z%gM@2&8zJe3aAJvF@g3C4ZZvDw|B!J6X}B2uc9O`Jw7I}h-U)knSfDeFGq-}$1?$w zQi`<>ibb#o6beLKf|z=l-qVlI^&jgERpD)A>jObB{!{;fp@Zv%mKK6=kovP18i(mW zWAf#hfII64dTLAaQ-l12Jv=>ZOpG2FUe!5!M)mY*Wn~qU2$86}ucx6PGttz>$JO7* z-tyT41HB6xDoV=Ar<6{c1k&<2(2|>x?By36;Ok{;XJUBuqULD;6_u2f&suu+35o{= zO{H1!p>lDPn09*X)eu-jqq^sb+I+Sck`z18Fe)k zm^l>BxlV9Av0dJ`U<5IW&rs(?Onf zVJUwsgMgF_4Os-FLeL>93)BS$ZBDNspi3Cs3WdIy>aIbKPXNe#QZ zC{>2)Y>w+c4a^jl?XlgR)VH^f66Vn5V=t86Wa$h%6L4R5kc-WOtJ+#>3d$KWy8!ee2fUN7XLgxckJ!k_!MeCOa5tojQ5^=+PsG4jwwG zc1i!?%pwWc6FIW{~b&<`~_c_!eG@c@SV#UsKqqP2$VgpsRKEdT$P z36y67_6qF(@Bj0IAU8I?pyG91V@rD%Da63=oA*PtsV|&eo&Ea$@xTA*>1fJ)nUG&n z+t>nsec#~d=xA?kevE^Sy}kFq*#G)xA2Jo{%JZ{I8cG}6yG5e|!j|&FoM0OVTU*b; zk-z^Z3 z5n+M8-gcI5{=%V=(f&6*!@ce0HLW!zINiqcOu)_LiwB|@9(F21I^KFH>y-KW;dY=s z9_~IIw9$c6KC%JYTg5>z)}gWjZ{aY`%mdp0VIfe8BF_X&kAi0cHnJ1E`L#PfD)`Zj zyUxL>xg`}fsKV4#Ta)T(c1_`==6y?n@J(N8U_y$ogV_`NAp78#DLGXI=`U=|Omt7E zXx}wQ!CX;}wOd+7LU@3OS73ZZY;=&fndP%PSJc%tF5kE47529@DHq^p7%+1u=&f)qMgFE`?)O0TCUN^FG00Ljs z*V9}W?(#Ik@v*7n^ZR-_w{9vcUA&^FZ)|Dj$U*c4EhR}WLSfZ6lRr@%7- z(?j8zfJb>IV6Ga(Ac|^WlOs-PA6X+Hr3C9P!!3+ zmP+1j$Z0bspBvLU^rA{3B*z_S(}sV$wN)(p_=7G-xot3EaveSj*e|5H^Z}?40QuKj zTgK&hm)ptdwFecSse};H3fa+tw#844vJUCgz`}S;&c*-O?n&K9L>=fKlXG{a8;A$u z(- zC@RX&<(YtaCg4wM%QO>egL6JpZJr63X97-2ru9Q4=&LyS)c?*#S!D6foGB@*?C9nb z5b`2|X9CXP4xsGt2?gSrfZ+?E4VZ256fkVz9u4^r*;Xz#GqCNGkpeS<$vGJnd)Kb8-@qR4C{o6KkL+ z?@UG?&jg&8moMn8ObqeP^RkY1e0D|Y+(or5yOkC9-+X-SZft5sPHv7s*q$Dm-Wu!R zV0=wo(=OQZ@{dZ2`wpDGa5Lm(a$06Ko@xEdkkrf|`^RTasJc5E-`lx;-|in&)ouku z$0wy`;JG)&d*!EjSw7l-@aCiIh8pX?-?C}7%7e!@o)?;y?hOrfGCpGx7oUi4t}MXH@TQsFeZ$fa7bBIE`}XhMb3Q)G#X-*?D)uG5 z-={gs$~VZz%A?W$XLwBnnYi->Hvqdvfx~zJvRAU(ATKH`Ts~Dm+;EyGz1NT#Mt}{3>6# znw~m#=+GWjZAWk=Uwq{1?t@*SEhpNkxG>bwBEjF@SXFW7ww>p+bRIu{^w`?L4NSk? zwMl^vQSkxJ7mVGXYa9cM1J4AUnURs6fq&`gj6#spe=1E1&p)`LcqU+;30UTVy|Yhn z7>udVP0MZ=?~|J_Np7v?ni&!x3P2^GDc{{k87-dxfv_ufr>wK_`l(+^ELu5tg2cqH zzWQp?^lz4~Uc2Aa!QBV;#gnyDcJG_}#TSy-znk#omtRegkeDK^vs+ev(jj{XH@LGL zmrnkA`uuNZ?Dbqb3G%Ndem!N8;qe(WrmV5Faey6}dHvh3_8M-TzRP-z1mu&ZOqe0P za@}+Zd1X^eL=4$k@7?5f-saz_?Sm@sYHB#DVqGp5a7wnOW#k(pJGu=eSK319xS zS>lT?7cZPPResu6UrvydnK5;{Hkf`*t$T!3$EJVv#p2yO6EM#N%rgPw{02yz=+`kp zOLudenW=|gHGn~_9UK`h9p8Xi?-vbx80{JBEcLkQ;0`xz7eJxRkszxszfS3;-am7XY4Q zhy@*OwH0ZB;Q_(P<*l8Nb40^Pi^53*;`aL1y6o7{a2K1ackE-T=^Vo)`oG5Iy+i%2 zB^4E!G2!mM-gmUsuUUrYmz5wBiYN#%`oM2*1l1*(aS7?s5pL$sZ64fxU>ur_LhuDe zC1tq$?Qf0Iu7Q!Uk5$?xeE^*4t4dAkKf#wWi@iStW* z8FWYg(T(fRgCjr&p4QV}X%d+p;ACoSQ(4tlSjG-ulNLpMZfIJ+Palz z0;cZ}1p&tcO+DT1!p^)ruj{I3$nC}ssaRyj#q<`>1l&l31m*DN|D`b$4-o($x=0v6 zIQvEjit#MBI*zV1%7SCIx7ZRJ8l9rj%Bk0ZcP(8YT_ra4Y>YAEoj+{|B z^4-eSiN-b{ z?D}ENH?j)WwmcKCNHp$4M&z6lGYQSs+T7x+f9s^|Oc{!Aa86?JE2WN7hu5a+%FCxq zQuv)q=^XEc=r>w(W6{;0zn?sX+|{3VU|wayk2TaM?^-okp1jUvHL)yMa*dO|AuYVM zslL{iX9DJ#fH|VVv48yj+pnPF?Ln$Y3Lv$9K3?9*RVe3_4+_+}`u0Em{QbB0Z$<}t znyRv5k%@Jzrc{83#^G`~dS zD=yMiO2q?UhogJ|y?}q0{vpATIv_=XX96ao(uR`Eud3CCf z`Q0n0RZtIm^TrJ{z*e=FNeKxguT9S=DoM0^tgn4a;Q)r;uwetbY&#wh9Tim#c}-Ge zRE(zs(Dk@{`zDfZzy~;J4NC0l*R}OEF+r|Qwoh*9X(;~8GXe8Vz~J$%WF_{>cqU-B z|2I2o>MAL&T_itq@}vo0<9`z-O2|I1L$$S1rr)J^3=Pg5-?Mbqj7bx}o;Z;@NKE}M ztqKgUkk{778rZnqJoLkAsVS3i{Z|vd{(8csX);gC^K!Fkxvlql;N^c$W&0B8$rC3| z{EEB5K2{}SocafRkJHA&r(^j zYW`d~$*Gg4Or1Jq+O(O{%MYAT*0`XDEey4uO3Ko7zgzN+ywnV|=^!cq)4&l$V*5r( zoifOQxU@R3Y~E}+SsA%mbLK7n?#E+Fs#>~w*T5E7g2poe6X6@91V*_X&=kpsc-p|L*-;HWc}&2XLxmpHD1(_lu~$vATKi-8*!_2!m9VU*PZ3EXzYOU=2feNQr)+A*Xk7upnnn+PxYu9&9Hu~nxo3SBAxk{r` zy`!E^T*OJo-t`g_c;DEV_$DOpZWoJrCSaZkn3R*Hz=JHDt1(|%QcO(Z^bgaDn{ZqT z4mka|I`kxB4JI+{T}-J?R8}WIE=EC7&T%o%1Wd;P3bsIdNptZ#ey}8Ds>8&D2qJ^_ zb^tZSAR<%>N{JG@0DlA6($P?y79Hs69aqno06C^!F%uw4P4sqFrbT*MJh-ZB7}YHT zG!20vLT>P*+V^>G(kma9mB_!QB^3zcKNYFe2=0&j|Bzcyw8lX9C8$^BPMJyz}I@ zD9C51eTL{~Wxi3Bm}deG>qJ-p@8)_Gm$HUXl&D5=!Wn7##l z?|*$eEU3?Y73FDk2~6NR-h^9$98BM|`n>t@;cuf|m1%KdF86g*l~gsaqzfo51F>SN zso3}1-+upXprs@^D$wrkIaMWP4K14{M41VwLGyp;w_pGKXMbZ}T&Ta5fhNjtDybP{ z5*-RQ2#9$mU{ObXS!R^GvB9;=nmiM5MHxW++1Xjy8L6ph<5@WXK43xt8^@LZEJYd+ zh(Izk{!RZGZipQXK^IV5RFFr@=Qze<93;VhLIEfh5cB(g6cb#F#{OJPYw>5Dx$DOV zB_Y?4?kBzjjW2E_B&&XW|EISh{=@{1NN z<7cMk){ag{F=fp#x?(RMI&fhBhD9qjAG~ny;iG3SURu0%aB@b1D`}DraaXHQR9T)8 z?Cp;11b26L4-a<_FK-_@GqA)ZbmwbsLg(GGytITkpa8@~g@uKMN3g{>4`(32%xmIl=%4g1Y$Cl^@Ox0LIOuLzNV630CKVs07^>-nI_;!LHy4n0psRnm9&!X z<&l8NfY0sgg9qfzU;p{+2^6$ZvoD*WZ5o zQPNgh66^Y0@1m~uxhr1L(J^tcagqU||Mc6hKYbW(tr2DhTN__FhjG(0BqAy*I$AP7 z;hhh^{Pg2cPlG5w-qZB{1x>AU+6Hdk0b!9*(Gn>fIB(zm`02w?cfBAh%*FKnd5tq# z=bt*e!^Dpu`q0qZckka1OIpkGQvz*`Z(lfbR{Oezy_=VRP$>G8!vTyS&f5WTLup2+ zi`l)aI_EB2ef$~)fc}6QgU)~p_}$yLgSCZOA!tKxWN`PM{$q3ibocfT3=D=+;gNtT zE0U~H9trp~j|4n@>cmMCr!RjQ91JY}+?qT*zwRi>U^ zKEU#$4zgXnv8K=O>R;KgYV(?ziWA3=SC~3yioCcw z1vuUiCeQ{1G$8No0Vf{K;3mNP2|on$qvAFB!GQVy(4b5#ZpAyTuR{$)Rpn>$fV4#@ z%*`lo@9t`26%LK{03?*t`=rGd8aubFUVG@OYgIS3Xv2EsDaq*llF;iaC-!XGuy&F1 z!rd=RTS0I5Oy`k+<9HDiV?qM#fR;nhpr-@PhXMN8 z(g0LvxLJkeptiP&J9~TkN8S$)cD2@s3W{pmTafr$i(4oxZ$ndMakoS=^y$|hNBTOO zYKk%=!!qg{>l=Uy4LVLL?Qo-v{Pvfh-;eaRH;Qty!hC$9t04Wbyi9;zjvd{Sp}+j) z*N^W8yPF$Dxv3G}o*scEX!ZgJZy{uIXHVa+fBW^Pk8k=rn(NAujqL5=;pCQ54Dp|n z4T;^|JM@pg{`~R%U`Km>WnpGgh>yFAvx8q&Mg~0efH38efJr&^^+B&xm*uC$0_oe& z*B5Xf0RaJlgd>d_X^d{DzQ($WlDy0mKz&C8#V;Z}Jfekgrtx&(B#)>b)C!L04971i zAt4?}p7gljGw6Vm0Gbo1e|1$t9tjvV1TqN&%Y?BQzHq(83B(AHh;k7`WMV)vV_5AY zK_rkHguxXP60scW8;r;%Fc>5z#9-$Lu|Hc)JHRrb@<>8OWOfi3upH}UbbLz8nra3n zvYaj~mtCGd!j9I4%F>eZ8Zi(r8t4rWf3tT_b`g&R9O!ImZls4Wo(5pNcqHKPuu%B; zI2d1`al@V_kq(^CLb&ddm=`cQItq^>L3z@E(G33|Qi=tI0P#&tPU4Y(MWT)ls%s|R z1$&c(;941>90@Fr+X?;2k?9mA7abZAk*<_(0WEA84Y;C zE-}Fi;QT-t9N=#r3Ao;3!WJ`6XN_f(rhGSM>==|V@JPU}F3!#_F0MQhuvjcR*^fE_ z$PWOg5gb1}5->$%2Zn}!`p0iSQSM={q_e5Msl^PS~CLDA z^*{doD`1eDTF9{@tSHWiiwtnb-cAno&cRV{-j4jwfBy1e1cwjUQnkXu?9`Y5FLxJv z2ObHSM*>EhU?v&Jk0|pJG6BIO0pD{0h(jQ8S^Ad3) z_4KrRWpdx>iVm=N&z{xP)O1ag@<_na{?_7nKX(UfD=V|7PYmzgzJ24S-rak~&&_Qd zoLSz9w5KjFF3{cC!REDvsmar4Xb)g%WAE(d>Elm{DacFh?d@!-6$uJ*(v#z&BO}5> zLlD15fHM|PHj+`1F$VWI8ICnHDI+~KH8mwQH7yNol4!otD=@{n^nm2)A`44i-|$q(&*O9hOpu$*)*j|9vr zgSa##3A)1|^n?l%#DJxPBWS=O5|lOe4kAGqHx&4CptrZ9Xh13*8g9$5dvHxd^~e!T z!@$bE0X74mXzk(w1dxY2!VJ_;o;b938|v8BY}jWI*FGSj#EjO~W+IC$E}c4j1PoF%la#H2+p4}VQtX#5i!Tcq=^!@V51t=XX ze`$E<5}>nHRMn2{+PZGtnq`X?%v-Q%$;v&(d8NfX67WEtg^`|)hRUIXdwKrp4|CWSvWXqS&_^rnf_w~Ctb#qLCKWrthYUAA^F6T~+u|;|02@FABdtL(bP8}Id`%G` z;P^s>ahb0uWC44D&LaW;f5QLWnS~q@C}{~cm@S2S%prmDNWeT2a3@S>s?$I{Xj@HD zdZdrHuaCE2U~2XRhM_2@qxnQK}T82I5B51-Gp@=`HJdLiZ2rnGMy}DcFAU zp;>Pqpd;mE+Jda0F0zp%?n@rOeA(At*-yHM!5ndyAR7u0h*cVk?JP{&jCb6PZW`#t z3d%yDG%FB0x*9?+Zd+mW$~6DZgR=*YnB}1_Q*A?Y8x@#}fv{9$eR1>J#Rng~Xp9as zKCyo3j{E*;`2taOeRF$9XIpu&+12e!<|?hb{j67zZgF()ZXOAkM*^m&kF!*mJrAh` zKYk}Yuk5iVAqZ=nW?&+IA(sPn>Bbpj)Jmu00GojyP$Y-(Im3_kr-kwfSpL650+mg` zdd3FaD*DRa8!G^!EwE7+uFxT;Q?9pkpf?ZAdq84zcx^glbWo8>KKy3n^&_w(n2px= zBBMvl?c6^wIMh4TRp+g9-hU>*rr)(wJ30w%dY#tJ2O;~&WfGLSev zg#KgJ4o9c2|Be4gCxH6D<9~Eqrh%pZkNi(FX!U>aKgvP6^Knpvnr8G)9H&0>KPv{2 zI-6M%|8sQm$};|^+QWWvUq@ZQxs&F7QaPPWHpc%%9?py^$oIK%WUqC1AGv<$#s2o_}T|&dPQxzvpP*i_z?;RW#2AseY zeA;v}Cpu2wZJ7^$m@{MhV_UDlkg&+ur1Wg2afl9oFCD*IOQQm)x+xwYhS_-qMa+4{ z9$&y~(+p&|*Vn*=7YGEUrDf%${5bu`TxfVCDCY!CK478KfK5gR1n!}w2bUfimTE@+ zwc1*CIv^b>pTRs_)T_Ulo!NpUjzm;6tzNL+&LKy;a8 zU>^a#uzSgxsc`G%uYdkdS7H`2+yC?HO#cs(cBToqg18eiq(()%Ih#2+2@=fp9;)xMc=9N#0bX9D z+|p{~}C^sYX< zdrkAScZ}mJllMkkM8LeXN7xO*+h6hID74mp1S(+!~1uu zUcYeq=4(fD{_>^TN4{SG3QZePHYE9~MRQzOG!qz<}3J z9zA;e#KhFh%Nl zN3*CbKP5gk7S#h$5#bR?9*9p!NK8s0uA|5Sg%(iUpP!$bm7bcMoSc$E{>6+;a{AC= z8a)CidjN5N)b_!*n46oMhmlWoPUrGSz>xJ732D93LASexk2yP}jom&N>0R}etFB75Dz-O? zjJLXTOm*At=YAnA?MU~a>}@RPk$`z5;Ib5JQ#X54v<9)ncx`QM=jh_*;pH2^+4Vi0 zEkZ$de0WGmXpo;b`h$2-g>_I!cqH*J9U9tO8Y+r&Gcz*MlHwpB!XqN1V`FhlVJB=% z5F`Jxwi3*rpOcl5mIC>J1S3cY<`rhoIs^oj2#W+o`FT7N@FU1fGGNHj2M+GIw#zW?rPkbRw! zoEQ@xv@|2mx8di1@#e>I)0=veHse9fSr!iHS)`Nn8srde%qzA02|SG9gLW zVCQAn^GM9$k$`z5U<%*xNWi@kRAvkhz8`6QrAHC^(JURhJ)|)DBnT%-dd0QZPqCvh zb21W<^Cyx|7JX$qYAjT27#&uk%#%oqF^x9ik$?$93{e6)_HMKE!Dm=1u(yVV2#`{MF-P&`&$hNc+?OIyIER%&?#0aZ)v@~@I;>)9~)z;~} zAWtp=J@^bcFEoAcf(N`HvMl7niBo#0oCFKeC(K+)%;DYL-QLxjmXvIKTKzm$Vj(+b zeRhNMNWgTqM6!^mloEHylgOF1)=`Y;DK^P5_yPVHR3VAW}7 zA$%baEz}C2hRChyUe8UR+|WLAUO0tp;X9*+cE+K8wX)k;$* z$-1gEi(49p_N|>hPVv+eK?TaCTZqST$^@@tN<)2?&F!-{w$GdN?eg0Q7g37~x{VZ= zK_-$IJ($u>Z>s97oT@PCNDm`2{)Y;r2pZyW&5a4B=hb)5Qcze<-PUQ~5C%G8ve*E@ z(Yk<_*R+;QR`?!a8%Af2H1vJP6~F{fu0^!Rd8n`axHJq0sJ*2K^GUHmL4>MtAjh7 z4P+m3Fr&zDEGeQ)M6ASnMfDT;%o`9oMGiH+4fMGIouDphG&Hq$wzbrW%0;z}^|S+0 zBpIC}H%kD{)zjWko|lrCUD-e>*6@}VR#mg*b>feoKD-%{wAYK!%{w?OkA>Jv@%~vL zzOL&R^tpUD)Yo2LUYHRR8sMLT9sm_(MMd1|w%>k3qf4~uX{|5MPmc=r_wh{x9Z0`C z5^!64=P$qh{PFE@Usp$?AS)>fP`j)Xu9KU$w^t*2Fm%5A48o~mJ{; zb?wUK%U7;mvu^Vb#~(g_Wl0ZST}7bN>sJr;ub)4@d(+z0Sifq`hOIkv?>~I{f<2LS znGUbbjr1>{Q`@z1&FYodf8CaC2e00~XZ)m!dt`&mP4C~hpsBWhD{zokui3b5`_9vs zZ`?I{1jt&TR#yvMA3nHxUgPBcA2x4Tzi!Kpox6@`U%Y6B4SR)U#<134Vzcbn5dxe)t`v|hd=%iNXQC{uILm0f%|gQ zwrp6tP)TXV*gyU0kADLFPk$c!o#LL0S8p4TwOLW^isI-+3+Bw6`1Kz_r{&*`pRJ;G z{<;wDw~b60NiNWjQr#6Z7*;-Vr15OT9K zGqW-?(&8h+LV^PW{s^GmVIY}tK$?Kn8d3Pl9L{J!Ia6I|(SSfD>1n3kolQt7ry3 zTzFe}Bw#e<0JJY8hJ5fyz>_CWnz%N&mwd!%f}iuGPMpjm0i(V@iBf!X=(*xxI>C8xkD-$*1JTMa zC@h3RECvx|DJnIIF)6Q;9Pi{@q9H72NS&aQLL&zy`R7ri&v~(MIAG5!=!E2p2~<{s zL7rANr;12y5S>*jAX-oC--ZwXrpU=W5-{Bfnc2W20S9!`<56E*JA&~p*YcX$fo&VN z%>RDTf{BwRPnx7SKfbZ18l~hSGOY#z&F-Dvy?67P)w5?!nW(5Zaq3isVI^-Im2+qW&5KMOeEiV71aO`5bIC@n1`BP)m2_gg+NRA0Yot@7*{*nA?cFnQ84 z_uz==xTF+_pOJTV+LyNRNWjG2lo(1FzC02z6L+Yq;Gz!{-C^e`p!5Rq2aZl( z$@pOd!D2dEKwcs!A*VAUtzst+1ccZHcqCvtWexrK{@1@Y<&c}~uJ);uDr%}4H^b3V z9-|I-w14o^n~%TNr})^ue0=@v$>S$gPpDmVf^wp>G1m9@kMzGC7DcXdIsj4wz=DFpd?2~yfJHEa85U$!u;v~Wh@DXjks?f@ z6eJ8fQm_Ldj|3c3gU1Rn=6LYJ^10n z`+>H+2oFa~!;7a*oIG*jq>gcD1a-NLBl#eG_olDAB`e6;%H)o&>d|8-PM*?vfG%19 zvW#T(fxfQRibNmV=SJ7HPY_bLy6!_)4>ZyV3ukoHxHJ^Ud09NUdrj;3k)uaXXql6rV@1}3xZ;mn6=PId~l zz~Uwl9k$Qtzzir}?9fInkGb`uhCgTalc~dYpd3g_KA@8U*;h1>KTUoXR^boOB%;^R zVL?7n)!*LS)`Mn@^cLCrFX-r$)lk7B0f*W?zH>uc<9u)~j|3bT7>wf&j|9xTk`zxu zmqI!<5(Ewgbt@#cz za(1O|H}pidwzM_mr{?CTg?l;J+S)oeyP`%9wRq5VYziqfptuMHxyiAB0bm0-Utuyj z%dSQZE{yY{+>Df@#6*Sz7#mA;41{X|^vgI;SYDE!!y^InNWcur6Y>j1Xpy!_?LPO^ z4sKpBbLzwi-zg|4Y>5+~*F$a&HLvOE4h$~qcDZ-v@H(YwlP8WJr#L}zxjTwMQ8P-- zvAR10;!694#wT|ypEYf&;`s533X>PTibAJ}m>9T0AlL%~i<@&F9on>X)?~$TOckv4h4h;`)YwwWs4E^%s zn|?`qRYg%|LR5ecK+4=*oLsyDFad`}_{fKU9F&Obae7ON2?5>H&C1Hw(cL#FI1E-R z`t0<*8SHMU$j?LOT5zC`pZCjW7Pe0AJ^_INpm)KB?UQ!am*t~_KR|kdeP6${vUhUz z@b>i&XlrK|=;@QRRhHzWB__s1_yGaU$;FKnJfpV*=$A(VhDlEVxO6;VH9E+GphE&0 zUC9S!q{!q0E72h`ka*cZ*%8;EyrHhHu7(PKWCBD+rMe^xwgVF|JQ6U=q9!C_RJjni zcwKQ`jK78XBk6PG0m03PCWd8K^OFMbhqu;yh7(x~D>j5Yje}1kCIYvFO2>t4CMNnK5~$;x}W)eluQS zON5}1dcoxoy|vZpiT?Sc%V$oXJXLY**fC?qPWpC=S$0}#N=gcekKT&swl0RpHmv+! zLE+mmW4<0UX5557?}&|xiH(Z`x!lRbG%(We=$r*p$BqZmFX-QV`}Hy>PqeZNZE53? zfSFpX#p7BgD9DHl_4oGRk${nMz%?9(+n$_%a0JpfHqiBfM*?nwwBwP0eS+SMi2uhw zfBi7h*WJ|6QePoRkB<*=_jGr(v$MB%a`Es3!oWZN{_}@HNhjKG3k9XQ@uA*yfN-$4 zcXV+>G4^ox@ee|Y!n=>aq9&msJhmFvLcW;MqeN@Pz z>O2>{xX_9#B!qS1iyOuSDqk5H8HB4w+}u!80UuvhMh1*4g8C#7F%aL33=x_O8hX); z@>1Z05lmQKejmzfhDR`IP)c;92%`f$9EAh}R9swC*ozDZz>Ezc2be{_s0TtbT#4|} zvpHZb*5VqGF)FJ;@C%x>6-tz_Kv9d22pKgV!T={?f6}KEpMxdO@)S7_xr|Qb2b>3= zdEe=zPq{MSS9pmilL$iwJjA+^U@r69(-irjDPVm{WX5PG#-}tU;(u-mbsOk}bYQ^% z9tl`FAZe=>=B6elM8}4DxLBJ$F}QN+!bKhl_|$1l{pYXyrHvh(^@5bxP;Yl);yzuQ^wh=hR>}5;396RE=r0D_H?l~w|?>9*0n3=wY9Z0wY9I@dum~eg0%s0 zMSfC{r>l*Ph3R9XJ2!7!yLA4-g-hu2_sYhBB7i&+FmWaqonx^x3hg1fPA7d<@<$bY z@T#V9h^J>07R}R5aBue(BH>GZj4MUKLC(qCb{Spq-%jL&zdB17p3D#0Ieaj z6R;^u@5Ma)0QWVMTT+spm@+yGjd>v~De;k_g3=+tuh0ZGXa{VADUfFEC>((J1llh} zdIxwU;B~7PFP}Si-dyE*^A|2!`XW)9>zf<#;=aM9BS%giI=Fwwrgf`VEnP5AdCnY6 zS#UkDzbrgI-u9l}#cjKf9Xxq-_x8;MMLcizETuW~loww2=&uV+&Gs_9ef98BRW+64 z`*v+yw_@?U*>gZwp1<G3UX^#cb_?BBU#)B1JG7cQJXAH{<6mY==*SRyU;$;ZCk%)^|B?4 z7A#n>aPhMJ7wo4 zM*?=?k$|DscqCx7YU9KMaRrqqA`=MhZ4l$CB;mqTs@z1t2?AZ%D9eYEY&fLvFDFon z4|N)oCE2j$(C!dKvK^2_#BG4}pOL)T7NpW~cKC7wmch#@b|%N@@bJYu_!8-x$+|B# z01n6hI}$jL1Z?Ff`S77D^VMx*-;{zKbEnN(ef5!~KEd_kp(7{v@7S_w-|1u87q48udr5uo+Epv&&QRWZ z^+8Wxj>FA;drzyY9zVMO;PEr6r&JE@S-)ZRqPa?Q7azI)u)DWA*!Z%xj`qdFM^EhB zxAWlMZEJU|S%m|}yv5tJ44xuCzccQ^i3`UMZ{2lb&&JJLepotx!MwTCrz@}6s&VJh zbE+ck7Q8&SZ`-;9o0hNIuyV=lnaXo#%wD>Fzs}u9&t5>IK=Cw38>#N!w`JM-)yozw zS~P#b^38kHujoHAd1Vc1C-VHeTQZ$*pWMB9_2Pv~mapG^T=Sa2BeT~I?mpC}nEt_| z)mR+wY4PN-$x90>8#{Op0p$}JO;xJUUDU4ynIJ6pX4ryrUJ?Lg=R_YD_6Q6o* zvO-zrnIp#=-UX+5S)LA^0iZhR?UCBVAl=TcfEXJWWFzjTlRWwOs6zp#x*G|?hTM#m z-_V<*DctFrxdCWPa_iwO@kqe9FFX?P2#*BJBLTBc(2xp{3nYsuwG{uDa2u5$+%bSV zZg8_kQ}{UnLq$St3#_F$LZ|HIFTQaTSd;;AgwF&fn-~%}C!2UAV6@>XBk|PNKOnKO zetl!>oJr%p9XEc0^6S|6l=RGu%<@3U|dQhEzg(Do})N!+;`(9>@v0U^rs^F zxOk3^J-WL)q7ToRr8s`vc!fhIHtznRfXj;kom3MA&v+zY8RNhVpeh-hzorg*-!Q?oW1R?iM5lPhfi>1JdnaiCom(KM*_wXfK~C* zL8Gy0EO%?^h=F4W-D%GBC+$v$o>rOLjo==bBqlXXWrIBg`T$oRqq7e{hJb|oO;dVA zsL{uSQ97-rg9ZMPyP@@72cxs4v>j3C(;|AWJ!q|U0RUlcH`0^i^0BrWK%Fqbc$>8NWevzURF=`AH4hIwvqPg4I9@jQ-AdI&a=qG zlytDKAi}}R&(`?TmXJ`JYbUmDKe%V{uFz1Y=V#606O-}D2?Jgm-8Hu}HYyKsF;PFc zd;hMTmlC609B$l;CZyb+<_uRuyR0C`fT9prOC61)2X>y(eeLUFt8X0<6^G}sHrdxa zFWb-lb$oz})fL^HTej=!Tz+9=ZRZ;t38Y^h2^dZ*Fe{G)4C%om0dL-=acY~M@s-=Y z;jph0C4J(Y$lT6UclQ?t=Z`+}HNUb~U47FoRh?_je!&swawX|&&WtK3jrDkOcE?UT zneo%izqajT=`goqA$?Q{T-$I1F;R zHjPID=8=G*e@U-HJCpYB?kJ)(!S;5tKWUH|20A7FKqUa%OPACCWGv89rpNKHQ6?jP z9*H+#jj-GC5~+Ut$Mk$b*1l3?dR+Z&R)P}KFGI2fBdhn7N0mY>zi+;Pn{L{FpmVBot^bxF#IuC2!dR8#IzL2 zV8;VW6y#0@oS9CZJ#GTx5G+A}(K#Zsw7E{nEX*)Urx*x$8f*;D9nKAO0d76EujPD* z{lo9y40YEvR*Gs`D2rLvLC;c|`^8Ny6~ct%oU$f3KUg&)0E~$K<3D~u z5_X@swYsh@J={M!OTfTi002`}3G&GQ{P%BfkYwJ;BLUOVj7I|Ik$?$9qM5>oRpiAd z9w)ax3nR+;U&g=W>gOZ`7pDAA{s*1O4>|jDZiCPAftHhU=aGPK-Q|&hx#$ie?&7}A z!a}dxXU@{YL;8ml1U+HgfV;o3wm#87;}p?l6A;N}gJ$(~H;csrV|6upIN37Vf}_(5 zlrq~`?(DVM=FPgD^pLkNokMPXQIKFA8t9HRZlRf~(?H>%hT1AoXHZ7g1u;{@nJNKxhbRG$qq=RfAmmZ^~NpNf5hHu7_ zyPB&<;mFLZOgXBJ4XN9gelv~Eq+~UbNFn(`Eh;G)2%wyGzI!%3V+T}rNM#xn5`tyf zKmj(^RuvbPGIutcM0q+SKd4J4U7rf!D};1acDBVFW?n^+G_4y(HzpD zG9VHd(3F7-5IS_h!<&*%A}!7%0rN<}VS)ZWo^Gzb=mL_`(9qo0@!Ma2Lm}<(Kv#2J zX;yqRpnARC-QB$66XWCRffC;J*S`YF_x(^Gpm>Wi<0C@?{Jh*Tg2KbX>YLDJ=dZv0 z{OLW;P#sO8+|=ms-~b;l4|jK0uK+(^Ea#DcVG!VG+tXerDo8;KkeCQxyVtL+t*veB z90*Ca5qbAy1faaP1~5cfDNzAl?k*@|c64wcBgVyV?7=969;x~*{tf-&`?8Q;pGhJx4xkwF2KX;)q^`aJQDD_HEY)5 z|8<)WTKoI^SJ%`v)W!z6I@vzczoD(VZ`;OIt5&TBef^dL&z+oHfJ5C-7w5qv0SiU> zz7H>|9X@>U=&5s;u3Wu&$H2(=@iR2CK_)K-pgT(Q;(Tn)&21fRUOj(;@yz6f8MWHt z=%uB_MTPk}8EJ`;LEdgo4tD59YG=>&;t~qWupYo4dD$6h$w~3iVS&Eh-d+Gqr?s?2 zc^T-%=nR7nqiJcWNpZmW3kuziY((gR-`8yG$}H+y{l?wz~(1_t*Z zlyVb@HNm{OIoau{iQ#^3_SP1!UcNLld&%MWvh{_;{#hAmDM|4WK|b!TE>2EPjwDvF zMOg`JDN>^$sVQd%mbvmQN??vA#`YGIk6w5+V0^kPLt z1*KT9Y>LSiKR>Y) zRa98TBLSm`zz0PHRC_l(G%(Z?tog&zB@5;+-==a+|H0Fj){a!A5E=nGlrsbX^nav} zM^IcGngDwHQ2}oZl{o<^os>J8Q4!!^PiIS&AU``RGcz+Q8zU=wXlQ7d9&se8!I=*4 zd}~u(l@K_a0w8S`7Z=I&7O@DWu#o!ak$^dYLhLOo0z{%b#U~J@kbRejO?V_=>Z#WM z?)}HN;<98vm)8%kojIkVrgPCj%rY2JV*znF`0nk`zqS=cdDvMRT~b$7RZ%@>T-46> zRi_9Pj|5zmlMrD4_=@Ifwc`i(?mv87)4%~J0pU@xiS*3%NyXV=J`PW=>zvg%ad6N6 zqiPqP*}3?D&WZ?ei~FmCU96wp)IFoA1`pi{t$SAJFd7mOg(4`VrIN}jbTTu#aY0K< z?dVB0ka;9vvJ=&%1(&m2IN?=Hpc^l zKmPjnzx@2;+rjSo6d$XHkDl1oiCdcy46PvyD(S%6zy0m+zyAFGZGVd}#>w>IeSQ6i z4gy`Rt%7cm4i5hU`fnfKyyQhX!y-v{`U8O{``Jqq`NZR z-{!HwZC!1H{Az0ST8a)GLvP;w)-#^-#)(SYZN5%NWgdR=|8rzb8`3g4-5>3zlui!hQkjoKOPB~M*{v~1&;)L zg+~HL?i>RGFD_*IpPlT{R3CtskDJ}yE7=d3=|ZioSuf*QX%bW0?< z%s7z@NLzpeo>AW3-PJ~DVs(x6(4TU8pS0LQW9OFDYY$y@t?F)J^g0}tWb}SX=yjD7 zdp2!YyGVKA?w6&lSl{rO-rwVY@9xD*hj(sYr8Hye6s5zdt$2f@^uGS2C$5$^wN>_Q zSvmu4Y^Ew55H!`%VNAsK?{9E%2(@}};>eB_^QTQz7(a2w9J7{sdV9$6hX0~3+tRbh zSMT7SjmzdvQk;NJJkw4I0R~ehCkqf8yeGeWNGOO<&RTgali$I6{5f)-}tPtysBg zNK-{^_Sb5m+~!=74k?n|A$2acamyL$PY?$w)jjUHjS*(<=jQc6DM0;(3~IV^BGHQ@o_k$^`AyIN|6B|H*vUM7<8VxuD?0rnXl0UTELbiiMY z#|CPpvaC2SD?K$iDIp;~J}$1Mm86rn17~mwFCvk)N?2Nymz|k`Imt;$E$GZnSjb9O5xg63d)?)%wK`o7CxryQ4&d-gm z-*TyJ`aPXT0?vsKbbtBONbj1ij@H?8=gwZb^U(A)S_R^vZ6>hd^k`ql=g;onzIy56 z`AZkiXkXKRWQtS18>6?hG!{j9+L}Kz($~|wdE=_?`Rn=*pS`rQwQ~Ty37=?lQ)P;` z!>gwc9~v3ny``sb_~-vM{*mP^eGiE`-4XUHrTb| z`}tERem7ylc*Pk?bJy%u(Y}7~(F+TkCcsKVDGDC%TrgL8*7vh!&Q@Nqbn8J??JKtp zji0@=AUdj)YHBl|9oxQP&6*9Hw(mP}TJyp+J(TD?HGPG{E*@8i^P1Yy^yol$d-Lay zjP4s!`HtyJ%hxtdP}$@%uji3~xkEmM-YF`{BLTxCLocL$>G1H!Uw`>Hf)wzsw#J5v zvcjyy*kl2Z1dI|_Hy#O?M*=4IAl!R$FCoMTi4b@TaWHCYBhR1Q$A|CoS=7dboQ+%? zKynSzL^kA8kV6OO4Xp*0B^Apvi#dNJ#GLo#DD8M9X ztSm}PN{SBgb#t*aePVP+-z>5MCkb*x5}8K=F3*XJ^l(9IUyFx#?_N2prKx`U6psX) zgDhk`@fb)Ff=U&)Q*RHPG|(LcK;cD&g@pxp5b1Hn1kQy(GssIo1@5T-6Ud%rwgEL5 zVeLU+u`rll%;DC8%rfT4`OcC6VAAkNz&*fbLVA@5WO3=#;UmWn?cK9y$GX+4 zSFPM|%DxzG0>=LXLwP2bbWfi=u5wc4@Sfcp*Q{K!aKZc~yY&6?YXSEz9V~xoc<0ht zbu|@LwPU-st|K7c1@jgxTC#GFab9V$v^&Vf=FzPSx|%Ad)sOAnv3~vP(5Zsm;r*}RZ{-iU{Gw;64>PhkdztYVSg0>J{% z2h!aoc@w8@6U^waBDgCMg;^_w;NoxaQILDhr*x<_%x&;$7~!O`&w)J zsK|0u08n)U2BzIR;_2kr9_^eU68HCz$Aw;H%Smf>B%58dbaHBH?am9Z4X>=Pt!r$C z2w*KPq4U}c9bW0(<&l8tR#N}@uEw(TaBruVH+6I`-!Xn**-T@&jK_60++SJw* zrAPXB!@KY4>FpQDfXWi6%Ppy3JQif}H6aL^PmMOXk)F<)Swo+*UW2<8(Yp9rtz z=V7q+q3jbS3!mz8VFlE&;4*Cdk`ih#%376Dsz04b*#xrRkw1boU6%ikr-cSi%rM~P zM*uXrf`QLorx+r|85w_bbQXew6CTeEE#s1RDAj>GA)w6xwfzASIG#NGb+|>Or0^37 z3DT_;)PMy`S_cy_Fah_8D}!VcWOSssa9cps_V(hh#8u?<-ywlx+L!8^-Y4j!ve@8s zGx-+3cnALnB=BZAo$)8zfeZxtd-0d{WE9l0{Rtyr^csYR%JF~ES<6DA_rZB58?*(F z1pJ6c0_Kr`A+&fT;0`g#2W0UN4!tc&N{zJ-Pl+v*nfO4agv16Nw)!CQ_^T{ztnS;p z`}-D9;{^$87~9S~kOPB5L+?ZZHi5B`p{|a$p7jK4$&^7$Gjvlw=p%#9c_R&i+*B_I z3-g#Z1ihgqs6`=Neqd<$U7?lWZGXKW!N=zLW5?2}YD!QN0q%h4Sl;I&lJs@fW`#Js zdhoz3xv)r3hUQQh-1@f-E`vV~cNE5ZTRt){H1^HR&Mzz~E)fWdk)VkE-*&|Q_IEm3s0ArAy z^vF9*x|)1#P^!#7jY0%QCM8R~gosYcm>w1UqtTCE9QX~YmTf>e`)zHE^2HZg4h%cy zarp0a(!_Wo8O9y?*Wuxo4ZO-}a{7uy+x9kk2d&LyQqo{H3ipLf5!fC0hJPH9eI@0{ znKEn!eUZ_*tI`GJGh`cdc9O07<&fF8w3bSfzCikw&yrbFMCFlyD~bQ`WDFN->Dp?p znLBMFQe-#9Q;^E%V_Ib7pLRZ0i*m0wm(3^lW-`(7vx6#y|Z}et22vyB#@URIXcH zNrO&mG+~r~nEgp+Jo$fXYjJ#_bDNwopj2rB%y`(pNFHjGufr}f?ju8g6zI1}PM3ct z-Xx2j9Tc#G{Poerj7+TE(gN}*9e*3+TDCjPV{U`yQT}Hf&KR8GU&=D8smS&}2RfzW z`ZR%#9gO-V|HFW$KR`a=zwtklA`qam^>VKN{GE0rhbfZ3F+RV}1lb@7NSZ(!bYg~_ zj^hbNSK^Rt0?~U$*E6c5f1v-)g}Xg00*DKUDF{jHiP|S=l~kq~EnafBSK5oug6KFX zaQYwQ-oaQiqvscQty*N%+0)V8jr}R}kJJD30QLp!J9vEl1cgb{jCycZ7b5`Jgb$hR zjy@B^K}UXAv~14o$&(b7lRN1ltF5lVApPGDkuTY=>9J>l^1P`NCr-IiRZ&?9A28`~ zw!T*~7&>*U)rl2zXG~U{IB|VwS`Jb~GcvOC@;Q1Rj|5C-OCAZBaUPs}#NSOw#;a%i zi7#+D5nqB%zG83}o$+}jU>*q=0;C}!BqJxt{^{8hXWSj1Km1|K?j2jtXzB;VB&KAL z0&Pz8;*o#@UO##C=>;RKp;Svkt}vHDj*+Z-`Ov)6QCW*i$<0OO~6rr84)r=q6^xUl%ULPk=r4N zHISVIW2~5M(Ed9*d#~)3-iE8~%ubLkBnoG#!I;3$LrF&c9GzQ3b{gsG-cBfZ$xwIp z;nY4x$1?m+YLI#PcqCvR33$`i3zv1&EeH(x%~-fYb={_yIXU@7C8ZLH;QF#@W2Q~tbYl6+jnfvbQ~dK+ zQ!nbz`fgcBTwHQyPJdhAkttsL`Ek&=^FPzVO?>5=~R zQ&Wwow7RLiv%9CGzPqa_EhQ^9IzBZ$3v+r0x}z#OEAmqk(sFA$`=njX9g^0(g7h#; zkA$S8w9LUa&4b?29*(xQ_AWlLf{vm7u1axRLvEnYYk&Bm;#15GGvfjR!V}Xovvabu z@@jhDb@w-kh1H@+`=IFPXcOD0h}h(OQ5{NM8mTcuZQqZdy2S%cHCfU2zL8N+oPrY4 zitF1D)r3EmPI>K;p9jSKgUwaZ4z>YdF^L5NVLh^gAf4IZk$_=JBBnAt_!RSjlWUHZtb$CHs5MobvySZo8mO#fl}7^3%ZCG9P$q=?_QTIjF|L78aZslj z*|C96FO6L*r?B+g{KSCh^w6hfPp%xi=@S?hlOWCAwCR^hIPIenfa7m&8Lh-%CH*V`9bsbj z5*4Y-h}fA0AJ7aCZA+^u!h}J_JEgVas85mw|Dnkpi(!y&n}i*cKOobwAJ?D?7Yc#a zfzDc}bNU6I3eLw!{zn-dA=4g7f3LV+R9?~4B|&06;!gPJjq#sQUwJ2!^&G7}Oe!u#xB+@B;xY zSX@*{zab!nY(n<6vI0L~3h3}6BGVDo{uLESCIpONO)ZcDsXGR>DFMzBK+1}X&`Sff z%Boseu#_@EOwY|=jYx|a0AWon5C%ygP)RrB1_T$q4YbJu9axiWG{6FHYpD~Ji)tI| z=^8>|IiqvrW(mNzdfFSxc_d&S3E0Qi+soR_!s@jx;;^k^lvNB5NxRzWQLGsQ9ArN~ zA77{EFU&2iY;dS*6?b7me_v-yeR+OLOn49gPy;+opTDH_=n&A{-hq>0Z*ON~jVLcU zE-E}UEY#cF%)-*@wT(R|5-{zV>8!g!8NMb#NT z77uP}s2|?BasApgps(AcnSfd}qSs|*3(Au1o*G;@rE=hhjp&5|tlzbpj|ap=N0WH1 zO+i4))4}-0MJ(UKBLQ#uVdvh%8oHNn=|6Z(`j0^d3d5gW&{R9LXXmb6yZ0SFc}Dl@ zEqz1dCo*LTb;u(DgHtJM6^u?Qo|@cF?`F4o$%d9BjSNuYiG)mMN1ibn$eddzpTcqHH(ddRXa!w|sN$0GqV zR8(X-GweTdm*RN;hER2pKMlZD4Ec-!mhFrQz3erP_{9tWvoUbO-X73L-ZJ=J*(A2b z;NUP}yTR{@R{^$*32$j|TVTq_8$#-ZU58g>P`~eZB;Zwx=PD`9{9Z|E_UyH3+=&i1 z@BN^ivEkVryM9=)e1Xy&rCGCP&z>`TODe)2bs9_*Yx(QZe6i@&D?o&<|xfp znmzY>jg%~;APYrwpqf;9H zfV}zuTt`KENGQStu2c_n@c@bsxWP(I3H_Qjpd8mWq5}kxpn3t;$p$%0Ft}(3`D=mm z$>4hlr;`SeXb#Pg(K!O@AlO7kq6ze7ijegFRbyJ5OvB*I93q4JyfkBxU9d#h`O_0o6mV0_T6Bzm`4 zMt<|5-cj?1O)EF-yOBNohO!OO+=FVVIQqMxINhE5)+^7SJ$=%gD~Xap(1BZsPl@gR zuEkPi|AAF&=FOV<{p86LrkoHBOQ_ZuQ6ReZn_k-sXZLK`#3KREm@#A8RHapiFW)!e zk$|ZgeLbmXHt4Zs{Eq@cbPgx`00-t#`M_iVp}q_>3;;` zTK^;qjK~s1kbf)76d^03`wOIBtmRfy3IrSUtE>nR(HYtqi43yuYzparIZ`O2;tkNi zMcCzV1^IfG#w**HW+1Z~3L`GG4`kBDM1o*_@f=6}%=9DAFja3QYD-CcP_X8y&Q8K@rf92HQP56=y!YKdC zHer{h*V!Q9zzs_Br~Q6MXGBh}F#e|r$ZMh^!_WNBBLTm>rKP5JN=^N0Al0D4MM{G4 z?caX=>pvwG$-&-E&#s+8J3$@^`0iseAl z%jclg|AZ;Z3$Hd(wK)(ZNWHa{3OYuDZXVyVdd2K13aIj*JZpD+Ba4BuCIE<54E`K& zdH=>0OTV8u{yT*!Q>U-8tfVB2@-o~D9tpUw=qe(gJQ6UE1Y98~$j&NhCC+X|n-&;4 zaEXFPpaX{94v+MUTN{d#{d_W-2(^jmHH_XT9en%KFF$=8?(gA|fS>DK)YU$B#fy4n z#KuWj6V0D~`}L;}!>u*K%wTKdE9Wq7dWN6~DLPs*K;fMazx?#$P)~y>Ki<>y{sm2~ zbJ_;zu@@E@6)gb{^6=YtKYsc!)Lk#g3Ue{Ne_rE^*7>K-?lAEqh(0v*_TBsU!;)4c z-~`$l-@b6>toC&adp9rtpwP&E^e6%p^1HVK;)c?UP#3d%S9Q)^xcd0D1L%Rlp*Y#X zN&fEb+riqxtPtB5#zqD_5-{2StX#OT0FDCW7{k@iQg_j%5HamK6cf{+ZiV0t=rYb8 zM;!q>C!l=@bQ1#ih}CoS&ojVTIA{>E5=Vz#X9Eh8G&*ZCLLCUG!wIb)ZC}fo{nV)e zJ5UQ0qSx198DmgfhtB+PyU_&NfYEC|lcmyDAc1CyI(ktNP0mrQuN$TJNsBBm9NDvZ zx9(HFnjSGn$6t!nEZVxiH|p^v10D&OM*>#4$|C_+kbEHaFD?XckTHzGBLVYBzzb$g z95?RUaS9V>Ex(ThS34(Hs16jdMLaRm)jhdLY4)@Uz$qK2FnRWR{pYVNUt8Ot<7$Vv ztMLA1&BL4L&77|I-Pmuw8K*F9_V(L`PhVQRwx))x?H%p5SGA68Svqs7!grW3Zqm#J zM{nH01WVAN4tOMBx(m&eZUz|83g8Xq=VWC7O&~EIuT!L+{PoR9lLH_bOOIzz0+|_U zsZ6tT-h8CkQm!3R-zaw$GxBq@GjaE2zJ6@aCF5ZSrvJ-|G2<(g19Ai5L>>tkCkNbd zIvnswz%lW035iMI?+?R2{{7bvJQ6UTjK;bO;C%2%z^p`vj0h1`9Ae073&|$OshSFZ zNC?U4Ov2DKlpc|YVg&%)1hy896}c%e>)8ZqQqJu_TchTXlVYrVk(>99hgxH_0W>avmzyzF2um?l>6%kUej83AI1TBvQtf6sA?c_zDpb#{&YHn$6 zi7S$JHx#FZdfJ#hyLVGZLtXv!>0_GquI^sG0ZlEnWwH4^ZDmObfzFoZMtTV2X=rGu zt6X|*=j80>(bU{plagL5%1?^+vwHo^;QB@Fvl?eKwa)52vw(f!jO4zBLTA( zUX^g_mI(^cOy^Qv6vY6Zx-7 zN`?r?j3h`$2h}w*{w5JYMhZy~4E!~i0A~S5X8cbR=mPjt>&R3R3900m<9+(dcA%RH z{->U5qH5_O0JQDB*<*DN*jsNbOumAk@m~Ry)PCar5 zZMv-O;m2udiM)PVef5+XlgHz5G3J|Z$4{EMbdTm$gU7F|9UAHBZL8stfbm2lde5*w zP<~rn0OLL_IWYnEj|m5;UE*8n3m_~P;-3H_0w)AKsw6*1M397_$5Mo%Uh)qLuu0a} z-07bJ^5g}?V1ap-X2{So$Z?0kbsJYhJ;$-uR^yi_WMAZjej z&P9c?Y0bLUL5fH~5F^oNq-yu`@J2v1MDS0?w3uIT7$o;`b3 zQ&ZD5P1@No*i%dFMHz$Wj z0`8`+zP-It6zq|R7?l!8OJd~=T^$q^lnw!Yg;VWvLBeG&A!vaDBosLzZJ)IK0BQ?q z25E1C^X}?sV>GFBaAdHjStKZH?4?qvQ96$V++P-+A8&h4@8Y)I#}1x6x_kR(f+C(b zdzR9idCCi~d-T_Zre=E?-oASHsH&RE@qN2Cu3NEq-t0M`E6-nf-?p#KJ~7(k`5j%g z!^hQBkL<(p6^rJf(r>Qvy!m?!O(cB*A^uK}Z)vL^ICx_J&K;Z9uUo!w;r#h17M!>I z?A^x_X|b=PiT*{cQ^$@S*uHJs`ZcSUEm^c+!GeX0m+ilJ_lZO@kY{0}r=y{A=-}QT zc5mIVa>a_Ji1U^jL%$+|W63U@xi|w{F?Ijz}@Z;brgS=>s$%oCxqLl|RS2FuRc+N6j%P7C6fPboS@Y^q;v;&fko? zWfT58|C0ou8S)ALzv2Jx%t8(cl*t#ljG<12JQA=d;OR|`D<*c5_dj(fMh8E+^T0Vc zqoAy!7G0Q{>uNJR&Gl4H>KI!|`rb=30+Z5w9n7EE2iXTFq~%u?XNB9Cn_W4fe&K-y z8s_%rTf1duCq)E!c>Uk(z4c>M*VZ?F+v1iGq!cgi?oM${AV@+WxQ9Rj36>CdcXxMp zw~4#U%)}F<(3ZBh_xAff@B3N%Oak|Lotq$vJ!Nz5L@96%-!o?V@k= zM(wVGg5qrrL!r34p(Z;yBR${U!Qaf*!pq52+d)g`=C!NJ*RQK+n?g|&SCxn6g!>uV z_}lB5m|H%$`&do&%EhaFsP1&e{9$m=yrgCGZW} z@Cjcd0m2s`Oz)MnQX!p0h!5Z{i!cCE$@>QSjWu=9W&!3$TTC$yq!LhK z6bn#d`{^rmdvbK|()luzr57i30Fkzq>raTS453T7*)8ltmDev>BqJp?^-dL1AOP1! z{g-Jz$UG7-uq@k()YeUto;*ofUdzJ8#~%qwJQ6U(@vA{{3Hz4vSecKHM*|H&51A>4?DeA!HBIpxK^MRT^Ryf(46 zcXS0(3y{Kx894X0Aj~_+-^b5CC=|FZ$tjeaB5^)Lj?kJ3X=v4D=r5mz4jWn7+4S8w z7VeeW=b?{&O$8Fvs0^E_zl=mLM16AXFLF#2( z#LN8W@WK60I#D~?=;Irj;KZx_@AGl$FdrXepIqqF5#-DDp)0er9@-gL(i^$(c_d&S z2^iwRBLT-ddifYWy#3_y{d*7ZDqp&KL-G8Dr)CZw{)F@^6t|_hd+|uX*nEZtssB6@ zFiWn4ds$c%>+NA+q-ABDpJICbx!guQb(PrCic$uR0{?VCT=0^7} zAAjXxq3fH?`s@ltt$9)A3YQ*Phd3I)ymnUm>Ag!QyyMJYJdKQxPfE)Yb<|}9J6gTg z$quyEJbPfzVY!pL*4*$iRnrKGjEag$6bZ}X{hf0?Oyg_~9$mO_Ly<=U=8=HYkzACV zfr z#%fEZNlVQ-uw;ToaTy92cqCwZxLK_hjQMWD>>noXaauX*n{U4TcI0 zBU8%`VO#2hCExB*-#lTb$%@gSj~Y8-;^d{PCybtcR?i3#!WRESGrrTmH}xM=kIoq} zV*L0~qen_l96x*E_RCLS>l=57E4Am0_-6lx(f{~n{@n57rjP&jn-NoGCXU;x45VKk z2^jK3wi74+wWtF?hKP3rp+XBo7 z>cYX%nPoyR5GK~4oF(`S(`h=s1er$^j%{N$2%C@T6{P^izXLD_RaZzMUYC$bk^3h}zH4**izkcl>=odFPlvP$H1bBob=K*`H z6i7p*r6nNu|JUE22YN)Ut-|J}(%kgyxY)R;)V%zHf2nF)UK@(()OKZeAgd=OvDKTXGz!Q*QMcxZhq zT;s!Z5@AfI2iMY6S)Ak*=;;$DXl%v&Vfm*Rqfpdb-B^_#?iXlldQZ(Ftc;|e3jHAe zm@e$=ZY(G+P7MomaC1>pR=95zm|awWN+^OL#MOI#c_%6>NR5b24h^z5&^6V3s;TA2 zBLREg+jmgiEWf0JknGq)roaxusx9@kZFv!e!6tI+m9*h+0+F5}tpdqOf$cdnsK8hG z&eM>Wqs7UtN*o-W-oSEeswuaTJH?jv(EIFf z_Di;7IaU`%%I+Il{zELB4;}BeHrA#^?B7J|pJvc?B%v>Iu|rUrYBTQb(gvL(*#yNc z8Kl-?G&a;t1H>2#kNl}1F)4f zQ_VX|1Ek7e9tpUN0R>jJ{fa)9Z+pc}RfSoJp?;novFL$NT#%j3V2CRlKYvD}%Xj_4 z27tvT1p9cnxHT1TS4)*3D={M27v**?PO$~qnHeD0 zHoX7qkIx@J;OjM43NoWZ0)0H)ogE$gGSS5WZpenFPyhV<`!D!-VSRN`dPFdf1k57= z$A$$5`1|>I3rb4MS*(y$=IY8)K~a8o7OMY~;v&LAf&&AnSctlVV4(2_aJSjtWI7j9 z6OKY^9>AJz0OuP6AOplq#x?8WLrA|&6-O5Wk|g?zE=K_0#K0o~v%8X+9v|#(t@HHm zg;Tq?Y~M&gzFXwdO3{gd?GMEnxq0Ev7J8~z&mP|os9pl{-LlKN7)TG)5T&{xTTm43 z$s++@lH0Rs?V439SFc&Wb^D>44<2j0DCL%{xBik6d*R0*Nedq48*YBvP zYnBj5eQ8;d-Lt1Rubew|V8@n?8`f{#wR_J|#jCd;s%fAj9#(ouX`X}H9i{Up4(!~z zWfPAC%(dVGkgotz&6?(rbfXaojw47piib$Li;9Yit1Bf9UjJ6;vGo7~EYG+Mnk7x2*f1C^?;*sBeNApLHnQ%HaF)p^eq_m>a!q`1n=j7@c zlSYpI4s-G6yOE>DZ3>PI4}%k=viQ+s8~5kiew;F9#K`Zy{g%i8GakFx#@E}oxTLHy zNBM-ptwY=AP99I>Zy3v{QR8N7+S}R{mzG!L%I{gdcEj8$lSYmB7K5YnNWkYW-{6se zfwbGtuz6XMG+z38c_iTEzIT0n)Yt$45g_dj^uKMiyDWER`>r)h7cZJSZ`O|&E8ilq z4pk1#O$^0YWP9h@i32}v-new_;yJTt&ziX{kOe@|01Yt4gKyJ~Rg_L2+<$28(hck8 z&7C)A&a5>_ZD@N4KPj&Nw%1qZslw3{KkeGSY{`$aXU>{Ef8NZCXx2u`9U=XS>50TU9x<`1^W$z(5?7YR9s0>!}o71q(jVqF{b>G`-`IfYFo#Sf>)K zV)~0i6J~kwOn~qXs(1c71B!Mi*#J)r6ksKTp_c~ZV~E5(pi{^Z?+iTwf&?F6ENB-( z^WidS8zv9F?;s@5&cQ*j(6^vOhW1ue4$&BRS7oELXZyOXhwrEMzw0GBBp4A<(6K$Sim82@DC3j>T>5e`~IIZTq1$KQ34>W!jXfV3{&iYOl4UXFy1J6rF#A zjgOTN?^wNH*6f+nr%j(KGj+=3Id=?fTzx~r!s)T}zt6vM_TaLmKhFMP`t<43rc9o? zSpJ!wm6Hdcg!=mF@eSlYJH2Sxs<}K8FqSNo2bMj@BLS1D#6mx$SRsD{L1Z2Y_{^!( za{25>bXmAEmTJs$@?{8km~YNq@A_Jva?PRX8>yJ`*hEfib6UR@TK5_c8K}J?qR(4Jf(S=ZE>ht|AwKN`GIdkmTi4(_9UeQ5;OiF56I-?8Q zYRhxHjWks6T#`L<_c9u?a~@Ny$VPHP@8oMmuOfx~(jK;=sY9$4{KSt``^r zj>JUv`8*P^s2!F=T@74k1zBj%n}}PN6dxa-kcfN*EN&{9Z*Hust)e!*fal7h5~K_~ zfKtt2VJ-&W)5dAajScaue=rXiORk!?}e zFTZ~NrKh1FF2u|H>6P+BL%W+jH$>!?4xcjfe< zBPZo9^GLvG^p8&9DHQf+f`Z~g>f&FGPT?F%2ag0y63Vs*9tk+v>(!Ap^Jh+-G=B7$ zNiY!>9=CV(@B$JDQh&QjVndAe)b}r&i5lW@qef3e7ykuE&21fB-Q3;b73*y9HGX>Q z%!c`Z`5HTV9M0Rea!etGIQ5oeWIytU~FNJldoOaX09x^Yt2HL@ngr2 z9zAA)^z?gxi(la!-qTT@QJQ6U{45_(aT^(~vqRyuP(PJQiB>kuSH$caL=Ms)f zqyiKYoX7~APm(Ha`hilgfY?ESgyWSIJSr@}Mu_+UD#J>P@+iY5F(Dy7J}x$f3Mkkh;EU!)tZDQC&u1V! zDA7ww#P-JI1K}Vb<=_HURp|c$BY<%HQj(KeNY#W}fTBfBEzO83zy_e)8YbcCY0WKk zzT?uATHH)R0gM_R37B+i+CO+CV3q_L6H(fj@BBdFG>-(#BLTz7K-qmf5^yE*m^k&L zv%}-D%2gf-m`4Im4s{fz?SCLJQ6S&0yN%+1PkXsTfGCv@iJQ6S$sezY;ZVk4cW}F`dX;A?#Hd^Yp@7b2ra!9B{^yZev z`pTU2P!D?pb@l5CmtN&oF-X_EJWR*@*4DOHL*6%iQ~ z6-D1qP~9nRDa{M>aMIUNdw5NOM*`NG$ z1L{!?at#=$w>}K>!u+h%xX7^3;J`qX;Q0Ida~bl)gky^=cR+L^cRM;VJRC|wNH9Eh zlcr%99uE-`zh0;$HI+1R%YB(ofP<=Vqw0`}@D@g6w+=bJ|P8>aQB`o=1QNn3TCY|bs0 znL2(nJp3b3KQLz8h?%dA&8+MkYZ?VNr*Bd@emPaL&eQ_H~E z%(AAoFDU>KvMvnUWBL>I^awf&yVxu;Ll2pQ)%o>nQ9YdD-b?Aut}= z7#IVO1T68SQYe89l+%HTG-e-xq8s?f`bK^GW8XLyv)>unDCIGKz~19fB%4>;E*sn zZy@+s(p*rwoR|_5;^NS2E)F?oks$uW1H;;xS_1`C-OHaRi8%!z6R**)3S2M z_iS5FK)efoT)24Ys(l*i`8i$f-nOQi_mnSRJbhOF`2JlRHmq5(Xd%+^7c5=2{8DU6 zSFv57*X#TDub(?Dd-}kEo$J>uSut~pFvPR7HqP?#yqvC+{nF)+Vk<5=DdH7|lL z+Ca@KVdNL)X0b-SlmfuE2Ph8I-3X3us94oC@FXAwASXK~D=UkM3_C+xaRv4nbQR9K zU07Iv&z1}>5r9i9<&l7q57F4rmXTA*Tm``<}Rnkn396+1X>R7U<*Mk``0eBBZ2dNh%d3oOE{g4Gh*N zTJcE0JQ6T!1SBRglnktEY!WO8hISp5+XZhIi9;E{lNB;eL&9tqgQ%HGA( zmo>4)wo7el%d-=LTwUPZcXD!Z_wo%04h@f_F1Ofvk=jZb;|;YHMY$O%Nl5QwBO!tH zPlZWKH6x(c*Q2>78~`ZeL3{>-WRDwE6avXaoUFRCjC1ei=H^m^Q6`_1$;U*O7$CZ3 zCB;QZ(}naxg1G^UnTubu?BB&()939aH zYSdMNbBBIA99MKm;U`KpgxeVD#4L%9P8~2{fC2Z3eoI`07$kJANk1-ugNfih$t^cT zuc;n7wb*CiE`rW$CHfo$kayzc0RI zM`9+21Wp(MY!C;TVjL1Uj|9vk0aKe|`U5Gc$%$|>c=7zTu7Qy$j|5D6S{pVWGA|&! z^ulydrry$?K%$7+1c4_K92-Uy$opZNWio&u(AVs z;ZQtiZx}vcnYP2W2FCTJ!e@wMhygsvy5rvc`c!0M*`>erq&je;eL$-4rg$C zMh_u@gMq#QsxH1DfkS`k>h7}9F(lXtj!vooeKP4j;?8DqOQq+fQ~H1flF&(&7$)~6 z1!rcs-Z{G8q+QJRMyL_cG(cywc_d)%_49v_nJ{+TxbYL$>sdK_`3Hp|$%yVR-C&WZ zUTgWBxzZCRjGr)R&l_{J03iiCf}`WaK$T0#k-4(~MLJRH@M}{C55SBDhk{N^3;G*X z`|(J?Y+aK^&nyXK7BOiWexX&5MGHEeE+7xnnVmsYiF(XkpstS2_pb-pG0alHMQDY8 zP2+w>H4@`sUXk4M9W{er6s51k57= zLosM$$_3LAn4UoDExc%?Trhner|!@oZXOAkM*{Zn@rQz55$_Qk;cj$qLs*dU_2b)i z9oaW$M|hCA+G#a35`k%08SSdCY3uq@tvbm5)v2R<5AEG`GbPwUPx+>!i#z;B?FE54 zcKH$Z?j-?sdS{LwKD_(9vK6qBZ@#c|aD{Q%lo4v3pX+C380}%9bzXMImK|3vUwx_j z;-!hDy)&j)#(G(XM0wg=*K*KRJid1ej|9vk0rN<}3yqvSDMh8LVDHGA`Df?O{`Mau zmz+F2Ys{GMCr_IC!&n(j3maFG-yXm93m$jM4iMuHJFAwdOKbaXUfXyX%L zb2oA=zL0{L27D-V8f1gn%W&Bt?#42ZcqCvR3Am&4(_2wPdwqnyo}+tN6^{fw1aeF+ zCKm4&Q{Nwvu({&)8Pa(P>==d;h){RnkVHxtyuQMga)Z;RjLux(aD%ZyZ1h80Q*};u z9_iAohL|IgxFUMSImv1(1O?>fYH1rLGIg7%q*ti-!+%eS#w55|TQ) zOLT&hJ+1Y$jII3>(lVkvLlXR6>b$sd=&q}me`sV^+UCuA2C8>&-?^vq)W9RLAU({; z$ID6k;)NrsNHO;@IV>OUW~guBhNN;oA5ZV_-Zf0^qVO@QMf1104=3O~0M@NMx#;!gwg;li(8d6pU+Ef)cr}!phdfMJTecsg8 z$s;N;O;Fp&+^Mv9ni{IB8jB)4f_Wrh9tpUtgzamTk&itKxe!1K#m<8Npm4A@d)V^W zX2Ua)0oEc*5F$F>?GSYeo2vwc#kFlzCdD#{ zs45ye5O&s%4slz$zuD_sau*dJ*rYVHBZG*%lSqWBsG#I5QI)@?z5ZQ=GiP>hSh)JE zO%W=xF_#*BloPoj!CC9gi#v+vj~+j`a{kN(hxL;Ra`OuX0KqDy1cjP(PwmIoE}U07 zbN29#t=m>CTJn>5Qc7xiW=>vVJDLg#?3MQ(I(0@?_MGCCv&VOBST=vwygR<(G4V;M z866^Fy5sp{2X-AgC9j~Qbm8a)`J=0rE}y^Pw2fC_L`=M>D^ULK&7(UuZQil>__@nB z6v1{3Y4)>Z_L|ta`UZE{&)T4JdgqQk2M!-Se)^o!t*htvo;tX5&CKs*4j5V5I`K%r zowT$t=+uO&%Oe4^L!62Ms>&c%!M@L6% zD_eWVYS7y|KLRGXw+kPiixOiPz|KzKFgG)|v?h9+sPDu3!ESMLZCO@aXrQ;dtCN$n zlbxZFiK#_(ePd&bNZkLn9}gdJ1o5H%DAVDQfO#Zfvz~XHDy@~3MtJF;HAako{mO(Pai7DA3qEq^5#9d<+MW-6)<{rMPjhGtBL;e zI~M`fyJz#p4V!kpp>C{&1?>7Y1*vfX{^mMQ?hx zxNXZK$8Iz@?2x%Bt!`~uIFivXmjYovU4IJ)Nxvd-5Et)-h#@O$^1JLj{ z06QK#UV7ivn-5fR4~vV->`)xNc;VcclSeTiVoV=Dan5OlE4S~%FM?8-f?Mm>FPuH= zd+D(wfR#LQXBDsC zf#{I%6eZnQ_2UoIr6*z_2T=w{14m`gU%q*-2#BGe=jCM^A6z)=`>9i8rcRqNYyPTz z$K`k=V6yoK&;o_f-Y~cycqHIQJQDD%6~-ZP>2RKB{MrWkI@I-7V0Xk-LQ4}f(7VbuymKQj-6LXOj24_Ru)I^>k|fDJG^nj zn$?>QC}|p7JNt%4#V4nuNP*G&`?@>3TXI9(oV+7rqQgSNViHo)arNB%dJ*DFy;0)xJTCOV@TBUbk@ZqN#-RIc@5+>64Fm1w_QeB_Co@|dHK z7QnFZaRLJ`Ai3drBw*|?bhkR7Zjj=ERZ4?L0-icq8aQ3kq>kt2=jG+*=HccHc32zh zUfjKN`%)eWxV64CF9Yy8NlCytO-V^kq5Z8FXB-P-;Z$sFs0EZy0d5}dUQSN79o)S9P=pxH)*-2?d5$_So@+c%Jb(G#8*4`wsPnVqn+R>xhtkw zSyaQH4RUASrw_kYkOx8Z(lrFbV6;e zFHetf(N@2IL*dl1qsLC3zy8V)3{J?A#iHoyY^g3v4RO$VeE+r*j|7Y!fk*{TPeU{y zkwf}qse#m0f*J&(`wz<&BX>WK1dQAXh)8EkMzGV9b3bibzHrW*t!6D<*kQOlOpe@? z8SZZK;Pg*hmd&3zW5H_GdNI){-vYZ2(%=g+!rb()AK0~N*{o?&Q)VoG)s72%p;r|Y z=LNh_+_z=Tg6}6wP5OTR=HO0dr=i9W)7u4wWkoIz&+XcEBP8GIzYH?gvA zZO1yHJm0YEhYub+uyOIqEr*mJKhu1rt!rRxX>CItgkZ9_G`BSr2}%kReOw%noq+rY zM@I)oXBSs$_rfxF(4DWY7R|j1(&HHhNN6xnLIMKWWXgL2R9Iatq_>2C0$7`|SW5J# zjvH8pl=X`R!!R<+Q3`-O{}jN9#3#^K0|O;@Qd%#M1e}?YoZCQ&hz<2{upq~fBp9n) z+&kFU-`U(yofG5knpoRP78vNrAtVX!>KXj>>!%NWogKBQ;dWX&&LyqDGOk9B6*`+y zo1UM4`{(b!{5;sxUKQ(V^i1=Gc_or05gsgude_y}J^1IJ|NQNjcY~evMWNPjo;`W= zD6j>9Wx$Ssv$LzG?^n=2e;61L*QGlfXgFZxu>gk(VTRYkr8KMNo zk)Y0fd`am92ih=+MU{fm(!8WFKTl6j9toI`iQr_0?9gE;(b!oJ5w_9b@M3i6^=xoF ztPTNP0I30qgwCD7xC+zi!2thABYc~TUR8;COh7pyrHo)=pbOA^%5fYbcXc%s6{RH$ zT7>AJMb1$|9UG>LyRr?HkM7&D_wq~k@{VSX&LaWGQweulTMUl`+>oMuao0ZNVc)!Q z<<=98*Lp@~7S=X)>}aLD_xi@_jJULn_yA{1Gcz+w8#~nKp%xDnG~DTnr1sh>zWq5;`uxYVe?(XQw5`Mn^?OqEHV|pG3z%+Agx%sWC4$_1p~P=>VlECMG(X z8degOaP+XE;aWsS1E7GQyF&|zi=&et42;fj_Q>~03O>?(QsK&ERLOjz<0B|Fh2i`X z-Z=YknoS~COA?L3AEGmkoU9@FCVd#ZP(|=fDYjIFuL~9Q3mu^cu9+yuP>Fu!pnoO) z*d$o<9>_n+lW+k7>l|(o$WoY50D#*`XD?7unIH8(y{(a8*D6u(GKAzyM|h6Gx!qgWtUG+uuCyIzJIxQl+0riR{r&gf zKD_N|ud6{4exQq!qgQS*h;Z^?Pj2lH|Mur^pFRw9w$xP?A{*HS`Std(IoSSFQ*eH? z3w!_i13CUZElpJ=SxM2pt`4>~mhQ<&1Wimtv{~Hs^IxAoz8w_S*9r1d<3l{00ExpR z0R!p-h5(NQOv(cr(7B;Mu>E03`gtT^Lh1z+FT|*^A~QAA%ih}5J(~i+%n>_;^a}X2(7h_z={(>+^n=-J$Z2R+SM!9uAW!C^+@v#tbBV$NB8~gU?(&ESL%=M-@kk3 z=H)B5A3b}eYlI>q&>4_#ZAq+)rQXYD&(zga?%jW+rukA^&&brwVu+6B`(=4)v7tWB zRwf2|JQ6U<0f0p-5Kz|vco*3VYy)yaqG*8XZ*hPl0h^IwH8jF@V0IKSK)WXIHE96D z6CosIUBIxf1|TJ12xva&O`+mAK14NPl^UR{;B&&FXocOv1 zM6ZHfU6J(a_|A=M*KXXr^QV(%FDl=C~&Lz;Z3f%S;US^Kfyrv$X+Is-1(QQw_TSeFp;f0CaOkQfx$U zfS;eQkB^VHw|5QtGGNdpATm4>FgXe_+7Uvdt%V`^QjP>U0IB0M=OQW(=ImlHx9Koxs1#e89*R>?Sx6G(7l<-q=9Y9{Awj4i5B* z+GV1I`Dy{}8yR8o|YmYSGV*^ZnJ7GZcdHBZ{R3OAq-rM`( z-+%r7aiF&gzLd7Q3ON0fqe6T<+?`!~<4Z~fy#xRJ`!56!`g#xyZv_rbQC@O*kdM2I zgQJ6eKyH5D$3Or5uV3E38|Z?Erlz{2s31K(!q>~$(cT_ty%8Ba67b;L4<81Bw^k}B zD9BDpjtvh9^!4_1cSH|DFCYK@fx*GIZwGrtt#y^)$jeScFD|s=^7Tc215zORz(92b z{rx@Y4&O{Tzwq&q@e~jLTzq^tk}N^wk${&m#dp zwvH;OfGd_q0_Kr`(SL_p?Lu5Q7aGdTi}Le;C6<+y32~ta92jt3;yfY_=-2S#!#$Lr zpGQO%p2G!jAW9gBgR?+~sH~SS2_(A!=_3e#g9W+G+|h|hC>zvPhm%%n3)I%la4e}n zK}_c`PFWU`!^bkgZtrFxa%!GNzIvRtVoKU#;0<`Ey^T_*;hrCsf3_RoVmuPCdqxE; ztgfCyUA2ci60pLVrt`I(Pc;q5V7d zZriwO<;rDCmMvSha_#=h_n*Bcjn7~8j?&44`*-cxv2E*?_3PH7kHG46TaR72r~cv% zbV_kY_%o#w2lwsWyJyGF-P^Wo*}8f2u45N(K79I0$B3VGhkPsZ3;0!3H?8i0J)D~4CL@PYbap{yZ9`LwsH=Bya8Ov89cF22Vd2vA_W%6V)gTmA39{1) zY6@$c+dBt)#0`Sn3?Ea(%AI-#e*IT(X-yZbquT1)Is#d)ugNdWj1O^kurac5=^l9Z z{!@QXU++LoMN?@-Nkf$&w<;qmh}u|~8`*n^k-*gbuA^VrEU0L#C_r9gRCH2Othcj= zhlP=yo0qr?8uMjPL}%Y(&D`2 z*kC7n_dsV0YbRGEA>*v+?x)cyY^|>>$qaCGbn^@GadJcsb6{vh1SRN`65rP?7PZw? z6=kHSCC5gDM*_E|#gB^G5K*Nnit~1hC z$o)%^epC|WQwS-?0bVHFb{mLrAg$pc4CNQVY` zo}*14=oF-vXaUIY;E{l7cfepf2&YLg`EO6k;*dZ&vV?<*EIB05ysIX;boaU2&zMMQ zfg(kFP)nuk>^GqjI_IIFPvB4y@R(YKuQq7fa!-#HaMAS{1E*}YH>qAXu>9{x;8X@i z+FWaEm+x^?D=TqBg_wCJ*ay)iICTzekB>T8TQ!B)L<*Wa$z4L2w6w<(G_6R~5~Fj~ z(Av7Tp*`KxETE*SqOzv05z+_bS**#9rYuXn`zl6OqWACHlJp*ExW#7X3(BkOn_5tI zfxJ{637AI$=8=F6vzYwCc_$gPg|q=py}6~SrF5svbE;7hb#}9s2240Xr&KBGwOk{+ z@PQGVPaaBk0g``eWl7slYu?UN&vcL>Og>1-{sAU1FxO<0$YJSoU0rcgNhh5>*!{V| zdPGxS=hAaa%njZ&YV1-8sqGd*=i!<`1G5&}Q(Lv))$J?Q_1SWZfR@DHo3ZG-I7C^+BG5m8Yed_+x5gFJ|j<1R#n&3(%M+)qjPiT(s{Gj^GLuv z5-`>yDHz=9<1`hf&*Plok$@#gpk3&7F_;@27iJL<84MlNni`>9@JPVqW&iZEusAC{*v|C%=7`O*U#ZjGp8t4EjCLjCY+rhUzHR<8*mRcI7?!nQRpA0->qEjCT zfE6RrzqeHo>uGK278HdBB>*$d$pgKM6nuOJ{s73gr7S-uGb0nE{DOiafsk7M_mG7^ z4E?>G)G7!ms>p+50|o#Zb@ZUa1o7}lz@)vx=zs!CFFX=3_Dvk`WFwZC7#hmWl9{nc zTTE6$GZL@iqagVw*oeMDwg6fT=xTsv?2WYh?54$sIzUplPed=vq~ncCScGBq*2vu&=-gmDul zPMU8V77?3}1PMq>XLNB-)k~Wowbjy6 ze*D<6<0ekpXX^?ER>aQDhhH~Jb}Q)P#*Q63e&R+GJ9l3K`X!`a&NbZC!Xp8bY;g7` zo;~ZLUr|93G1zSq!;lFNtjk8^)KN};O%0oamk#2iL5_SXf?@P8NWL7AkwO2$F!Vn6 zH_0WD$w|N?0rN<}v2i57ouaPdQ`#Op5-_|dJQ6T`=P+L-SR1ut$z-Jy9galWrszM~ zh3It0KGfJibYj33#TmP#+|rvuDo#d-pXh8rFD4floe21;1HMaQ7^c%~7#f_lA?|E= z3_BI{;n_qXD}=c_MFT0?-Cxm(L{#Y2WK8#TWL-$U`~tW>P3~;3EoUsq>!A*b&M14Kz?Rjy*A@(*pmezTZ`uCs7 z?=g6xnOqGoFJ*qg8&?%?r}OZci=(ljwW*rH>jyV4Ja@J+4MYFivT}GR8?&SBZrxV5 z^s_V3ynpkV%B_oMT|%w&UT5Uwbkqg(NDi0 zn=GuYF|w;^fXV21QEto2UNK2OHp%)r_N`i4%7b$(UI#@OJv=VEeXo|gZ+#OKylNCm zW4f>?*4x9tNXyDPKgIO=bGeOr>MF6N6{V%6kZ;I;eVl=Y!ZjBUa}x)4bNj`HQEK@$qPcE9$7r2zIo3t&<&S zt$Fsqp2Kn{cdfbMWvZqT5*Za0lPD6F#rr$wdYH!98a%pi;fCUtJ?GABzpruQfm;AN zQAI)iQ-ji4;~X5cRj(Y=bko1FUtWIm9$BSZHts%w=yD|z*ChpK=7%|IU)Z(V-1LR& z&h7iQu2r~j%fi_meY9{-+fw~Y3xe#m_w2l@c~ABH>P?$g%|7!&If0xwyLobYeMf73QC>;}5)1vj-CfWh#L?N+-OJlIAc%rG zWHbN?v^XaX9s1*=!-E3@SpH!co*!$I1qSq-sIDj}$VGKqWRDc5F%nWph$w*I6W0-~{S)3(G(nX^k6PWx% zedPStAke{%L?HYmY-uj#AFD{CVNiC>_ zcZ8A{2?>zsKmYYBe8l4BhO)}agaD6_v|MmB0pjfrG3Y(isb9p3S z9tl{4%8b6Acl`}|_eogi0s4 zMC+#XykR;?C>`pp&`|1Isg=mr@(-M3kVP#`mBmS3fu25bg2q zNR5b24h^z5&^6V3s;T9d&VoP%MVQVb0oOOPhs}Elze-;=s>rz@3gn zjhb>Bxl{D**a9A+k(o>1xV5pGM*`-NfO#ZfK$X>#j6*P3hbA;gN;Z9P;m*zhIJ%T)uc#|24c9pd?32*MoDy^C(C3Ccc5t;nMNP|s=^eJrgk#DZZ@0}$jwSt*^ zB)ri7paH_tr7lnPm7cn{6qZhr`T>R`NiGH$cqCw$-4x%$5y>L~BSRWBnLvVMP`+@d z*EXRyXQiM}P*GDwJ%rE(m(eMbUd1B;e;Di))>jv$M+EzOdAK^++qoqq#K%@6F|g(H zAD=&ede_(8R#%yy91#MjUKa-kd*_I#i0~>v`SM7>@Y8mRTkER|Gh#ynygfWTJe}TX z=^7Xrn<3er(k6ijFKn$T7o^98qr~6e&qZIyz|hFp)B-E71~zL86v>XJNLS3Krt>6YzIb;GsC@Zh5t_btCvo?G6=#HZ7Pun-GUcGt^=o_{k)Uvj=#q{dR za7P`8s=NON|{fdi1DC>r|5glUG4qjEXC{OuL4-W3y*|KY84!(PKuB964s0 zA$sd$dngvr-dR;uA9G~o(%Cbm#*Z2~257&d$I9F*uOd2+1YBBPkt@Gv_1X<{r%W0( zf`EL1=KI}uqehRPbyru<0OwCx@#XcamoJz-ZTxsfC!P_b#!Q-V_4$j}@WJp%!1)DK z(x083;AO0#s-~r{^Zbd*LzPFWs!yKg7h>cy*#}BDy89=@MFqIqTc8!0uC9)bZhiqV zu=!ae{uKU;jtKO2b+EIwwzjsywm_FCV5nti8j!}i2ggK(ctbgGb#WFHaaTaUfSl}X z;D>-A30UCq5rM!8_wxJ}iO+pNG6S9?8$CU&EdaSf$z)?lzku1}aP~N?J#Z6h8FAo+ zGNY=)X8xlH96}kdN*PGyMY3lAC5?nSPZdQt7Q1%<*Xh;U`rUGU*Sg9$3??mU3 zfT^wzF5|WyD(|2%_`aj8qNu79g(N^PM%@P`GPJi!w!VRPRW?d{wy)cI_ozW$JA2l&X)@DK zmkh9Hge*dQGLHn@-JkpX%#syr=Fglt^ZOa|Hpo7FWA5nb9}*T3MM%qH@v<5P~RUNmyn#2N=u&(EOwr? zwKP_jzywbRL{erJPUM^%5E+6slM0sCi3ZFi1pE%61gM69&Tg#A4t8N zmxyX;Qo;`UWeA0hBjY%3ElCP?GSs|xLp`LuQ=(|KG;!qa{$5dI zVN9TlzWS|8*KgZ5Q)^Pme0*Bxz+jKCE-To}!9ew@y!^Qv9=JTCb4Y=G@7})^mL+*R zTD-omcvklOm4|8F_zKjA6p<}a=i7H52Ad0F+-;4Y-8z5fw4Bma%Vws-p~eEgb5-{s9lnaMo zW|kx`pHg$6PNL~Bbu^|Xfz*ZwMn9t?-JOEBgu_7yJn))PpCa%S<`3mpNOC8>q)|{J zKL$xI2A5g!87n1Pu8f z{NwY#{@qoT80h7qdrv`5?u?xLO)oe=(MPHX_mf8g?h;mJC5G7Rs6V`S<@BK=C*>}y zJbzbaA5$2V6ibDh;wTHPnB8Tlw09*ZKxVrWOD(aCUX0bxI3g*wGAh zo06=kKwlqkFN6>=ynKB9NaH6K*z`>eH4wY}^u!p36BZd75*8LN5d$Prp)wdqpryq{ zxtXbO(IqA&P@+Fc338UGB9E*JFu*$Dk$_3vz^xfZ0tbp?vd4>?3JMB0O!C?JPs#~4 zMeh9Pk$`z5;El^?PoFF!H9>0Tyj2IT+*Q@o);F@WgpS)nN1ZK?1WbB2C3&Hh6uBk| z2^5L8bYRoj4t<*}a#mPNu1R7bI_-T!gX!cbt%d6mn;AzRQqU!8KmKZm_k-2fu_S+X zZBh#`pp}!Kh5F}Uw`2W|E6G?G8mQ`Tssp%o8}pvBlBTcd=#*7m3>ak|3Hae1#dBAD z(o#}V)6!r*z(?@MzyALF&yT%rRoPJ<2AX%TU%II5iV{K;l8HO1xy|Q){`=3*KZ_bG za>MMj?q9vEcf;8p?~3d`vWMT*A2PQmICA#MeH2?3kNF!0OU9^5PeAYNzsQgLy8IsH|uc1 z=n@NDqV|J%Fb8;+Q{bSF#WP4 zBvjB{5N7)P?Dlo@r^`r9oh-B3Ju59OH8qtK(Dt^_l#;HdfETxpZe6qV2bsxJrKc=2 zkBz1}QKIrlz~t@Xk$`z5U>*s0iu8nu6Qrc3FL`DM^VrK9T}81!cv2P+ zA}sL()nD|0JrTYCp53B8Fm(ie+?Fke=X5g+DH>2q#w zJQ6UIZwU-A>sv@FJKGNbiusE8z08h)lnO1cK+j@Ho*LB%~3nL;>VfOCP|GO zH)-yj!ulaPo8Hixdh_Jo!7GU$t@uS7NP$I%44xbAk`mBmG%$XB4~%8WrxZbQazC$keQY| ziadABzlT^S8!RwSZN-2$fjxoZ^~Atl*-tEVkDLN>_BA6{hB3eq_!S2{ffTtUwz}{8N0X+ys`8?|{Im!^7bgc44O&=OSz8hP-3Jsf zcL`f+D@*c=vXa9CeOw)#>}{-3MC;(@1<fucg1UwQjau^U=g9uW0OQZ@RdPb)_EPZOt#hk<>u{0>%fO2n`Qo!2$a{I#2E3pun zl{Hfe9bJKvAlNPfE(*;9N{g8ASs;KyafGio2&5Ll^@Ln$;ADVAFBtfHhzt7-3l%UV za7w{oxxpNlMszDM z3AxAJq6R@`QmDVHt+jh%wFpgs@!Jr+9_>Xjx3MTSAvQd~-OT9K%U4=X87*Sg@B)zz zOz#%elw`+8M~C>j+1nbvd7=LBkxo!CO15cVCURq0QCeJ7WJs8wqpiuC7pga|DPQH0 zfX|%0_(;pRvx`RpX6=pW$mNlMCGsyqOHo#$(*nU4cl83lg~>j>Fcu^mQjJY(6A3S> zJRtv&v~CWui^(?$F={>-jgVe?I_d;@1vNq{m7@KD(VLojx**`aeT|9cPj8)*J$m$_ znpcUqn{fy`TAG@>aoN5We^vQYClBx6zJC46wHtp@4R7iuOh+=9h%7L;cIL>@6NmTj z+qY}|nl-CeZ9HR<)7->T7!bfte|_!p*;6M@pE`YH-`-7YS1nz%aKX|&k9Z{D6&F;V zi@I{$tX@C5$|C{ew1Kx6&3|gD2nie(dQMhW7FH@9p;*%B(bR|~N5OaW0zoDq%+JCC zw$^DqLm249rNh0pW+>KIR?50JF*@n(Y|SFKoEq3829NV;G`qUwD(vJ24_U2<@>7r= zA$}6MuNgq6uz(~U02);XNI0PRQxXAp0Wwa8k$V4=;mZZ+`rP0?gOrD_zQOQ(l6?9- zB%iDOCZq z)f6NIxLE7nRl0orp@yag)c_z%(U&FYW7niMwdL6fL9Q<7o#Et!>Iz=j#}+wP#aQi(7^`D!?{TvY;bxb z!z(wJv%5JHoHNde0nUD|61jlo|C2S10pWy6N(V1vVBSFXK4+~n(d85Xc>53xpehlv zuC=S@%$oh*B{)xV)e#V zOXtj-KX1mIWg8ACsc62^hC&GGs|!(=J@C`k4YZ>ehP7+X5HQk!D>11YJ=iEuJ_@%%Nq|CyS@dq{=E zAyfqi-IXw>pr6-QmlkBf7Zk%rY+PI%HArR*t*sx+dCD7ipSxcYP)HLiKl7Yl^ zq$998JVJSd{O9HXsFXD+Cg&MLp1}?Q0p$Wr*dvOHSfMObcN)y4#%bP?I6SKXqpwG5 zGm~^WnrVZmraC`Z33N2W!N3aosJ94rbl0$`KxKJJ$r$=}#6qeg^QDom4gaP+jYk6J zk$|x#I=cb0+xxz^qdeBbOz+vl`>Ogu0P;=E%+AWn!J_HzhnxM~hu)^VxIjnaH){7D zzVQu*U+U9~zxpClFM^kb9Zv7#!)# zD@_N69qME_hA$NQi6j?Kgkju~e;pof$-uizu9-d0c@uDfhPt8cX-G{Wu_t4Jh48TH zFgmx1kpa%w*j@w%Vq)~+{R1o89U;IA}Iuc@0e^_kU++dj@Nz1P1yY&~;iu2bEm?-CZ_1hCq2he*>b^hDh^?D(-9+ zw^VvwI;Ah}8lscRHcajVTvmqboum6r+QsbiDFz}z0;gxkBLT0U|AWkgvEu+Wyk5`B z*~_1xf`Jsw-9eG4UTgWBxzZCRjGr)R&l__m52j#8aCEHP_V$*LBXeg-PnhNn* z2M@o1z~E5OX+h&m!a?3vq_%FF^yEp>@>&)yKK}lJ!Qrt)XWn5Pf>`lM&vwk6vGci^ zvzISEKRO|W7Crk6_=8ER8uEiZsk#Xbgc4HHGqag_D-jy3YdRqjii8TEmzS514tjLB z<9{5Ibjs2>+k~7DD%Yhd>=MG6Ap?P1w6ykV(J&xi($aY(;Qt~2Y!`v41B--I7Vg=6 z{YpCuwa=j1rLXBs{(ESTBL=!aD>#N`a`N96)hl5jI*$a*BLUO;pnh+(W=Kiqk$_~F3LGO$=T?| zfkP@U9;hp>*|=%_a(T^{4_^i05XH%y7ij71Zl-ZjG+1Sn2?2!phcpSVHF>dQLrN{ z5;v>sy6#qY*0NQ`4dMD?3KN(eD`W=f58S}vOBMptY)D&%s!s|K8|OqvDL>OPG`9u4IX&3L!p6`3$yuc<3WtsyI`q@-6IU-D zSA6=~(A*A4zQP7Ux?6D2n>&~9Tvbp|QapF|+{FvBr*A&jGqZ6dq+elswpU=V(W85J z@7{Z$^5_w|cdOidpk)M{PbVG;7{;$;@JPVMYD=a`OU*j4WP(L;87ipIBwr}%iaa%c z^2!rRKTce*b=#V=t7T>^+9kVwvuV2oeBU1sbC4>aKjjS_d5FB|o(;pX{Q zCVltKH?x;cA2IIxsS~Bgea9mK^GLvniHXc!#*vJ~97F~vGFV=mpOXp10SsV;|APxr zlH-n!b~2GzB!ig;V6UXpoY_x!VeSpa0P}$*2r>E)8GAML5uyuxO{eKF+M!9}0z49M zXIn{gV|ALBtFZ_CP!X~EYDwXqo&iw_Nhzr*$?4_7x9y#^%|&H`APeu1kdW7A!GU2h z83L4eP}rubyh8l*$M)v#+VbQO3%8))7uMd9@i|p!>`MMvByhJhiGJyB?(C^64Y4%y z^bd{7%qyxwb`ZtEX^6x;5-^VhOu+{VBbL&tAsHVM|6%!;$oKza?=Ae}I+ktWlf+B{ z%MdeT%*;;AEK9Z}GqWvQvSpHGSr z^rV@cvuAWtx=;-Jz%@jtt3>rhk)9zww|4H9(+S5?}E ztt=Df<0x?O(Vz?cM%!xcSrSr+z4FZ$HZlVe0VjZrKQNyerIV$o) z$krEB3H%TJr}0r+yraEQBrGfzw@3i2AG8mY*~r4P(w2+>bDe9-YNzkmrq;E}qLb_d zg}_-7QGk_$!A8vSp_8KG? zdgw4C}*b zZ&m!3MI$EC{3ox8Wx-;lqoXGR1A&yY(tYQ;7j(kF8r8ICjiGFyX4Ms?If8un#Dgc0 z+!6~14VO1?Tq4Cq^qzw~==6{o0kQx*6EI6u=9z%O@mlxp?|*#$`2J0gv=LmhF`+@e zULGz^j{a0H8ry=phEIQ@h!D>N+)!PXn-CV@4GKJWR~J(~V)CtqXVi#F-n~7YE%hQK zYleXb*~7!l-R7mPfswHpHdS?vE$F~A0i*B-c(+j~gwVl6wT&27Ar%k6N{h9Yz7WX( z{vmmoIzV28Rhlius6s%b$MlUA8zF!(I*^=R!79Vb5TFZNy4euaL3Rnm$XueVp&C|+ zgzA;B0oVofrMjjf-N)QOSI;iGq*hGtQA*WL2j$hJ(RMlyubn$_X!kC4ldM`Q!$D;_ zcqZTqQDsUh&jkGV(be-Oj_lgJe$DDt8@6uSzW?;a>vuJ^K_`ouUQ(LpsCnb;$wRxg zY*@c`!{%*2?>lks@~!)ip0Y`fx*5eqfiEtoDev96ZTt2ezwA45^4!&1_cgVjQKc+E zbG?vF1j@7uTM!0|Jeu3Wu&S3^tt>5DI7qU_|EfJt4cTnWoifO?j|IIsdm(El1j zFN+k)uiIeOG+y$EloT^^bRkZ zJZ|XlAAa~@2>$&rbl9l1A*dsRO;Ba={Rg%lPdCmUKVryGM&pY|xa@)bg^(QxDk0@D4z))9?fD#4gtk z?Ar44v4?`U9|!ObG(IOs_cwhBHx3=$zI^49d9#=COu#%7FamBw7|JsNGagpd=O9K_ z{4AsUWT<+C2;`-3;#LSi_J|Ik3YGO^%&thcU`LLS zgGiJ$()4~d6w$jvj(88x1Pog#HF$9U_}UGt=ggX-G(l85^IJOgFx-_QA#dTb9q8K4a?SNt4lK!uZ*b z9DPEg;}etEgX_<~c4F`1#d9E^JbBWD@e>zcG_>;yiinB@lP^7Co(Y&|0+yKuW))ac z4%2_qX=pD~Zk`GF$N6&=6vrzn%Fl_cMxcgg0uF-t!t4OkZ&y=pu$z-lLr+z=1=D4jw+Q zmzY9CVi`={-Xbo~@iErcxN+v#zI_J{9ag@e7ZsP7oSd9Oa!DgX?=gSXmw-)_w4=F5kmzlv zixT2T5A6D7&;COvZ(4#TEI2GGhLXrSq&yQam!1fsUkZ#PBta!acqZU4OrS7rt@Wju zF#)bFVda?41UDqku`f*EUELkEg(;zqukKvY2x#tLazbNp$FE&)-t56Rwa#Gp#jOkm_`Qg*MK1o$pVyLsuH86o+bVc0=l7s0R^HcKn_uqdTXemjK46xI_ zcvAV~*_$a69Oq&aNF)}L&R>7~{A+hzL42sU#iR2ll~qokGpm7pCZGn*|K49e{qgrM zu^`gl)8xTfmE+3FY7f%Hbg)uh1~JbB+|g83losmv^1g-XGN$!O0ntmx(SQKQBjs`Oo2d$TRr1 zK>-M}fcedwhHpjO&m3y-TQQ*>zUj$yWdiT$ zl(sfBG`Ds3aD@P9_YP#U3_!s%0lS>qzI@?K#j&GC$&FV~ntuZj@AQn!Ox)G3g1qD! zo(Y&|0tPa_o>d6KDO@Sa5D}fo5a`_(vR$4DxT8&+7GeKV&!wc90=Si;3MtVOcXfaG z{m;Mt`eC5ERTSrD{P@W;i%NhbahzFB98{fM1AqJ5pTGb5cA%rSD9q;7^Y|M>zH-B>Pmio!7kp(dE*#c=tcW?i|AOHN<|M$1| z{nF~ZXn*skJQMI!V+$L22j1SkSX59bnhud#GQIQ-%x!F(?2V1gt?ZnTq3Gl5M+iUk z=%Ev#N?~bfUUImg|v?ve!8p%nCC>0nV7YlR|*NFI{u^zZtDig*?cv4c(4Q0^Cg=B?$8fgF# zssIa7NDM?i$k9n|B3F~(x3s5kYNRE;g7}1p=NE+r(;GqL$biQL^2MB_0$@B7a3lHF z1Ys_xB9Leu?IBMuX~gec7`u?uB=%wd&;bZ$BODGBZplN3sL9mT^JW_103`Dt@Ec%nmm2J z2|a3*TBNk6_KrfApz+6G# ztzEx-(V|65maf{eUFH6x=dX>;ENyHdWCgQ$CSc5XI+DbkrTU?0v`G+<>EWDk6xD}? zA%s4$fgv*l31?-cU*&MPxniPRhf35dha7%AHJMA#iuYir0Xv8RP|FlNBb66n0dK0%a)Ggik7}M2-Z>2gO~TwM9kgNrerqU~DAaM!ri)@t|DV znPYfj+lJ*U_g=LxZ3X=iU5{9aOx_{!zkc-aPM!&vX9B)@`OLYiH}7dZdG_*^o<2%? z5hR~*`8*S_EU$;qvlJp=jm{BdZE#Tq3Yg?vhe08IXC1J+;F*AVCSXfD4>-|*f%Oed zlD3}TKJ<4;8cK_^lcPes+?*U79PMoET)fbso)Pu-e(07oit@5jW5WC(=b3=B!Q+W) z<8d)a*~6+#ZVs$e9WCJHq1qH+CT`h($iQ{AWIvBoWxXu91ACPAp<};gR(XlJfPe`nfCKcz&sN$ z&jeg8CeoZL+PN3z3)14F!oxy>f&v5l0|Ej#8ega${Anux%`*YxMp2N09aIQ~bP7jg zyW~G0iAg43?Tq=}0&qLkA?aGY5`bxIrHT&OdllvHeCR zb=uIta>^QWQm}KUeGm*Ko(5gdgviB4r88`D>S`&BLi;413E0=awq9g7ZiAk)?TN*T zN~4F596od?@&`tY8Zt%41RHdx>U!bTiEA|sZ?Bs_e&U!BNKP0wWa#kGV}~t%^3u@6 z+^Sk!AEUbY=&^lkW=$BYICk`iVMB%uA0@9aVgFr}=`yv%^RBH8zOJIWTxs(7v7<(g z89rjvSj8!ecJfTXJQFZEJuEUHI~k;(5wI8xHz1#y$?4>f8Vkg-94y|yYp2i|I)GpRwa`H~Qip0V*a=V;Ef)ck$$2JVo(WjziPo))XHK0w zaY98!RWBIa37u`#Icc%_W^VSLZkEQco;akcMy_7@?oIFGGV1lPd;RA7Wf;o#qL+jt>W8DUaRPMJw;ACH|r^RY{C zw#hA>%!ZH|p@MyEk)^UgEnv_l`@;+fXNXux9&d|}jj_Af5jhzjvNTw2rNCt2R~iH2 z7J}n}%lFH&P5mg;&sAP&ZDw|>6A5*6!Ixe<6Y%_nKkd}c$j|9)^|3R1a_ho5wWBJk z2Y=qWdiC-p^XAT;GiT1iMT^hGrFItE2YKt9S8Ru zIk02P`c*3y&z}vVt{Jmu&AaZ@QRyF_>Y{n)>b?WVl#d?zW&7GyOBc*Ww$HQ~v*!Ht z&|F$?86E2M^6ok1eTS5f?f(UrFP%So)~uQI33h7gNTgnVo;FW!omSnw=kTs=Ti2~# zwPfDBIdf*unmwCm0!~S$d5^|b&*YhaIkpD){cNjFrb>2zNH+q;LKr&gQ78UZ z{&3d9?u||3%C`0hviy&Gw$e7e3zJlDaBM zCw3;>=VTp7j!U{NG=qG7n$ly-n%W7}Ja{Ka*ag^6ZIZPs&jbwKkk} zk4<|OCX;{Bf4EaJ$W?Rui`7r;{!{;Pz04VFZP2)EX!W1^PnpMPcp9C42hFUlrF9ij z7MU0{05VP~bbGMh*~Ypd)HX`k*g>3T#7WEfbL4|H#p+!&vau1@wPtvk2bPE`DywTi z22S-n;bFBk2(0vP-{YBpaj%(U$}<5IMvzuT*%pFl036Nd&A>?HQ;KGIl8vPa9Zl z?PxbAJA|U0By^2u0)`V!5lfy47`uH;3WV7$9l|cGS};#hPEP3tym=me=Ggn(NbE3jnIr()FnK`ISmnF!_FW}@|5;Iei8yjaRju|y(?6_Gb;gLwuPfAYZ zlK7?4ZqakwV9jOna-&Crs8`{fuYX8HR7`AKLXreK6zq}3ZnjsB&5<8HdgRDaW5?~Z zb3+I4_{PL?;Sa=hqmONXeALL1BS(*2V`}f=M?}BG^ouEt3EtVH%QFGf){aH;sZc)! z>Ukz$+N*xsD7uC{HZ~(X6ENy2my`Z?AZjStrRKDA?yT7p6cm)MltS^529{S)SV;QU z(JtxspRm#R@Y0!+$IB}ytoBbx14J|_DK&$Vd?A;%oV)ty+NPOG^5f*>>iTAUgq+ASL5|PHGXdB2_Db97 zS=O__I?n{0=EE}qUw`=E_N}`&FPyn}<@Cu@kIWrC144)?SlW{A;q7Je?8%d-&vaht z>FMbky?F54#?{*|nB;Ky8}jp_Y%D#UtgUSwon4$=T;1Hfd;@|)!ieb@Cq9^JHNt|- zxXAGE$nemRpunJzQ06|x#IYPu3DWE8s>%v-vNB=V6XRoJW8>ly5)u=Wk}2qm^QPuT zFz*m*RhImlk)EEOftJaF!Z@%(f^jXdt-#h60~(NI4j~I^!#>EqGrl5LH-wA@i&s<3n`Z*n363I)i1evT9`W8-?uQnb?)E`@Z9=k zg9o&*2)Mw!Xbbf-_iaL*OrBp-(S3C5%psq6i)W9b5)zWr1(LR!%n&DQ9lh)zn*d$4NSwetIuBTbNo#Fjcr>>k{zx~AVO}DkL+;I;C(O|Sh z+L#uc-W>1fsH<`Qz!P_aD?h8MuG@a>>@`~t-=MGvERQwGAzAt1PP(VIZnH3ZrmD8; z>0x|pb$GDJ<%1iy?%O$ib40L(=21lb)QEln@gE1Azhrq2b|peo#Bo-)7|ZS5=fCaVIk^B`F~e_8~rj zECff>$@CvH63`%FUUnwZgHln%Ej1-2g~>VnXV7+32oUDy2v9#DGcz+IBOSX-+37Xg zi4Gohx1zufa~2@xIv}z^C)3#NQG~FirJbcE0biYgnc6^5K1DE zuBEskDd5?{3G3(H@9V?~Kl}4*=|i_y)(HL5v{bA^uP&H5Tz=-7BiBWZz-fw*Uj<}l zgW)e*EOa!NjF`YP0e|=1_d|vcAE|J0>-dSo_F7swU~gkRXT%RhkV$`A!8L5E*mp^qKdw;S(~IOV9(?q z3~nj?MQQ)^Awx!w9yWZa{Mgac=59LoNXNjWOLY#d|Z5VT3&tu_SZ#16k+-Ozu(su zRn|7t);D9iRTT^3!~LD&Kolsz65ZC;`Nv0bxiG&>+|bA zU9BO-&Bd8hg&C<@6nkGqIMpj~gkyBJmOhR&Zz1kkvP$z41b4xq7@VurTo(Y&D z=>vcLYj3Y8+TN1@gOEFoIrgQ| zpF>c5TKvQn!UC4y+aeQ_Hx7TCq~v^8RhASpc@wwHGYOVeY7k=yDl1{XB9aad9Zox5 zNBn{E2&g6?C9=^g4II}f{e;)+Ax6R+&jc*U$tx%VK;+%8;xK#fkcg15=%mzeZ=2Uz zw=SHrili#|nTP;&bcy{vTpheZ5HU)K^oWh}(bRZ$_s(nIV3>g9wyqMrkQ6T){g);- z0g36E(O#j6{?GNEUD(^gd`{bi%l5crujqU~&Ms4llv zKEg<@2VZC3>F^2HG}l)ZMIGBYNQ6Duh4#8U6EM#Ne8VpyHX%7JvrW>T;dJuAuC05H zsH&emdusnF)%`y$Ts&*eQCshz$k+r)XOQa6tNS;vUAKA1!4v1MoJO|;fZ0z~++k|( z<`>fDFm?64qgys_-?exD!J{Y6Ub}d5$C2GzmQR_axXake&iT&FJ^p4I_n%uhI62ze zSQtLNc0>K*f&E)HF8Oi%QB!lPc{}u88Cd!xX6J(jJUux+%G>za^V7;_H!Yt&dC~=A z)7@tun>&Q$mRBLqTwF!Fy9_^r^M_8KS+{6{-26xS7LFk?iTTyCVmDYcP#Y=1@YadF zzpR`zM*jG-ykedSn9D~Y0|FC53o|W~)jSh$L4HoQfHLuMAVpPw=xa$a6$s%@KM-il z7O;vTs4UFFT{yXBC4^W9Dm0vvO$CEUTtWPQtZ@YZD1#A&NWt{Wt|TTFi=!fTwH~6 zpwwimJI!|dI7sv>ECx|HN%2kAgUry^4m=Yuh}5eh9+)}Y+q-eG{K#R$hWzk7sCXK4CS_4C(nV~WDtEV#CE)!b=Q zC&`Z-f;%*H=*Y3-6xJP6J#*!zkgC}g3DZ^QFPb(*Np94Lk)uY996frR!o1ywRZd^N z0XLK!(W2xlKh6GeqWoC2F=OQvC(hlr{}{1-7Zn2M!!rRRU@B`M<^$6vi`BBAXg`>G zWg_keM#o7V=uA< z5IX=x1vKIzev@Hx@9fdtKkr$waP`WWGiFYoK6QC=3q^6o)kIX>V?EIYkY2ot!(!-%9L_bkFSFyKD8b1v95joATqdY15~#OyG8OB=5G+);zU! z`{t!f=1!Y2ZR*tN(`QWI5YG?_LeML}dGB!T_O4?amo8s1bM}lG)22_GKJ&*DaVdZx z7YT`i`EAql+p2pu{4{^zJS6wem_CDN0;U)|ia_y9z|36mOu%Nx_wHM{W*)R_!UV;M zM@#xa`~X;2T`d{Mfp&|F>c4E-xODNf`P0C}tEjNjw;do(${``!`X(nvvMD5ePYgMv+!l>avtXtYO8&ddG@bFm3{ro7%y7GY| z%IXh{Z5-Xb{RzYmIbfh@c}{xIo@(DYee&F`S2j){8S@Ja2@8*4=pZ0?fNH?uS5a1E zAh7?zA)&xuvm{Ewh|^}5^4@By5jranq#**Aj7AvVB!CexybR?prsAH+{>n!EzbusT zPfbe$><2yIHxSb&(g2v_fsh2x1WYLlUzk8~3wb7BUsszK*G?WirhG*CyqQ3N6t?Va z0<86ZdjI>!rkn^jTl2@4PaHXV?C{YuI+2tEoXD8EyWhQg+f|Z~CRJwJAQf#yWS;9XoLF@R8$ZAGxE%7Le*p-X(3RD~@(Ef2no- z^kHHOS3URG-pPY{FgbEus&XP+44>V*rhaJufdhw6oPTOzlcQj>&I6pk`%i6_rr%&Ht-qgwDgu`U+K}SPYgoo*!qra?Q zG;7M_Im415Hn3%w&t-`{xBGW1{@HaZ0o1U9G0aDXtdN{IAc?YwGj4 zd1%A(rPGzW;{29LZu2Px@0OuI&ER>+-I(OBem9Fm|+@(u7INj7lg4wGc%B zB%NiA6)o?0Cg8af739Z_96myB(yV1iuHMmnp>Jem1uP&Cf9*c|Hu6lsD4fYN0axY3 zdblNtn-RxB0H(5BLjISuvwPsvZ=c@x@=U+1FJ2${Dnq&hVc@LPI58biDKKw@)8>+Ny+^kAvo;rQq z(31QEf7B^Qft`LFgLE}ku={>l5_RNK=Pfe^K2h|vMzMyegJts82-oxwKl79O_idh~G+tg_ ze*Cm$ube4Q)faGiBz%UydU@~ul{L%Oub3jQFm|lmgc%$3Z0wzo?M~ijYnwp())mzq zix(`MAvb2+s8Mp0X6|}vWMS**;tGsCW>vlQ?JKI=Rxg~cI7V*ls8I@2=AV3~iw;h% z1i5c+k(PDYrggQ1qzW%V;Jazz-O zpr1?2< zdH0g~i4(__k6d)~@xx(OO>IqWM0RIuRZfDxvzgwD2RFe2rmCWHP|ecb(Z$_MT+1^7 z^Gv{0*$@Vd#Ribd2P;O=Ysw#_A_nw2DG8ZCZ0va^;2VXtupg8~!TgJcy5_X2hj%Pq zisONiLxEA3+n-*|c!=}!nE%lze01{afu%DhkDnqxV))1rW92pkW6PVoVaC2>* z-9LX|$&^XsC&-T+i7Gsbqm=Yg6XN6I;>dio7r!*O(>%E5ryu3yMhzc6Z20go&=9+)wA!pN~;`h|SNs9}r2qvORh0gJI@V>LkD3m#WNURF|szo)B{ zy`8PCot?d-lXGO$G`sb@4vkt=x(TM#563*7NjRf z`g=G#I@()XSO+Bb{r0ba{`vdczK(+8nu@xb;(S3$d{}^+1Ln7txwU^x|HuFSuYdgx z8f0;81M1Ec73U;H1baDRY#S>}Ti=lWfxiFqkKf+)bu`t$m#8Qbq{fGNxj5QcT3K6L zIk@@s_ci|4KYoALCv6p1)ryMq5+fu19GxAlEi5c8c_!d&0nY>sjh5*)zJF0}9Nn_L zI1^EUrvIV;1d6J!r)wdlT7}#f{LUE&0$tVDGwVS_4IPq}x@s}c1k5u5-??++=IwhA zv|k#SS=rL!($U#gnGxabXlrF=V)#nu`3qeGBO^0QTL))1PntiNU+wM9wH3m=thB_~ zh|u7m0Dr%LfIz}X(X_$Vn^g4(#8% zXYUcUYZ{NA>lvC^**Vb(G&{VmugFP>j|lSf_Hg&`^u#|eA7B5#U@Gm8^#HL5Xo|F zb$(%1La2+Qt+AzRSO430pZdCcd-|&@8cHil>O{g^QKld`z}wx`!q~x63Qps$w{3mx zjlzohiURDAqhpej<9u8^JuQvx-MyuqefOi7Pr5lDD5) zqeFe4-F;;1o0L^hTv3B%tg<4>+2Hn3WC9pVq;ETuykipFtqfjR`dIo#C1jT7qy(B7 z=v_Iidf|~F5d0mPrVh!eF+pBVF5c0>5n(>A2F9;6Z>p=GzOHT5F72wT&Q8h9$oFsz zFt@Yxb~e*>e5rT!(!~pxFW=Keo$n5*s5~qu!r#a?z(L>C!s^b=2bvn^)h=GUa!1F+ z3be-X&uepo>~w>zpX*z{*1mD^{yi1tt2b|Gyfn73Mh*?7>C_d(MMZkPu(2_Es&)6= z>B|~-?rG~8BaHwMCoJJoDbEB<$6Mru;0%na^6*T+JQMJ$J1=-9U<6K4NEqi-)j5&Q zhR>eryf!p81ALLFe1bz;K*WLdm8yf{l&iL?6ghl}aUkkJ!xldt*K!@2fhwm1<|=IP zDT@!Mv)JON!~Dn^D7+X$7}Spym;**HCnryWcc(tILPJ4;0)T>2bpSy9=)3^2143D{ z#_+WyM;L*QxakOq9A`=qB)1rU16@zKCBO=D)ixnT!<%3PE|HLX148)1KPVYNpN_ha z>qyQc8V!jt=_9o?b8@Z|CDd>oxHl!=f+Sp`4P8$Iu^#w==)G>8bS)>B;IWe!Je}P=1GzEr;g*4M;Q~tP zM4wjNoklvL016#2^!`#qGvkMrj-KvWC@CQ&ZYQ$0bU6m^c_Z{P^9~R8x3@NT7NHmf zD*=M}B75_`ZrhB$s=V}g7b`=9uzH-!i}46?F2t?}$X#HZH_#!hh+0zr0mHoLxWpugu`Pg{AMr@8*)ySFtA zf}>F*ISaX}Ir#g#;Gz$_ec#iN7a!zg@=Ej8-B*6$F-hov5pa8YNdD&2hxTGYMu@%H z(+3X>y+bfSN?K-C7IK)nS&69kAK!I~@>9L6bss-8^azTMPX?7uI^_6xeJC|GfNDtH z)fo{URxh>9JVHRRkdlJGg5=%A95B$|H!#rCER6HAv2qWNjt38F1}eQn-brZ+cn|yo zk#AF3KKQn>AOaIak&q(r_yn>JeV9J2fMWmw##O~E!z@8{3Z4m=={eacvI~?hK;Ult zBl|!G1oHq5I}xr2lT)w%q5oex{6F*`x{y+7fdeGJXq5lZwwq_y$5TAm{o7=wC ze=-nVowj;#Nm)P^Z$Ji-J~c!z0gck8O0P3V3_uG+0bBYDux0up?gd>|rrVAEKby8n z+53Z55vu~^?CQ?05>q21<*AC3=jlr6{Ts0wz}9l3cXad?xSEpnWLva=<>oM@igfXQ2m_dmLkoS6XX@f$*aD! zboC7YKq(@Qx^(WsT*2 zk|)5t>c7ib>0Qy|bAX81QE*^gKhSr3lMm%aF{{v+B+ ze*hZm@IUmQ?j;@0apEuQH$6hG(TEf<<=9IGU&S1t)=eufb)W&7@%aYj&|$Qe=pI=I zlDENvaB?QaO3`um!o4>WupEerR;*s2~CG)_{6y0ms61K$Crl*eNWcpymm?b7HWGdKBIq(hjOiq0WBF|Y{ z)}>9zlH?n{$mwc4W%jV46m<_cCO~#6T}={tT5K*z`crkg*c)(i60x_Jw2-!Sw+T+A zb~4Tex{_T;YS__P9Fr{V>haNesn1r0)~6=rBOTq`p?`F<_%-j9c<{-g4LDuU*@(9`XLm~AY0PX zn8OBW_%}JH%+$BN>S_b?6J!^Xu-}EK3s9)U(iM`-lRBk zwI_;jghfl+EEWy>-e}#d^W%Q_?z?FVCk`1kNolP7s2}DUJ9`mCrL$ni(5v|>Gp2q2 zm!S&|@0~hg#3Y4rN=gXUb;))%Fzu><1dzu+`41RU;ZL@ z=cge&6YztF4<2Yfdi?yQzM&~B1T9~!E$uCp$tiKZu1?M_j@IVJhDM-xvvYKIMU^vZ zP1usxRtbT=j*E>B3-lp`k++|JKu~Zf+uXD?5fgZIWeM_jGg1IBLV6)(aK*&Lu-zzIqYS{H`X-JH zmz*qV7f2eqe;;Uj(_H9u&&m;QTnnBZ<`H)}@I!rl-Tj~XTHn+MSX)_v=9ZatTuN~i zgjp$?-uLnMPKhMl#m)+k94;HVS#Z%H??gNwjb{Ref5|feS5_bnPm`mmp|UvHJIKp7 zURaO#n9O`Y!|9B%UD8-pFUpAU53)16rD+*nM%oW*BGP|c-rn0)Ur=0}79Qm2?y7k~ z{kCyXc2PlL5ts{0OEG%)uWuz~1!<8nDPh45hOf<@JbLodKZ6|t@l3$^B^9FT+D0PB z#WI179oCXsaZ6rgVTh^nsM}QDaQz1u0`3M|{~_<_U_fXZ2thaIKnyCdsFKES2aKz|Q6wxZ7Pm;? z?chT&1*avP#c!o883E=x*Ob*x-?2@tYn4SO+4_%Z(T?RBVe- z4tY@jNnDrc^77TQ8>dh1Ke&78tSNK$8YCCw<`)V{|1mS$sx!QFA6za1K{f9rfzG>W-;$a!c5jk32w% z;I`vT7WM>(qm^E-uc3)e zL927uxPN5)I7LiOdiiUk+OO21Twh&f-WWN|?Qe;G(XSaQ)X-2X&b#%?nh_&u)_&W8 zMU_d%cqZTio(Z@hI~zQkur>9cKcmp)+rIWXQDJ6ch_9!cdvuAgxG*=H;dDfA{`~yw zr}qOrZAcYK0HoH#&BZmol;n)*v+=KgeE#*r+kx)3n$q-$m=IqtH)j{md~C%?URBfm zkH3EY^lqT9vq>ZrBt?e!dAYl|I>!~D&IiQex_5v7&wg4;Lp# z|19kEz?@vy@afObfBhA2-(Fi)lo1&c;O*(=>|pPXDj;!HRW@o{`lkfU*Es&k+#&9WhX}l`(b!TG@rnL0Fk&J^3T71 zeB0kGX%Y+5<3j`S@$vSK_AXu??zp_6`8QlX&?{|es?JM^3Bm9#NHTV`ad35Y!KZ6z ze)HSMcW-(+TN^9$Q={?e-JPACoSbYZNLB@TYsW|2oSsg+eJ)aru>p2*Mh^>f3o9Fv z^Gv{1HFRuOkBS53C51UzshDE{zCK=_9_}S&L((cQDEJQMJW6)W-os`YzJ zJv}|k$}6iX!hP&*%wOEUar)RVo7OH{wrn}%t2gX^X=7uD*;iE=;bd#3`}p>SbH}!C zShZ~F(q+q6u3WSEXKe(+!9-jsO0l=KGSIwp`P`wM8(03cWXVsdhX%l=emrWx-!|y#6au*aGLUlH7n;$n>Kmm4?leW!}s5T?0Dp8 z`JET9-qFB4EG{myM{@N1xih9H48!>Zjv#m@U^hp5I}~EE1|v{DklqEkNGQk_2(r>y z_2Ag(P#-u4ZmurEBH$R&^7Hd@va@l3fDXyv0#Aqx3h?vw_WB;k=w5L90;SGccQ?`T zQQZS){W<01o3<`rIDg)ZnNw%0RlWgN39=j-8&Kh%X9DhjQ~3Jqiq#ty z&zUoO=B$NVFX-8OhsGwS3j_j2Al={F+a7#r@0!)im#y7(_KAs&i(goDLP~}pJDbV- zdb>KhnsP(koqeKWW5UD2V;R0TH#a|DChx)HLamOb`s%Wx0z&_T;Tt8lii=536xQ8c z2vh<2i5MB&Y$Cl!Lx`DBdaH)_*R{D9GTQgyIOlCcJwN;*N2Fz5*d+z=*3 zd9XooFBAtehNcYlz>}x40pjX!<+Ah@d=7?w!~4|LA-jZYxSqk==`e=HLGUT?E?vlN zq1KHCIl`<^U50+cpMsCk)!9WNv@V8`VJVaBFXiOWeHqg!)SeVy_Kj{A#cMex-!FWn zjIfmYkcE)R2Zd}LBJ*T}$Ohn7%W#Qx%5E>pCP4fp+x2MhT^tL67=g<3Ti|XAc zFO967JpFuZi;2Dnih7TA*!y;qi6H`*txbZP?H z)Km{Lkwc@H%0Xo*e}()DcF;zqQP!9VB%N-7=YT#Wuls>sKqFrt4Tmt?4`dH<@FPp| zqXQghHam;KkE|;h%x~l*BQQSqopm7xflCtP-hu|9hBskvhj$nppB)j&2Bsc35aKK{ zB_U%_W>w&Ez;wV;upZ12YDSV06Qb5rQ+0lFn76ZQWEEoqq(25(05UO7d3Yw^*H1Jr zsT?|Zb#pypj0K{MPAI7A&UKW zOrVm^51-!kNvg6EL!EW5sj8m1=t{sAjNib?-~RslZv!nQ$&mqe+80kMpFDdrMZ!`c z>xl_e()sIepMUMHD~J#Ews>^@q_WECb7nQLzXa4EbK3jsr$7GQB^E^bdzw5rt8!df zS?xg@l7y+!0~y7EKR*BCpPizlAaB>#x73xDk1MNQ^`;zDt_p;7;BUYG@efIHEYAdd z_tyENd-fexK6mfwD?peXT|JO)f;-qzpB@$9^jh=Q?Mp=Uefs93=lZ7B_D&=xjH0xo zEY!pLmFAth7gUa`p1q^3^V-A$OlPj{gm9qMOi~(bZ)Rlh^!kNMcXSL;C&bbQJP2;? zq!={u+uIuPxJm@kL4LkI-kzRbXx_el{*;?YU9hKbs3Ruu{EVd7nCR%JsHm{e@bCzk z9sx*2RYDmPcrKmnCM6{&qL5%bsTrlC6UZ0qJv#7Az?_A^Ed|^|w*E^cl_{RjuBxl6 z^Gv{U;}w+V-=GpS8JU@|A6*4`$u*S*2RAO5_M_aW5hLXk+R!LQV9weqJ<1Nq#KOP{ztDOqjFZ!p_Of z-NOTJVMm>x$)jt>SI?RtH+tmop~J??%TJznNYBX94*Ee6srI19x7By8oHu^Vh!Mkv z4j(J0IBns{r!NgntsPzA14^X1TGv&#Y+5Q03WK4;MvNY(IAhhthfj13O)MSolv~?d zEG{T-T|QTF^vKcRC>SF@aqhNj_q4$DYhg{b0a~TmSNCmO%QFF^a3)dQfTBtW7SJr> z5@L%iEtK&1;qUWIz&sPM;gg$}&!}B+i;hMes|0BW#TY*S`Om+7{vfHZ$mN-U!~NOX z?H>>r6dW9k#Re!>#HMgS(A+2%l@(+r#j_-wh=>TJ;*f(!{s9ajcHT8r;Q2xp4i<1? zVM0z1B~Y+N_{YY2;AW{z7;$u>3NX4+5l@oiI0$G<3?LGf!LQ5D1xp`tbdsCM)g*tI z_Vi7SI8{LT2)ORV^GiffKxo1{AYYcw6yPEdJuVKfc_v_%L{!ezE&v8OxrZHvEuzItTyf+7BOFlgS)6O zH#03c5iCta1js7#KuXAb%vhcYxS25GVwkx~hSea)6=9z#ghgil0O1mPm z(3FYA>i%Id;F*AJtt@Q9A|s-ru|PGw>;3R2sJyycYAcFzQ)B#HogD0JOf7r^!Gsfn zY_Xosw}1O}ptGa7szjI(AK~rn>}YSxGXe8Vz}SU=sTYmT8p*|h6NLZ;1-6l%5BHao zlM1rqNEQ-j9jHh-*MTlZ&LR0wGz8BC%rgO>zjX2B>1+3&yrMe)sB{Si!rJQW5NC6P z7h3mk-@bX{>bdjR?>~O=+89Yhkc+|XTq7=tbG6cc{`j$$=Dl0D?`uAJuB&fsW^Oqs z$C6N%mmU}9>tbzcsIQM0zp1&UwXJ>iUD5)zgHY@;1N^Y)bh=6(`_XaxyMp;8U z_riQZT6|P^SV&M%V1Pe12vyi8A~}aLbU=?whi-X-GzuX^M1+ThhK3+w$9mukcJ5^* zMTG?gd4lwmr1)5b86qMe=QLw+_6^Dbr`oU9@q} zvC~)XXllQBZAfxtDV0|wzc{#M&59Lk)@}Lau!`D+YqvGDw4cAyr-&OmKr<@x6GOcn zEe&2i(R!$>+Q z#5IEFqZl#1lq9UJ2}Jct90N!q=R5h_FGi_jzTL6E6Ezw*AOeUxF0~YP1g96SGCoBwuA&|yWBe($_Dae+585)N= zagDl=n5$g^2n~(E=|9(jj^>+T9mqbQOfYqzQ;>f%5P%j@5pGcVGX1A7Xy52R>XJ-q2 zorhXi&Yn{{b?TIwnwot=XLD6|TV-K(l8>jale3eV9?t~KGXb+5L1iWO;Uye6pBWJD z3zgO3w3W&NwSYlemhuCTKY@v*+-fdc@>`KQ1p`IKhb#>t_c`<~je&K(h2VG+P-_h8 zKeGmSC$91e&jidf0q3||>)gMne*ECU-CH(oTD@ZV;)V0)&Ye4tX9CU<2n3vOO;ivoZIZRC56=WF?IahxzZVm%HoLSqFC{L-*})^o z#nQ&v4QxQTTU`Wl>g#B4uB|M|3UqRE_Yd`Tc5?UjL#ALPInU(8Q&M+Jji?Bynv^)4 zeSph5E+H{7iM(s(qGKT>|GTcLthkVTcR?0&@!4|67BoPC!2U*=y?}nuNk1SQIXNwy zca9#|=SrY~vZLP;PrzgK=GM|GA+f5%-muM)L{tP z13;^JCSWojXv`Rrg&|&c7^&7|K?U92uZ?;T_8)bX(IM}mX)cqq+s}HT0}%w!5KWcG zro9T2$?5r^kr;14dz;4Q_7|(4*l|x!mb}Qk9NGqnfxyJ$vwjayv3YeLdLkY-8OJY8xeN?4WQ7u;gSSaW(m1O|g0xjcjbh zb*&j*=7A-mipuI5kbzS@Pt3`-27#6S?R&=7l6UV~lJ)OsyT@ha3(Ko&8&K63ettFm>p3}W5YEs_ifJd0*Qs_Qq+jJYZD7Z6m`Sv} z!}ib|#!ug_zF*gW^g##QC;BV@_w}C)9oRXk(BuEU{=-ecjsPF%+W`iry-a(NRYNm(54S{S7lxA&cD&3;^ z)Q{vK5c{hCTpdZANj6p=n|sHYUC-_vjljGdE^=AloVVr3W6+L*7Ie@&P#02R#&afd zT=?|$YiUDC2dzC6{a}r&5rF_o>4^mvhOg?ix84gCceUfDp&TcvKOSgvQ%jZq#Z61K z^j~G(eROK~e!YyclF|wk{39d-P@zrPrWe<*T(IYfZgpsY_Tklwwm$Ss$jlR#iE0{} zn(GUF^{#GNICC1$1nfp-is>J28_xvH(9ExO#&LIgr!*I z$VtcQhz7$=H7*8g@vxPG${~{gk9pPbjLF<+5{Erq#)(tPS4%CUsxp$>E9_%3-fM)`RO~vxhGXYB^wJ#S>pCLbH z%;+)Ww!gA)_GAt=n10b{+Vf1noC?z3k?kaac0>C{FFDQ;dQGF#R)7t_GXb;dM+X}`6Y$}A6BIziJx*SJldhSAtA`I|!IAx<2yau!kqz@F zkCz)aW}KY-ipP2u4lW)(AnS&lYO_c>>UGvGnmbiV4o!afLmgvlM;8w-zd+ny4gn&m zd9iBIg2{@CsnU+_NGs6UwwS9SL7}v@GBwoI_W7%1U#q98#}5B;^x)>T_uQ;? z_0j~{0ztN^7*T3YqzQ#+jZ~x?MD$wshL@sVCqZpPpOZvvU+)2 z{j7zr@%5d`$9C*K!7~AySi5)!h2h}3u_(dG+m~koW}$8P7hI#rA&doc`I)~@HNRvH zJ$NQypr`=chW@t~#rb#|8o#u*$xk)A{8V|3zSh0C(h4d+UtR(IuZ=g(MwL_zj#-AieQ9f>$jgc zzUj91l{@Z%VG)th(EqgH^yYX+M_rBc2cEbaT=`j5b=~%3XRq0M_y&bV!1&c9hh*i4 zJL#U>1B#tuv3V(_P$7s zPA11oa3SeGCT*)FBxO%a;?MCNOwL?KTEZ#HFO@hSzt`H`V|n%N6B#ENg+w6kXsHy| zboEskZ5SWiV4zdm(@n*gVc$p!gpx?4YbkC>3V60~!uq-Q`#KR~Vdk%vK6HC!jnFSm zOT{|$>VlcWbMiS~q5k>5}1K z5*{{k$XJDi%f<|!sDdn=Hc3;!p2e>X%)aqOrK7j8a8sjoI^>A^AI|7F(J@jnb1J8IN~ zS(D{P440cEH+1oN$PLUf@3PHLjXHi~@?VC|o;-W~){Q?cSh?cI(Id88x&Q37u@!u8 zN!{)7J5DHlr?h?dfuqM&RZps)J+NliO|2JthGy1~x3(16xZj(#=-#6n$SmNQfY}~{ zX96~I0*oXfxw~F%k87xtwYfQnwZrq8dOBK48tbdlz1>Vau|P-08E7U)czFdzCxV+W zH6^3G{Y`6!xUr~A7;Nbi8XBr&9ugEDn<+$U7Wo^Z@(SsPkFAYe;_{SGOZVWAXEr`j z2{|GZcBObM?eZEVzjiftbk~%ITA6zVghglN74b~K6l0{NiN3?+ccHp|_uIZY{o8ax z{{^MPNpadub$=x7jTP6Algmq$1lh&37O};KtyxXwhDXhqoGo75Qbdp;>V?DbhN_(G zJo2W$f_T^wF}`r;+&mL7&jf6`S2e=j$iULwFCf6**UKj&r6@en#m~y#>c){LE-tS7 zF4);RdPZavAmI|olB|@(!kXI1fOHSXCpVQ}IytF7G;#BdEfk57EKT4Kl&LCiO!Z65 z^0K>r^rV@cvuAWtx=;-Jz%|5rhk)9zww|4H>PBe-QW7d@>7~VRu_m`EE#>gLkmRAfavRY#MM*IiI#^_QdN-?{0>ITmQyk*gd zi88MNTUv7UVFE~daVtT}S?RuW-3!RMMhP>lAu>#@wz{G;N03jR0M`K<1vD57x*SZz zl=EL)M8W;R9t(NFXveJd&|LM?%uYytgN3@~8{0@mys zen1z@(eqM)>lVrGfSS6vIpD1gFk z4d7{xLkWHEa|2yHyXcZyF%>ic6{k?fxnWnWM0c-P!rtA^5Nw(8ap?vT0&Bu z3HXTSxvRJCKcfAQQ3e(UJO$J6j?Ei3ZrrqG=l-Lb7p~sA_wccFv&42NNOya1=HSkq z+jk%O_3ZfzS8kxp$fKv+UJ5Z$cBX~9n(6DCS((1leT??>*>gS2%{Eh{1kiSyo{|t3 z8RYF^Z*6IgYNX~C#8*f?@-ZHTf76l^;-aG>Lj1klTwI(%m`)opbr>xDlZFbTadELx z;o$l7_VV(g$vJr*+t1Tce;6bK3GtC3K>;XK1Sf*9jL12ljkvV59QA$5%DEC;g*bGQ zoK*~fog#Wf)@m9{d~azS*mpw5PXPD>2{ zdrw^4zGkxgC=&mLbs0Kzq>7HctzA(`S$U4e#s!O(PF9o|`qK|+oP5}*iC12}GQcHA zX_4lV1#_mUj2$(K$*IpzLx;;uIP>W7GjjgQO%^O$xp0EKtSrrsCqO=Iq^#O`C=hcj zc_v`e{mk^V)Rg4-*vJ5Pdkd5zd-+mN?u zK9KFAP)k2G{H+f)S$a^r1&KApYKFhRk+!$*-@Sh;E$M=)EV!sdVE3!|-3LikO=(>> zO2%LcP`?uNjWPYw^!;yZoGXby2aj#RUST%F*pO8qWkwOt9UI=a|uj66-4((%=bffKLjq;x-2zx~Z`~jFA}W z79=M5AlWh*fTF4|^B|7NJu-6L4>D-`h&7<7-#UU$*6P za!((@kO&%&Tq;s;D0p|c=7udxRi{lFuQ2(1WP2A~7=su_*vN_N3=eGGwqViJiON4K zD#|GB6ZBBQHd+ESy06Vl>)7U1D`u-H&-hthK|xM-foBUXkIM3LHapJ*+}-MPeC_O6 z)2H!Fz&sN$&jhS~akVN5D*a zucW_65Ms|W0Uten5D~h4C+-+o@l3!7ZWiQ`!$2E7hk^L|2f8_ASC%A0W8&}r$l)Py zrOCaXseBOP%iIj|Vg@G|l(TzM=Bkh*!gU~U1>}SHU+kzrWARMD zJQMK#BundC}49ZDJhmXNy4!w@| z{!RaZ@o#BtY;Nu7rjkAMo(S*2GXaBWyi`~&dh2{_rJun4Z z0>cmdUQ3(z&ehB2sEm_WkR2_nJZ1jY(^qcmJl8j}v}^&YhPFD}jZ5aJDvukls5p7% z;+1HeS*x^YKN)(2MI*=UuI%~9HQ|ysCPz3^FK4S7#>BdjnKI;Ie z6U8=}ys`qr1|0(mnj!2?x%V^yjVFxbptz%>9!#J~f~K}+ik&fe#aFqwBg;^0*XEU* zG@rPawF)^okPWP4CJxCw6L6mM^%Doy&YL=Zth|E!__;5As0>dSJBYP}yQBvDnjPJ> zbFHe9qMV$ZqRN67PR_0%$^i5o37=svbZ_1|zih$EMapvWW5&pioxDoV+Kx6j+J9PF zGap_(ud!*)%vqCVN6Wwk89!yKuA#Y&gR=`E=z&YMx^`Y;!_rxk6h_O!MUhulJNo!J zIyky8V7#z7=f1|S6>4fz(?%tIdL2cmB=g4 zT5|l(!{^Xq8#~(huzxl*1gbAzxp4OE*>mSDS-0`L-EuK6R_;6@LVE#PoZN_Ym1jpev9p$qdS+Vj8l{! zGg?kYZmvU8Tnw1z;b4kdnmxnwB!v(6ub&GAksC8cPF7LvRd8T%NN6Zke-Sl%d1cq8 z>g-s-GXe8Vz(hAl1;z#WL>4$i%E5`O0_^J8a|!83K?H;dSmQDpxU>eg>9R8j$+-?p zBK^U3U7A3O@S_R7mlMX19b)hZgdx}roLu@YSaw4VC<{SSB7F-ar=$roW2kZqOHalF zU_^S8cneHUVo1Qb$J`Sj4hN|U3u!nLb6uzdE5(d5>x2<37?jf)ie~~Auym>`igMBt0m2Il0q;^^KtNy}tu9n(#z`J43o8%D^R%RR z;_-YP0VdD7dR)ijN}tMKv(jA^C53reX~~HR=o1|kRfo##oD)Mwcvb-%*xB4H@OT2u z8v{Alf&6NqW8o8*k>Vh`B`qa6F)@L7P_gXEtFGrtlQMZ>0YHQ4$w`Tfg%mxQ&rS3K zL}*I+GswTmL>z$>pEbDjOiXZK(oJ+>`YRRV7u27|lYJP}eks#0(e(~?Al)V9l^PH> zoRpM3ht0qY%%FvVoK%oAGtx(p0U0zwJQFaiT4Qr_Lt|-nLa?)o3C{$~GXe8Vz(`eq z8NvRKU;*bIu>BuahO89r|45J|wgs}fU-y5GUVw!*D$8L=vH#O}CMO#WS6d`RaX*H> zA^)JT06CR3KMbr$SQz8u;UAMHYNpsC{0A870wGy1I3LY*VLTJ?=;1#hx!|WE!$yr6 zI!8y>z}U>Px~3sYW9@UK3Y17Asc|6e}8zFXR5e=8Y?W$k&L2q0x$G4BqgYK{8s1|8kK}l$a2Q zrAkRUa2zNF2iFHs3MBO+YbZY#lOzqL$Q^~|aJ$Ym3hCg?NO2Y7{E*()ae2gCS5A(i z2IScQhfl8}5J10Jqsi#jgcUo-Z5>w!_PY-BJe~HH{_*GUANsmG@DYk? z%L@zgk|INVJlvgKeB+8s1UwV)z`HLW2RhoCYATBJ3Nw?!1ASZ_o$PI_ZLF;v+`N$Z z!7~AK&>4iZ@ynZ279hx8o1iQ1nUXYO( z>hEf6?VeE8jv~O)@pZT!!q|qwl=ztN0CzK^r%#^hI;A%;ss=<>Ylz{Yy}CFnE-EU- z*UjG6@Wo^88@Kd=iclwl>@tZPN()nCBVUJv`8nE}ym);3{8_CtcdR4x=_n@YXm75o zEJ%ur3=IwSce2ud^+NmVIZe&er_bKznSgaoB$7^HU1?TSu#c0ig}w=@q+UFK`sB$I z$4{O-f9HvT8NS?3VNrUNx09Wzslkg!+BdFTzIgVumeyIS0%&SU->;-TJHp+;(!|I} z@5$qPH+d#t<_+*nz^y2%-qisG@9t?xFu#BCi2ANw$M1O+i#r)z7f8_>g`H5~o+kg> z8vFO{_+<_9*cL6@ayz`SlQYkVE(d07TLl^b`p_v zcBef%t9f|;-UIs&?A*L*`J(x=W=x+pYvV2V^m6!NJQFa_1dLlYR^A6EO)CC_N^V7k z1mWYEfT2QPk%JuW4@Y#0rDEr%m?9#k-^n?!B1>_id&q`*QF?X-2OwZ6PG4^`TR@me z0o9TR_61DNfQJxM4izDe*?nkBgZ4U$7?Pv9v2+4D%TOIjVo7iB3)e99VRGy;TnCbn zH-;|AKMs50==ndHK-mmP^lWSEj(>_W4ej++?UZoHy-z%hGMCXLk}mUmfj-_%JQFZ7 zu;2HzROX6GvvP|v5~IT7BW=ziwHxWZR0l3zp5FHAz`@%7jU?mu@|E6G(hGga{|s zhG?sA-Lh)V(uH%>)YPU;pSyCi#`#-1&t928N-+MGxFs1NN=CX>dG6dOAAW(onnyBIbgl zaAT8H4k>Dh^z`)_>wuh_$UV@hO$=*C!v?Gn>u!$~}|_#Z48SzwXxuEs#`BM+v6? zB<=xSR=VruUB8&Lh}pl8E9L?@8{N@aY+`75a3W}ao{Q=EfD#Zeirqlq**yhrcXw@? zrK%t=rxxEzFlVklAsHY1zoW~-K2&ST%oz%@vPzdL0dXt^z5$h&S@=QH(UW;X)9m=7 zDdXhF$jYqKc0?#(R%={s&Z|$88#QXgh>>GtHru+QL&WRI zsAz7yMBEywz6$b@BSwrEHD;NKox5*PSj6ksk&q*E1D|cv^ChZ3D~uj75)WAN3M6~} z)B$qt6XcnI*|NsMpj8DJDzGY)=OTR-sf66Rr-e(gWlsK$$#<|Ht!t_jKGvkfd@ESmw?%|ZPKQ(0G- zp0@6pjSJMYn_HV&fCt4P0G~6)bJ0Js=a%hzr^(1FjMHvyYHmSFXLSueV$vU?NbB+5 zwN`D;RsnW7{o10vqZPu;x&!=ao}F|kbE z-chvwxyQZ5ib`^0WMpI&4_hI2<{J;CKhh`(V1iuC^*reFBf zXf!`SYyU>hrsA1^Dc0WAnt3d_gQ{#bem%aB8cI5fq7ucO-QLf1^&~if;Jd)5M&5~} zrKYr@tG6{Hz@}7WV_M(SBWB+v&jj2qu6pg8km7Ce(25 zV-k|#PSr*_XC*otJ>I(g=Hu(yCl@YTzGRMu&XXHYgCb+%acPnpXzA>3_VDZ~Uq91} z`&O^pzIoYG2*F7UN|rm=t1){Ps^Muym0UcM6&{u6w=z(R2b~29~9|kb?&9R)n$!sE0(X(x_IuyEn^#JuK>t}NdZnqrhzW^ zk6pZRO+#bv&aIo&FKHdV!ZQKq;+l~>cFr*%CYbLUB__a-F}H*SaP^rJA$25pCSVR+ z3G2@@0eb{TxEozv8Wv=HZqMrVJ2y{S8y;kS@4!9e*+c&;qFnWLY+aw+s|vDzx_{TE z?VHwLNDj7mrFFs4#U19kB|lKlE-%8~y*R+`)uBB*c5FDRWd*L}3yjI$&jd^dzIL7om}df>xAhew=wKRu zI&Z}KP0IiAAF|iy{{$xApN0(^A%AAQ;<%wZEG+GDwy~Nv{KwHMKabhyG;b*6KMeVC z#1w6w3E0-b$;I7^dG%BTsIE$onS`wF$j|_9428A#%nh0ooqHsfgA!vk< z8Hn^k1Xm)XqN1dDRvhaIa*7lHK`t`;l8_z9HAK5$!l+&r;6+F=MDA-*UUml3%h8gP zlm3G-90K7_p$Q63uw5UqE0m#S8Nqn*EB>Ec1+D`QFjR#Di3^ake*^tMAdtf{5j^Ls zoQC5~aDh26Ka*R*$tmxF8!qV?=<9B&s4fsM-|y-QE9x5S8o>9|R8^E23$SiXN?Jx{Ru0M}cKq?7 zrc97m3LdhS)~3o9QB7P-QdmetY>v~{)w7d029$Gnb9Eo&Bch-#bK>(esg{dp$f z_EcwEOWeR?$G{4pixdg|L%|)i{@;J^>8Xsgv!KPn7AbW=EK=Oj!D8)iKfZt05^W2r zZW@dG8a4&H1(L8C-hTPin_y$Y7AM=2zI6~&hyD*A1_~_osmLYvEHs?bx!>?fcqU*d zIHmKJP$Zvpd{F(r>c3Qf$@Qn`B7G14PX8g7N;&nX0OO#3laA7Gd^7m^nqi^pnyHk? zpqx|~!|97_YOE+q^a}L!i4`;;J|;CE&~Q3R541haR28fY)gPl$-26fefBUH>)Xpn7JUBEmAvw&;`la?&tz(uEu?fkk>4*SHI&1vgUFldFdh&jd`NZ(3c1zvSZB_gVy*dZ9{pga>WYJwn#g{&rpu(^*{y8k1D31w#cKjadYG6VY!WGgBne$#K# ze=@p2wMyEAm4bqz8c{n!oZss|)%|J}i_-kfo?Se6{N#0;bbZEoU=?f0q6rz&Ip#GD%KHgdP#pBB-kM7#DZJw&~v>p11`8jz70@8n2kJjom z&*yi}9y@yK(BU0xSFN5qW9Am~#AIyVAgXC;Z5IjbwKi?ve@I>Z$jQ@(_pDz!TXo`; z%f8{!afvDEV1P_>Ji2@9`tAEQPMkV*Y}YZ3UGrznQJr?c#w#!)If+TCA75A*d|YoDjdN2`mBeyZmgA;Rm8O}TYKn$6gLE+u)a_cv##Fl_O1Pj zG74Cn^zyfc8E^yEP*?Wq#NipEW$A*2TfKu_2*HIIeogMxEz5?FpjG>Q2NqQ({CIU$ zEYAd-%`*XWJi-lMzWny_{XlP9ePuyFh{Xlcc^2Zk>()M(UYAf=RB0~JU++7?T z?42VbBf>#b+aUVmkI$dp4Rnh|NViLj2=c}BsATKl9pKM10rO12bkfFIn|=^WDE>p8 zAgbbpDnW(t5tu8EDg;EiNxyKeV7#K#f#mcGEpzhPS>t+eGf0O(T+JX6-~+A27@C4T zVQ@?(&jf7o!H=v7{hB z7xId#@H?jVH+QU_BR66w#{cjWn0kkfQg|-N%t#}1Tji|d?D0Tj)l_*rf5;Eq1BQ=2 zfI{#wWyK}s6&A+s!Fu}^OpqBe>_?2ne?JZxI&yh1>d4>_R8e&6j*a`H)l(IR|HLx^ z1EV-%)F>JG8Qb<9K6&mkwkV>}E=)W>f9lWUP(pRgn9*Z^-O!t_w%?8JQHxAw1I=Sr>DDk7_12-i7{rzv=*wP|Py{gOU`D5&hz(h~hHzS>Y9)2{=Xn(%~KJ zSFKZ1nLSHMQBg@rab`41!qUl%n1(w&p4~dMdFz^GiziPSrywt{IBuN0x`35pErPsH zWcljZ(M=oI%~6{)PDx2Y9up|d4oOST%*xK8@nZcuw+<~|wRD=QveHc+5i`GVW!JPg> zPE_7lhTUE0%5`A3#Vpx}n2K>@47t?E(iE6O+=-tE9k*Z{-8fV09~c;T(?itBEb2>F z53S7@#!272Ya=G`jyG?l4uyOSb2@9vMrT1;#`NP|0&zh?8^SE&^4)RKB8V_Ds zIeGX72KV&R*VmWx;J}R8^CwT30P~@;VCT8J&y1`bT-`l=>GOv_$TI;LC zd*HVqGnS3U9~qK@>DMZ5TvBrXm|*F9G);Zp6++h*u>NW7eY*d zSrRhU1B#859MmxwAP0=5BoLV3tSsTj9B&Bckq zPKG*H&ufRYP}&VHhVW5wqdR-M+Zzg^16}mBFaCP&l0B-MejSg=C4K!}ZMB)fUJeGg z&uC~IIq!k#+5C<4#>G7YZ{M_)CVD$sJiB)Cu=>%{H&WpZ;<^es3N-x9z`K57ezd!- z@q>#;4;?so>Wrn3?y~S5(eSP}{hvNJWCc5#8)~1`P**>o{_Df6MoQAEuA&d6`~AS@ zKWb9Etc_pYJb7sUfrC5~@NG*6H}8Pput-{S;tpZ5zpLfrOQ((<*|&Z3*4+oSo|@ab zLLM4H%MIT^sgJG6lPj7$6EHFGP)Hu-fDxeQnSiDG|K?4vxGq0A-08&?&9moi8ruO0 zL8&ky)9?0<_aEQ(wpV4uhd4dE2>n0fLa-B7N{ktlYO#M)=!4JahEm(NkBF z+R4!amXY+oly;Ng>+rnS(0QgCKIK7IV-&(5062tNr^Gv{*X=$lx$q5OG48X@>cPStQ znh#&AaZ^M!y8~rK0IF&#x;}ZH+q!(-?4RYwjFMFvJAQ#-F(T6lO_O=ynSiq{ z;O2AsIC(jl5yOVdj^~+xK@-MzJb~Z?U*8<$t$25}P$^_WBYMile(jWW*! zeB<)TBd2{*lao_YQ*nIg?d|*H@BjSgUq5tV@?%fJ_eaC$9JLDu{`+ z29EjkdNq~t6DFw{H_-6XQbbYO+9Uxv~dstia8`y(3u}*`snbQ#j4{JWR>I<7Px1o zQX&__u8TsGi#r+v9$(zGYT>M(737uV6la^qpaw`>9OT#oq9aNgcqZWE>o)_3ec}A+ zi+3MBdu3#1VQpha6i<|OR$tdpl^&a#9v9$jX=X+o!^qJ?E*{~9sRwOnHI*fWxv9}% zf&PB1`J+kYm;n1xMHvcsXQd{@L`6n29>B0LlB0p?4ybk%1|f2(98mPc$Hv4E5g>Ll zCZ)1K*inhdhfx8ggXS+jE;g3EJ9>LuJcv3F3tvGVXgpJrlM?aXG5I$+9zoC)PPus| z;3BDeA!+x!bYS1+70VW@sm|E+GOr%KMO6hpN=`0m^|*8M%-NkA)-6z(Fjh%rXKXzM zN(SX(Nz`LI!z(8bY*{sXf`W|fSe0$LH5HXa6~@L(s%$O&jPCE-wSL~Taq_ZbV_k1QU}DowD4nZQs0n&J+bX8C2pKcc>6+1;`P|L5dV)7S-r_=iv60)0M}{ z%Z!qh<(YubU%T_@`70x6UsD69I3rGNSUG2^@;D`2a88=Lbl;gvx3nMYzIX-gfr{YH zJy2uK>P2(r%$vVp`NoZVwJzPbtNr+y?hD59h8~T=)R(7z*|wKw0_K^3$&}LG&NBhW zCnP|5#2xSd{PN*Ve_LHG&jg$v7v}Hn;qK;!&EM0LX96bAL)r#|2RsunuwD2N7!xSt zF=l&ELwQC@sF%I9se4vk4Sat}VJIx1TA*x`s;kaVjSO(H(bc|m)wa0iU**{3>MF9+ zLOtvaw6)Kj`1NT{C1xk+Db3F_0oM!SL)@%%pWeNG;p`cn37BUBMmiGi$8gR;B#@YT z(Qpc);v!_F=<M^{dHUE9V}>EiVc4+YBZtnhc5?Of@vCd7G>}=P=VWtaj)KytVIv?%3dHb{ zKPf*mHnXyGtZooo7`Obk!L=1?isN`D;Jn=Q#F&WS06#xpA0HoYZ|~|FM&wITcX*ss zITSJK_-MxR%QFEZ+=vJy!ENXlB5ia}Fxcn!IGzdE+Sbv%zpwkxzkPa(B2BfGWrewUsS$oIP7Zc9 zmKGLP)>b6vnSg0^W7QJI50_uKp+kuRgr6wKwh%XD1ZShQkK1gX37AX(r{C~Z>b5Qbgz;n)CW4%20+!?lq(_+DxprpFrajyD@7}a- z<&s5n)TV-{Yw}dp8J8R-6@IbF&iAfg*tuK%;DNncHZET>Z{}2F`>0G-oi_ijnYh6s zGQ?5$hUUSYdk?Db+JfQp)TXMcPMM-Qb=u~8&)UVFz8=<(uAbD`wte5$4eM7dT{3sZ zjA_%Rs!p9c_t?!x?HxQ5FqSN0ow9hjE zdpGxgZZFR$z~|8*L={ssRMPD3?SI>rlk8*b?A7_t|N7jX5gwUcR90D2FBHMBio1FT z-uF}_1lZWwxOe>RzkhFSs!e$vm6cynQ{ULs-qDT3-?oaZFiTSl3zx1p|M_PJG8HNX zS*iKe1vSDJNq?8PUXYXSV~V?Sr|!Pr{@z_u-GS3lO;t@Tku29$=M`kcg*anFw{Yp~ z8+iM%x2vbSue!Xkq`bJkQjk-bo*Crt<>q2;WbYyF>Fe(tXzgti3d$SG^Kn9sj7m(5 z@pks`urRW7^AdOT@=U-|kR5J|MTAQSoS>?b_IP*!z<2OWz?^;HHhvC!OTdVFo(Y&| z0*2?y98;bNn7~4$719_v;AyDC#WMlZj)~yicP2z6Y{0#EV^hgGg-5>FFWV*DxdMM; zE(p&Atntg@1t29;<(YtES($pANs$pox>Z+ORYn|cS()jq)P5>KJh{eVckt8T)e_5N zA*JbL=eD=7LdKLXi{xH{zB6z|$Tj9Jxu{MagOJjqBvM|u&BWY8 zZ@DOqokkmqQLf;dDAArQ-;q>OH3#R}q3_w#7nI_&6&jgImg=Yfp zMV$U^cWYUUhuNzKH?G|V?-pt#XJln&X5;hegp1xk@UFWtH#X4G_{F`eH(vOLMJ1p^ zW@Z-5XE({;e0<+ll$jQ6XZq;Q9RsgmOpuh4o{@nZrmkM9@AU4&+m6b-WKXN-5AGVc z2S&ywCM74QLJkh+UMdCkrvFV>by~Q)rS3yh_uwduPfAYDBstZAz*|JMsP1M#jHk7w zTTmnllq9F2(mUiGog}AnQhkUdHI;$~gB5`X6GWi^_-=|1Njvm{NV)}Z3_^F8vWEB; zf=pD1g8J}Gz!YqREg^aU${FC2{J+`{Y>?CemV;#Ulf?_MNCq1-EUW{h|BeZqX9DJ# zfOkAIb@1>D2;`FZv2+mE#TF|nytjC)oV<*jhOUK+kH0^70%NdQk(A_M@oY$Zuy*o< zb&t%Py?nt$92HMQhSI%_C2b1p^MXA?LPEnMKw_AjmXSpjCb;EG@ou(5;wrMJATKvJ zH!m;00GlKIpW^T&$39D#6JWfs|5C&LPFWs}pt=;#Lj^fP8>K#USND2c8L- zpxflK4BCZnzsVz^%X@&o|3%JxlPjG)hRLZ9 zQrTdL$=b3mt%yxhDF3UR%rGo1ciIq3>hCl$IU7o&NkZDl+2g+C=bhilNkmd{yNE9N zcqU-0=XxobaKN+L+lr%oT{4|bLakn0Ie7ZQ@#X6et8cye>o(VY4(aXp1#-+P=u3f!xMeEly=T9Cz zcHhjw!#_Bb*0m_r-OJPXv5wB8$Io8q>FK>Pe0t}JwTqW;5XrGJ8}o8sTU&TIT3OjR zI5|5xySM^A3I8ONn2zbPx}~XBke?nC5f&B^77`o?*9I)?jH{jHfVN{ltFJ1}$H@el z`|+{S(a|w6adGkS35gUyVCAg9yi-Xjge<)uH9%5R)6mjMj{T516?CXCFGX${#TK%- zM&1YMFT)RT{CO09AoM^k&jbvnV>+RT+iI$f?5gW=WOO`!aOIP3F+NckFT_fiRcdM~ z3(mH978GH0V~_fpO}g&Bb&ZIkP#6Wnc_v_<2{=E-7>EFnfQE(P zn8Hrj6dlB^UwJW@O0d2>y5bG!S;Xl zZlGh#vp}uCIsyZAi{gKYm2mzA=Pyu{j>?oc9lF`l)opR%hK`hzjQJCY0*NXFwVl0H zhN~2V8ug#mb$8KcM*a<+3WQQ+<#UUg#svSzv&OEReyg_wr$P45ucJ5JR#7YPP0>DV z6>?$blwopHmhHb($uj}-Ou%{urdDis&9`>DsXF`S{mU1x-M(}8&YgSrA3V{0Wnf}q zV^6zti>OUhk(d|kYPWMF7wX2~-FBRhg;0*=wYml*Ep84wu{Zo=fGw6eB0 zEs`2xVW}Xk(Y+MFpoXRv z1|ugUEA9lKvg>ny>zn2R$D5W8*at-Labdl=;lK~|_ICAs>}`3|;BRGV+1x@Cv(p>x z0YGB1Nco4)fPL~zz*)Hng5x9l)?g2?!;!<@*jQ68$j9f4Pgp94t)TEZNon(~swggE z@+NMZXA-*SqpAjk141Dg_E$vG@$J)*9kU`Oj)^G~wXB3*;bLj@M1lYyhd@YWLwF`& zo(WjnEU&n{vbs*l=$P!it6K{q8}dCIE|5P6r8(nVQ|z}7dK~SI$u9}j2l7QBs#FM zG@fSyCZG<~Tfmh4yB|!232K0Ae!>89`pql?6%;~2Ai7=n3iByuFHw@ZR0se7Nf}6h zPz0P?PtH3B6u`L`CIWcUdY%cGX9Avk9p!Vd`;dY|MOl$JVM9%I{EI8%5JC_~#FckU^IqJ64?o?k_;ra67iCK!WKf`GtL~Gu8i!57yi|xZi;##INlc)WE=_JOmCvIo z5U0$AoTx6TRn;^$qgIBXKu}&?NzXtTTqftlwe3AUovn>k1!*yn$;DL+AcblPC8Zpf zuMiNEzV7zMN&%{Q`}n633`>xQ_fPz#l@+4jK7V`*s@=xQg3N?aKTnSsK(LDPv$D9+ zJQMJTfxfQxrW!$NY)Alzj6p=~VCU@V?gk>^#^&EZB;4O47ByApCPf8D=rjo&)wbC&06=lzM+vR4pqpEKnIDq zxeheXF`)t89v&W^PA_y{(s+RFYa5%8UD4LoTwNweiw;MMzrUZ0zMg@hk+G=-77zR< zVG|W=Yy?kp%OwBE6dEp7{I$PLSTb7rZmJ}20>Fi)@V{L^46sYn zblC>G3aZu!%Z3j=BP}l2-CFPdm1FxitOQ{fhSh3j`7J}$uqfu-xM z|Kjdtt>Xu`t_BbC!bLn2aJav(kGGeX7wy!vpJOv-`#Ek0l5o$E5FZ&56aWi>^@|Oc za)xOmE-mGB9Gf=nzn}^sIXnfV45Jg)B65#V&!iLt%e2QA6;)MGT#9vJCI&yyo8ZUP zK~MxQgrww*NcVr3p>G|E!AU~tOtk;k5_-9&4tJsG%gUV!3X4e2T1{h#?=6i3`%dWi z3F>R$+5m#}oxCnjZ*u zHbiZGWKb0exXJ6Hcg~xoqAWXV=#b%H+Z{GS;c6Mx;VLTRp3O4>FIqZTQD*2*KcM}H z|Nl62*r{nBSy(>K6ByvZSppYO6^uJ zS)w+5vah&-Mt+65#;@nE z;41{xuOL-JZMKTClI+OgJQHwYA}l~$L?A4Lm*)?CAX)?2CaUDH)&)X7isaHgAG@;a z>m!;yj%SZp-9XIODJ9cm!2`$#$xyjJpxh27!4!0PCvK< z{H_Pj1k5f#5zYb8Z~vP#6c3*_0FF z=H&f4Iw~wQESlkab8_QZ=JK>vf`TR^8>lGDQNLOBZ}hQp@{H=DLP zG&sAx)M(vzgW3&2N>3YAwbzsRg5EeT51}yfHJePva_+9 zVL$8Wz;Zo6nLlL61Bnt5|f-ZGzw5c{c-<6P&;yg1p>CB*{WtU5OM^Ks{XS+ zB_v{vTkr_O;hv76YsiG~Ou*y}@JzsFTE{l8S}|KqdB)H33JP+v3p`s`5=nVEZ9i{P z4KE(twr2UNX+Nt?mj@HCg4{F^g_YuhNI_JTYuZHcfXm6^5en12s3` zMTI#IK7k?OQ86$-y>HAeJ^Jl9&Se|T*y7Avhsh+c_v_<37BUB=9z$D-Lo<>GAS_; zjR3doKnS~22Gc|FJCu>n&f=t0B7jP30XTq&7#(eRCSYc&2bsWmCg6s&Ku0UXduI;q z+rMw${!l<#sUbQzxqDH~$PP(URbfhqgYKPcmrfnnxpOzq1k5u5 z!*>ygs>(_-qlOKa!4YBl9(z|0B&qlV`rA<)6Jq>I`_R+}#lslhpef-@kZhsp?qSQQ!#}Iz~=z!i>Foh8DKYu5JMS ziQ57nTsyIK@eIY$!-o$YGHi^jg37F;k8}-8tQ=gBY|$>x(Y~ayZp}Qo5yLQi_$V2L z$xF`M)p>4UY+;XYxus2Hu61zz!s!a5MvMYS!DzX0(>Gkasr~eop}7?tYEg@rX9DK- z|5^Zt5SB!q4@(u~nSd+Gu>fHou#0r}_w-7H^;Ox??yd3d)4fIRu3PY`5Jh*%7R$voK$>5tnBoj4ceuMnWyS_egZJM)z&i!j=&gz#_ zgs8X(a7Uo2{ znd#iTdgADXi2R)FtgKA2qzi>K|Ml0V!M*5jO;+cRS8JSx9daWOu#Rk zC{NV~5MLyGhP}|edF%YL1uGXR%gK)!BRh8TDm`mEM`XLx0kfqw^WoL=8k^?KoHbc? zw9Lqnvg4<0)ipG?ad38FiAhZjR@csJY*;#LlEP@&F(XIHE2|xS{2Uz|T_{JUS=gL& zUt`w_HMJ@7vNC8Brmej4;F$r4hMXt|24T16*QeI6T{LszxUuqb^2)Q89KZAMxgqiO zLX3DBF75)=m#p zgLaP?Evq}WbQ&-`GO#XkKTlhHjAsIl1EF{X zKAi@nRp8zM{w7pT3Xd_6! z?PU9ZKFGvVhfqQRYe)?uE^MSL7&Jb=)kH;D;oJf$_@ zUx~q3Zis{8Qy}yP$qT=Vxh~X!lAxF!#K%xDD91*Dd^Rrok`4!z_(uR^$mE!x(lC(* z+uBqQNIB|6(%ArER><*KRN!xGlk^VsbcyQA1Q}W7jo<;VET_j8G$8IOY(e3I51-%n zikoZ7vJ!**6Dq4KQPY8(2 z-#@>5)74U24G?~yi<6^Q4kHrJ%7g+ow~9ai_4CJfeUhfyiUOdKU4XB*kI4oRd`dDb zc1v6LpMQLMH_+A8SXrEz80G5<0J^1n5`dsQ6L15PW?3P9u^9VGX+F;c%xP8;VJAud zq%^?N#6qck7}S1&Kq`jba+!xXR9BRd@=6T|larFNi75AwGcbb|269qC&djjfBXZm^MgqLF>N<#rlzhuKPb>E@PpbYN`on|K;=_3f?TDLk@O$fU_8a1N$>_ zDrtV`zerdZsQX2HbrgOjp*8149DpNa%}(g zcni(h_jb*lsytRkcH~Hz$(IZ22IXvceRIl%eVgXY8z(0-VhB8J*c!-tI+K1Oy`U@j`nqtraf>+7u_-#Wc}uJU-rv2r62MjxRtQb{kF zX99lu(g4{aNGYi(D^Gm7XWgD|0LV_}<_C`H#PS9_W?i7uA;6 z*B0evCdG#OyV_%YTbfza^oW+d>xz|tjx_V zEO;j1tW4q|0E)RAC}jc>iYOw2_!(_ZWdCyz!D4mxAR`V`*w^@Z#B%rzj6#Xo?zsPOctUU6ike5&(7Og4~Rh_~`JEpg?~=Uw{7q!boAc z0e{ktdlI;ka2(61Q$l=fY-~&{3U$Vl94iuXs{W5mefXV)xmg*ECJ2=}v7FIR_=ti} ztU3o`PblRJ)z8Yz%*?=2rsa$d*e@x%Mdg1u4nS7Rm#uY3X#!T)a_|t%1;}_NU}6a- zrWc+Gc>j*=Ti35xvT(ud=~GoFPX<-*^h;h6o(UKr#F|*kJDP{~@7=w7*N*Kw_8-4^ z`@s_w`Lwikq#Go1U0WK;vy)=O1AS3q#NERK|9N`*_yq(Jt2D%PIR^a)4VWM&EfKN% zD5~U*TD@c(NDfyQF?mAqf1EVj{v{LxW?e5I=~(0OEp+E+JJGQMvx2 z0u<0kDQy((PfsU|GV?$o#SC!YQKEvoJj&0@&CV9l16ad3x1FU3Ha5@&HwB2`;{)M{ z)y*)Jk)vkvkiks0Pcp1y^c^CqGc3s`+7ux4Pw`D6lTNg z_#a8X$$>xRnShTOA^-#H%QFElp0ncEeTSf!r1Y%B04tsAM^?|9GH&9+3p(wUukFt4 z*tLJ_`c*5o9Nu&C%=t?<&+<&bVCsQ~k7GV7vB}H>&jburf~6#H2;oX1FQWDp!Urj} zZyhLN(bUYn`rS`y2aJd5YW_~nyeQ)AWochB~+J zJ#~$PCkuo#0va;BCo(Y(WAy5OA zu(*xBcc8>@d5G@^A$&xbu)3&qQIwvQLnc3-4zO5EEIy4h1QXN3qgp~Fr+6MM6}GbJ zoI$ZhTD??HndE3v+1Fn*9K>QQ;_$!aw5sV15Ez%L{e#Fy+VCnV28m&=0QV(eAz~8j z)Qum5ZVG;rD+0d*zwjTEqYN|}9l%*P=Bz+Soj3`5Dm`GZhjfB}qyN$=m}B$r^q)!x zp<#mmQ~#+4S&#pr|F{5XNykAA?}Evj>5`HCyP+eDqN20IM$eG+pOaHmmgFQAON8R4 z3eR8n>w^|ZDkqoitGFjII3wNl@~&S@TEy)6Trn5O+31eWViQBdgA)}d%y=%Q=L1Rr zEw1Bcmq>aF-0tq$G)om#IMm`>3FgezCnWu+ufEH|K2&ST%oz%@vOE(o&jifQ0f0Z% zkS=`RNH@ti40iZl#;s}+lWN!DzTw+==q#t#5aT#NBzHJT)PB`}rpHXdVVh{@!X&i9 z2Vej88=E5QL7+IPoU=Mm1s0}sQZ^QKHtl#`cV>KB&+!pMY#Om}dgUCQrL3 z7mt7%^Gv`LK5B1nu8b0R+Qg??KRk1M`@WZ6fsL$U9E>RBExED5A@(+Ume#qi^{+k9 z*l6%rC#ecy9D<)ALR=YVr+4Fli=(ljwdp;BXV)(rd*p0m8ix9}sNvq$-jEe#ckz<8 zrJtRN&b12OjW7l}TWk2KWxZ8@-K?ed$hJQJ{~ ziMg9kP)KWAQ(>^9eo&;F)w!4MR+lxltysQB>*Bc+w~TF^y#gQ?CIvVdnFhMtKX&oP zH4TluJGX99zod2eim{cmS0I>t+k}NY6EHdK;LTz%$5NF51LD?{it zF!?txgyNm(0sl9fP0;u+a?XpO-fdME8kwIU9ZC}J6ai-ff9{UbmT+=z4Bf+$gV5HD zBT{>JOY+WGF_W`%2}JOr>FibXIB`VHo$k8iJAvw78`6UQ!EIJ1DUaisfO#fh9D}h{QUC-R$}<5MmE!sq8+==P$Lsy7 z^7HndnmT6Ms?`e*FF=T3z50?BFH=&|vvTs<+jB3?88>X)_!ax+&R;%GZHe4ZL&l!D zHF4A&-|+D0#1u(`*Dj@@vt{I7ZIBx_Okv!AppBZdMq$KKkHFyI&`5Es`Rt)T7_Lw~ zE%W1F{-QE#+)pFND~*vG`QvmWCr^T?bmVUuav|^VWR)NOW5~>XJ0=bvK3-l%>E{s& zIuP^EdN=8hSLO8ck;G-CckBMVzs&vvmWe66C5?!u9O88&5> z@=wEtppgI2(LYa{!!rT%Ou$I^#ct1S(QK#XDkW1!A4=4-yd^jTBw<8va0Xi25W5_d zQ@$`aT+%bp*WFT4T`VXAs}f?L+;C(Avf(`+K71UI2y5z!3SUR3) zKY#1(>lF*@ODihk13W^KauFIY!3`%ejUn#+uYZ04i&b-To3ODYCoK!;%*Yg03%;OG z0D#5k|9)3jSW(wl*8sk!rmCXM*f2lGn3S}POekn;YsViSYRUw8rQjiJX>F=(5!J-S zB!z`Upv*M-v~{)w7d029$Gnb9Eo&Bch-#bK>(esg{S6%fBZ*7wYB;{#CB)In%na%j zmfO@V5fuv?s#3jNjXgrcBO-VvV1V2yazwiaMZh}<($m}f_T&3^Ez!1Sw7B4+kynTw ztVz0AhcBOc6KqUaaGIRL?>dO7L;r^l0|l1)SaO8YL4zy9{e~;q31W+`f!_L8*T`f2 ziqfH86eSk}n2$KHu>8^?@^^{sm<=WmlsTL9R@<7&3=WtwIbj6YKsK1(44SC1Dw}5l zrh^5(I($3LAc(AMrn`&p^&b!m`evIND~b}m0zG|V1r5!Rf7O2^=0Pm1YN$*L_Y1T& zy?W0gtdtJdT%vy&hPU-}Hslu-rGy1KxVhZZI&sY?Fsl$m58y8>DZ%Vrp9b1X^HU12%8xIXwvCuD@c zak_1+Nx!=OIXf}qZ|pbs7Z(1;BCjsZ$jE_cut~de0d`78ttb`bA)3x2;an&B8-|kt z;QEl#3fU{){Xl4)q$DP?ES?D%=Dj}NS@*@`%O{WS+Out*s`9iQ`ic2Dc?AN}e^`&! z>NL;icg`L=dg{>O9cx#uo;zda7W2epY~IBD>0FP*JA zamr=i@MxY1m}df(dJQ^#b+aUVm4^a6Ibc;d7o0S+5if~68)8`MaX=$o& zT(xAuJTUbxUc7AWFAq&j%u!vVqB6dngH?Lkif9~A*3l}X}xpwaZR1yS@ zZ&^i=m$mV$2e&Sr-n(hV;)NK$V9~PG>oxB_c=DVP{Z=Ge8tZG{I>$2srzI!EMMp)1 z_ItG>N>GT6;+cSXCg9-L;bAxgRTSO2W8?m4^;E^-KM@u0 zk7z#)89GAE#@E{yFocS1t-U8MZeKH5eiVuS!nzC{I#NZ)-qxWr1z6PLmJ{lNza-*N`Sv-Bx6jimw8}=MJa_m>F3zx6n zARu;LUOuJtXJy5E89%&zPgh^>(cPOjZr-|m`|kZbu0vinQvQ*Dn35bH8yVnkZ((Ba z>g7v4y_b0m%FV`SqI5uVQUWrJBLcl$9qepTh{XzAWHd#74$=}aMFuK=#>d4*M}~NN zxVyQ!I13878S;>jn3a{CnT8IDJQFY?#9g?kL}0hC4_v#{-hGf%)s)tCzkAo;N9@H& zCZNc3e?MgXef@81oG(?_;o`Y7rY^XZ)HA^3 zh)Rnv9O4E$&4Y*5tY0`wZN}s&6Q>@pc!Sx2)D||<nR;$YiHlk$hf35=oAwpg_manraWaN zGqU^_j2#bXg*mGon-c;5sr9b>Jr8#)Ba!G%i`I!@2)~ud4M@3CV z2{fGYi+$Pv(xxacQPH6yj z(E7nfVh-R-cNlb&0C_j`5(>`Ll{U10(SJlIk+=lCMxGLlruLnMpr|P17*BaDWLVHJ z2b)CtZb^=}NLo+tlJ%gpDej-s-bVBDOu#%7@Ph+0X3w8IVZwxQV^tRHJa_k*k(Gn1 zyQeSZ-t}~McDMQ*Upsr&^l5X~9Joj%AC$uH4#scD;j2LlI-7EWTphi`!-KqBUEDo= z{DY9f9~F(Oeq8oLj$Ew{B;VDQp*0mHsIQfq z3#Lu@)Y5&6RD_g=l2?%aM^);n$ZI~&1WZ>LbV(1(gsY6f-wbR)4#GEl(ASF&c)sXQ z_KI#z*l;!j84hY>L>O!XzeLU4$ywzLQ36~ zj>pp^ef?c+wVAZs`wtxCnSgmFV4evWaEz}^ps*P&4JGMO{w~g; zWt^=B$R7usLH+OSlGGI>g*d#pe*U(9vxFQW<_1aipJxK*nSiZc+`E25>+m6sQ`a9p zdkGo?J4Y8cpo18Mu)QS6&eTx<(Iu_3*PrPd5Xl&L5M12|Iz$^^TdNSI;fpgP1ATqG zy*xZT(Y$Sii$q8&0|gEh`p==8 z-Gqe1coY(hCENfINQ65AEC5AB;dvD1A(EGtiravML=JX9*a5040c;#s{*y?)vNF?C zl9`+o1$z;Aco=Sol?M{Qd&$kt#QMiZ_-`8h!!TI7wg1}=G&i^7bK=4MU)q@)|Nq!~ z>&K{)ZEg6TGlLVN$e_V(aCaEoA|#N6gaE-Uf#47bad&rj;_lvYmyUK^(9v_|$oIbY zdsgiZaPIy7fbWN0GjyO=?b@B)wW?Mvd7iN=Nys(I2c-5Na<;>doeRVrFgz6@;}(#j z#;3EjwY{rvkO6_v^*j@BNiiO2U!}0R(@)?Hj(U5F@=&J0x4E^Gn3cLTt^inEg5Mn^|QMurCiNjQ{U4CO>+K0rgD zy#NW$;b6uRVq#+6k`BUBq$EJ5|CIM$UYwtUSV2ZgVp1~cAUaUy5cHpC0>;BjD`>`Q z3HJyw{qjt}ln_7iw}1Zo-+%u+*ioAo>tpid#^v*B+Mcnoz_Ci|>7|7F-~asA-+%uk zYN;-Wax%DeQA^{zj(bEzWOP)th=FMS{QGY|e;8`6DoqWrc&u|C?S@+*ijX2AM1-L^ z^5NH?KMi)(39@6{UftvwD16`|LT5YbG|%?mqrOVGO#5kfWjA z6-@-!o|@Z2?iUb*Jp&L-??y)ls&mrrZnuY_!R!RUhrs!a8Xgp}bMmh}pjm98p$;U+zRsF>M<+vt zT$8{b5zECEI34?G%O|TGJ`^J2Wu#zigU3y%kr$C6B(VXQyqflZuJ!dbg9$WE(AEtE zE%J`z`kF8Dp1wR&?PL45?$dhiUDYMz0VYoqjFN^xRQ0bkY@r8 zAe_r6X6AK9dt`(Wtdx|L?A$dx6R?{H(7vHR-0q92l!n@h(!$L6D9`|~ zh8;XuEQeAs;~K_!US2hkE@`PjB2aC(|1m3O{XI zF-vBeDo;_W9+7wAi$!*bvz>&?&An1jz zPQQTSPDj17M>ou!AuBC0Rce~lDwnjRgv5k+7??mq^o=g+Eq#1?_bO0jOG!vbNy;uY z3J(pBh>V0A1Sd#8zkHqv_>Ja)LnluuUAcT->&lJ0`cIx2yf!of%^RiULqS{Xb5n8( z(n4LWOpFYThzHrm&fbCL^F!P!Y^tws0ET~7LS(SNkB>JJY^YJfKcVd*tG}_PuBNQ4 zBtIi5E+#56B0MZKBq$JuC^eY(ElrqGV1F$w&dW?oMwSjO50OkxD-5a%2vrc|b{GRI z1k3f|nSeQ_U;2iy#Nu1P7sWFH^Gv{RUYJ2DPTFMJDl44^cL%qR(X7A|if(@R@c_v_HA8;hQbUYxAZJr63 zws)QhxUs3RDLSvOvo1d=$j#F5h29NKRTY&pXHM`;z#g7BAk@)O7jT*|zH2MVFwf0M zd>a)R9vT`F9E1Zx9S+j49O%3Y?hi7|3v=PRi(_8Eh=_2Qnh5M^D~m)Mj{E&&CS)t$-%|b z$A1Jx94PA$HP=-Lii`8o(-NX1LIeGMyrYjBu5XS1JxOzXauerA*lY!;Nt@ZGnLwb>XQXP5Ca(=L{W&Ss|_^1RUiTaPZ-gJ zWo7pO{~u|JxSUe(DHaF>csPHE<(JBKkkrOA0dp>XR#PKhMzG$nPyu5KXQ_eWJ~-@+ zc`On12-F&5YD@h{pOKRbgQ1g9eY?B+XzfIoFTIeKgvFBbrs%eP zgP>ob4y-Z#=Q5W%+FPmi2vrF5$xwvU&#DY)7|#T}cjvA> z+qZ7rwq?ui<7!v#JbYnj#%43m1k4%l(09ruq+~%#C!!!bLj0taOr=O6>1068cn+tG z$>Cr9(gUjuB@u7~;QFsjzTzRo9lj1gAQB2nI4Hxmj~Fl$iB*@92`P#Oe?VndvLVJg{QpM(c_v_<2^fWT$p1yp83Lk5D52=tif~r)p{SjUZvV@xIDvEP6 zl99YeErDkOCX{X>4;!=MVF=Lio=){V6EK$B))r(o^Gv|xW&in8cUf*$xRd2mJv|e@ z@Ytl(w2bVW9Mmuk43mrfC^yvdj=GV-5Z-IGIq#Kx6Psz_6!qn7}EWrIzAo z!HyoJ_N?I~!u4Qs>cu=P;v+u&pz2#10@qV%;x|m+W1YATxDCSa@}Y<_XE3+xoVceL+zvDgFLl)_GUJH^uzk%MMYd6NFJ<#)ULx>49kaspf8 zW-sCu15t+h1{e3PS*qXO)rMFw%L^A$gpva{>hb;Q@X1BfBxPpkceS;5qNKCF0be4U z{D@}^`5)W4bmjbcvNBTg@nAY>g!_dcaG^e6;sx`}K{eO?i{%$imzI{(sQ`ru&jjr1 z?&TXu&mRURwiy7*2p(^lJ7cDd%(1)AEgfB4J$!;9NRC($lcT;Y{n`E{^LE^QX<_f| z>KPCglK}Zx2hP1Mi1H5$4hRBBP%N07(z%U`dC;I;T^&%Jrn*WL;pFDzJFh(Z-~qE_3EJ8Q%M1C@IU1Y*8<{}W^&@8Xz82lsTKq)1D#_~v_NCP$B*2^ zN!+8QydOl}=}EI4nr-o{Gxuo7hsd^avhHEtSUtk=fmU&JI~&yaN(Nq^P0l+SyRqGBDhg6XHhP-oI>bP>{XBIm4LPcziddzUKOOjjbQ+mjpV#R5`ux z;NCr#Vj~=Fuj@rbzs2|8nBw%nI?dnCH!sl1R8#f%p*?4{%)K0~?pcI}M`J!#$9oxP zrF+|$$M`y$>1gfQwo6O%@++PRI14SC%grO*#<7K^5u>&Y5Fq(^T!Y@y+DX;D*ma0O zi!zbXg#n%A=Rru;I2z9c+>9fmtD4f*=Yu_@{j`WSvh|^@ttvd<=4Dun*_{)LJN6lP z2R5~$=97{uu|9N{Ciwf9m>JmF7o}TXeyX&|NdIm^MRf(Z2&<}*@6eQJ@>usVuG`8+l zRo-#yvCeI;5ZKqTq8?#JSY~^oi_0tB3&)>$8S5NSQQ5LrQS+*UcR*-lG}hF{)bJdh z3HbGOt?L)n)ipI#�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 z zi_moXRwFlu^DE>Pr%XVEpIRVzCSV5#M@J_YSGRg-3@I4Mt$8M3E)2j`8B$u3!UZh! zM~8SWNcedQ&jidf0oyyedV^u$AOH5thkkJf;J3?)inC&ZJltH+G-!kKw;jpfe?$Xw zuc*DDwxYN!FFiUmfKCVw_73(qBKVDrjC}m{=aHWF#=6S#l7h^X*vK$HPd7JLtl{G6 z<3Bty!ZQIQwvOmJ#pwy%lDL4#m1mAPhhR?Hia#_6pC5yB3c%#c1^+2Uzzv#0V&)UH z^Gv`_mr1o7bk zZjLr4?_NK?d+WyKOP4g%FI~Fv=%uL@D%Shj%5&oV-JC2fP2Ye9?2h)WYnQKFxpwEr zm&TU1lmX>SITu@eCf>hSSd%O7?0j16=VNOxuM^i{pLx%UHi=1$zfTzd{q(U{jD*L zH-Evx#Y>jIj_b|hnSgmFU~;>m%v5$xp*n1QFwW%QvJQHvXL4fj1 zz@*z!7lDfeexd(k+AW>HwHJP=|6~EkLX1uLe@6d#CSY?r@rMtcX~y@TdL`x-3##jy zTHArEiry%J@5?*qZx~pM-+$_giwJn};ITtMYHmq+4N#aGYim;7Ozx?o6Tn<7dEcAr z7oY59Yx2s*-zFe7Ij6E9J;c()=*CI4E00Z4!QYef&N(e3KGfIM-7hXII?~_6#Qcrk z9Sx03x1XAcBz?{G`RO^?#ojK#R*p7)ZkDfI42=-TzjFP$&TGqVv}XycBMYK~%p8K9 zjo(?@-oNumPxrF=)mu028(7!^rm?5DyQwhL@pYKpOJlpYPqnW;)H!$N<{fQaLvw38 zAcDf;@l3$jw<+GnPWQC414)mi;pilf*SX>yXeOQh+0M^10rO12JQFa_1WbNCwr{Eb z$-)4f$IrHNo(Y&dYMu$0X96ZP0t}uBnCt>e0?-$p2^jW)AW;zLuXtx>c4oHx%*C%I z)D6*wAPbI3WIl*CVzAWf$D@0f&6k&xU6RsGWzHNvA-q?1dB2Tw9T5uHXZ@NC{T({Bsq%?Q;B+8OWKnib7%5Q zz%0#2jW`WZS2m!tOlw(_4h{-vv%yKAv61%wF9(3H=p2DkelYbS%lD;>`#X@**$j@! zm%aAce_(+^0vX~~z~SR_2|vZAFjy=R??G=sSw`;FFTc`?JQHw5S#eg1ZF^S2= zz2&D~`{=DxKo{@y=`srE?3_Kp(i;YtEiodLJ))j!6EmI(7{LVCkk+QUD&WiK<>uz) z<%0)C>Kk(&vWT_0v=Yca3cxVnJ%90tQy2`ohhsL`cS6JdtA5}ohBQcVctXAZlc^UH zF3is2=cJzhMb4H&|0{$%6L8+ej9$js@cH;cC`diM&w5y@2 zrGKb9H^iZ;)4{TNa8SZNOP&c>EUAkPOwIJSd3o{V1s6NRCp)(8-L>t4`a|EyxWv>9 znC!+l_xv<>^A`sW>AbkFe`)Q;P3u>vJ$w1!RajhN3U=nAP+NCztEbnt1_oK)I=OA< zp?ypD1O?d}UNnk{OMv>6`C91fm{>p6FAa1wP&>8vz@FXL;vyVvwI4-9$Kt7OOm)(; zPWQL-%@1@k(>#Cd;O+}r7G92458q*jhkn&0c$s8pc-vUS_&S>3(AvFqrnY%Ix~YS zOTwIA@7a0h*o8HeFB1EA8L|$Cg769cW<0+-n@QmYKCC}0t7op zXIJz{Fxs-N?vAFiqKuf3z`!7XZx2@&S66pWbYQ{J~oxrHnldjpoXTst~@UZ#kz?g3e3wdEbi{^ z{p06`YC&-oc*wfC+l5`74ate=Q4yd4O~)KjUsrf}M|nKIg__@ z+dNmI$8!B72GmAX1xx+6x1%hd)%YT^hL$vTbyEX6(JJv*{LEHiVYwExtRYfLl>pQP zP)SjI3>MbM`%(oUWT+98>NhP+zZ5C>f2aSDOQjSm;r?M?>OWo1{>eNOFwX?cJ>Phk zy4oZid3o;lFI=RDhkLp>410Rk=p~*BxB)0Q6#_)t|1zM>P%p0G9`1fQr{6RMY7Q9& z`o2Gx`Bbo%D4Z`9(zTFMH$L?MRp0=Kfr@;1%0OJ?dW2vkaNuA)EI0sC3XAO4pZd?3 zP^kb|SPMDF#mZH-c9CKGQvYdv5z4qkZ9+k5c|)fdKtN-1+QJFx}xP z`wkh+T@)W}q~)>0(^DZ92HQHD+|f9DcK3!wYtA{8Q71C>G-LlKadV2h;hPuQmo6MV zesIFmGtS;z#$CsZk%-SFg7>U`o6uTu}necTXdB$Idc%nedL*d znW|!U2c&NemqDQWN4qP10!)udL0}(h2_=urrF76P-cH)Z#7HsM)v}G9n?RX+=rBGoSZ@W;Cml_iu==rd^j%d>BtJ&jidf0b`emcIBCX%LF-IPp+Oha^%plvp-zBar4dtUHzxe zUvYaWwo{_)%#QZ7GBL5Tvotn*f$_@VwGlyWQ7J(g_(HMW=47TO$A$TOINRG=TUl9I z+i+N1*o|?0AyI**CMU$lLS1H2h0lcv^Y z!Yv|*P`txure9%0Yh~a=HPw?m6Y$sHeErQg<0ej3%}h;7tgfi6skO224mUcvX6E#9 z6Tbc$VvKR)CvOUmjgG=0sJ8s!BM0y2+ZHHH{AS$O2=LRKapNZ~aR~GeEU&1lEx2+* zIlFz=+GR@?&z(1WfqLyI+Imrg)YdvW zIx;*gcGSLh;=umR8<%~zWR8l;>{;7Fd+Dr)Q#;s8M@F+PbTm~Det+n@WgFJbn>%mL zoY`yBIw^|VP>=OT2Y4pnzMj7J!U!)n|Ja21sK}^k$5Z{N0Rg~}2YMFjF3h8=@$)ks7H*MXp z5KKTyw7!y(;+nwN_@vZ~tbu`n!4aDuuI)IyZqbs(ip2D(q^P7Ucf>CwCLt*`jVPFT zCSa%l9(&4h!)#+vq?O9?Dl5m7gULyyD80w>Yy`zsTl1xy?HDM+#}X{iM-RYf<)ig! zN0F|8Kp-pQ=b3=}8CNVv*geqSOZEq8hd%akba&zTMe7zDBk$k8A04C{#&RB%dg$(8 zehZ05KZ=M6ymxf8k278*?`k8r24nbx&`y2F=G7bbYiA4&6DE;_fH`<3V4ew>n96An zB`7RBF{HidBVg%w9AQbs1|B=wQZa!gK_JNAR;)(%oKPoEGWl<`GlZPr8ZR6VC)(E+9+QGt@IOD2Q-=`%LfJxf91v zsh&{PGR?`$g9A`NauKlFnu>#M4WB-|eD>I}6DN+Jylj+$BGSyPY$g|VHdGh*n?Kdn z{^9fyV){ID#V9s0B`qy2o#f)S`l`Zsm)8$(Ur{@8;Na2YC(d0r4voN!RK_&g(^65A z8D?*A_p18YV<7T9a{T-a3*VrysJO&re0Y+cj_`CobHv@xX-0 z*m%kv_DFapU`npSt6-tCatbFF6(S&*n@4Fq3_|Z>2SSwnR5OTb3JM}F!AYsM2Mz-w zY{bBWs2gH<2=9Rk3>Jwc2;s|PU`yowLYYIBGW?uec5={w5;vlrMR?5(A8 zPDqLh-6cDT@fSI##8i$zGq?!^H%iOm3cLsju|oFrar%$z35|42|9K|h@`M1M3Ha`1 z)k8;4p3%~I{stw?pgQX6qS}<6maN!d*SC6i?_E1{?9`b{cOJhqerE@!5Xh-UQPNWt z;cfRu@BV`;=gz8W-hXQF7BmLLBtjJqR3#&>40EzHGkJdd%C-9jCZ^_=AR2K2(GPCBbK5+St?z7h>=C(i?>jV>8SEu8i^(*Gj zQl6opFn8&?BUkS0J$?1Y-?5OL94<0fMQLh)hl`uLn~MwDd|X`JJv`y?x3S8dmbTW$1|aX2WGBZ)M@L0Qh6e|O zN}XLybx-K)ZG`?;Rg{4aiL!Xfi7_!Tv84Yv4U9zlZoaa- zjG~3Alo0~IU ztMA%(^tkFxo(Z^|>;n;j7vyorZ91m&Ou*NU?B2OXWu}s%%8{gI3Y6fmMezrOsN!EZ znccahx_|5Pnex+RlvEBDHPqs?Rv}>Wo;pX{AoIs3kM3HvP+3l9n%vB}MomH@DkhW| z&{Srax#fG^JG5`pih1&~(}BdJe73B*ybKW<$Zutspyf+Ef`CWu6K6*$cxrM#j?S z2yBk6^?9keh3PyKFjvNp&4~@xQb275Tw?*(nL|vUoDmUlBJL}tEa70_kU|*5+yY<9 zDFTTgCQ1nrql9wN3{H-678>*oVGu+psRWR%z`YDN1`RA|U&IGRG~y^mWW5I5_0-k? zIWA_VzKLoIaZqAKgtRIXb5m#nH9=u>CiNARj>)M{5`C0#l?cTN?}2{=NE4YH3s4&- zAmh8+o12>3+FP3H(0{?00O@xI0>4PqGxUD2zq7eokegrA3hn2afc-%98`9F+F76)q z^{3$;acgCHep+len0|RC;Ec4?l%#}&`1mFuvt!WXhY&uV%i3zVg+TJiN=FzV5pr$< zr*YgIvNxVy)@r#d&()zx*AOA62-l%0i7j%Nbq znShz*@=U;-lfw3YY|ylUQ_Jz^{h#9};yM;;U?{QwgBgkQ51`;3aJ31(m+H4T|DdcC zJ(W`Z?`$he4Dbm~ zNr7hqCM(5m1#T|Nu=~W#g50#oU{6PT@6$}&?DqeHx{%wN5H zW$2dEE@6Ndh*scoo(Z_IG%GI1-NVbr2`RWo+FI%t)y|wgb5`w^i8W}##NAD$nbBdc zj)3+xeWIgtEnP-kkk`bhtA-%**-2mKyw57t#Xb@K4{+t;sO_1(t(y3wtDVk*o)M3ck< z(`#ps96fRP`+fU%tzWx#&FYP3Z3+-3plE4N-$1s(HLY`}PN<$zJ+g1_rtelSTfAuD zvON#Ib82AZd;3e@>OHsyg;70y=J=j%>(_s`V#%Tfic4nH`5T@Im>y?oF%d=lcqZTuVj^j+eBxY6?h*VW`bvZRB|6I>AKKNTztbj> zxlddJ7Q{sQK%N#sriq86)hq)z_lj#9f*fPa8Iuf0y>a#BnSkGa_%zf%I51pa(^^?m z(JT}c3Ul(p2*$$N+}TG0PUF7!-9w@_K}}0d2{0?-;?vR+{oQ?hY|Nd!{3N|Y!y_Ml z8tf7lbynpUmFK3#N2kO&+WGs~Sc1*O54^`{(;6Ng#HZGjUs+z1o*3@t>>cWEWAEna z7Z{9Bqi={tkEo-mwjwvg)zvE~BEZen%P%lAGKOaYrbkxlVsJpB&-vMI z&z@2bfU}2hAS=*khQFhwIzJ`M)5FWt!_CdZ+b=LAJTf|#P;Oaxive6VH`J6B=5Qst zsL}-vKMF7~0e{$m5}PZ)JIhgAgX+7SYz#u!MBd} z5DQ9RBQYr{3DBTDzm=Jp1>+zcEOv*T6Y2{SLHb`v^n*nTp)0hX=a6SG(9bd@FvBue4x&#(N?OVrZ zCP5a->l0-Upx2t5aQan5cmr-xNc#Y1F+61P%t!Fa;d|IZU3{qOXj zVGF^VBI*C2|D0{=X#Y?859pz;95eu8n`iOP4qycQssCgk`g$FV%t-$^IT=Kz|AY(D z(49lfbjOkvbLS|?%g#^eKtDI~Jit?~Cg-CE z4vYAJy6e70^A{+|$tm8b1mrr;1WY`7fpq`jU}BT$>g*Og-K?TKLtg%<&Pz*27grCT zpa_z)d=2C{L1({7aV>=;3gyGT5o@w)E|IXM`#fJEF$g%JJSJQJ`{W?p_?UcOjV zkr3#S=l(9z?#-Pumv5?X+I8;q0iBok9!IBU%__x=vthQd%aV{P0Y`$*z6@W)E)v_~_o<2Y0UgaP`Ke3l|?-x%dQ!N78fMndR;0 zYw_aQv*#}i-WVAf8JoR&^wQqLFEEVccrsgyi(>6YZ`fPo+* z(LIC0AgbM`*kV>zRyIZs7hqt~MV<**EH3AnfcKo|nSifrp1JbG(9GJ=y<60l9qwV{ z5aj&hInB!&hmIUNw14-BtLn!uJvK12cJhFJHVd-7!o%KZYiVEA(9pbe{@i)>i>Fm@ zJ~y^m3wDnVx z5r2_%H&HNupflzBq#ouUFn0kWMCl}W3%9d6UHt<#Hy=Dh0FoRS76^os-dQVX>>H{x z+o}-OYGTkd(2pcPRZP*l;>a%)zP4#-O$~mrOlixaheJ36(CWy?&@U~jZ4?A%>YuZV zxVd!R1lf5TPu&)_p-xkX{wh>vwwmtWX>Fjla-x!j-clu5nb`-HPPHkoLW5&PRkcXm z8+&TL+^Q3r3#KjHx^3;bHS#kT?>fDH^V`hK91x9)#YMMQC{IwHvH9f6)ti);te5>} zoYK{Yv!|>GjE+u7%j{|KJE}N-`E*(1-LexV$SeN~#*`J? znHe(UR$RXG<4Y4OJZt$@7bl<9p7}517R+3*W!JXVOV@q(?Uac-Z#;bQ*4!4ZDbEBv z=A={HjI^4i5qKtGo(b5_*0zJe?6K1u1xgTRWd%1s{|2x|o(VX=h+=MFoBlkwvo1O8 zt*ty0aBU6l4s5Wvy|uPH%`en9AW6`I_?VQ47_I!2V-$(o>RN=^(Lte(mUs1RqN<1o zoNM&2#^s{HzLt{m^314E7cURJD;oFAL-RqLRE98GWhGYc|K+{7sw6WeK0Pwb+4QaD zv&YX2gR*mS^9qVe%5eFIUm7Bv{KBKLou+0)`PskKzkB7PZ49B{=j3+-ceEkM+r!y6 z91)}B81IBwe?8q75AMH3d?_gJ~$;SC(bt_CFrHmiyMdTc=`oL z#`b1y-fV2Dd*`g6j0RTt`{X}6kF3-<10rO12i}xD6 zF|qMa$uBN0%*{$miuE&p@$%9c&FyRF%$#w>{M|v#Csxkkh1GSG?y0M(6clF%np{3{ z>4(kBm1LGYHnw&Nk54J)nSjT9SWrK(p75rIqPzPyPMk#HYAMk*n?kid7%gbCOWL`7 zqB5O2F>s`Xm_p+uMlnze4uD?!Hov0tDI1?lby$@R6u7>ovLLURHhyjb)#`Dv5fGeBWmtk@Fc}kfp|G~| z*WW&U0M%}*urx0)h)mO{_Cfo-VceIK^B`59^m8Y6$d$>R={D1 zG5T-6|MCe`yxlD|1<7#{0p6bO9!Zrv6R?Lnc#2y)Mt}VoH@CN|t+qHL4m`$QZf>rw zuJ(40&aQQkclG=Xnq*M*HdPg-Mu++Pc!A>5&DGk<+SZ=ro#MfdA4d8lZ4FgamIO@g%QJ&nPNMuzsm~ z<*e#KEdSki-(kv@6TXoV5oBI#5|NN{vwf<46_;;aPtx!33mkgqVj{VX9C8Pj;9?aLka`PGXa+}{Vvth ze{}i8?uAOz#*h1Y+&E(LoiKTIS|wnpGPiZ^&)j{UsBK*!H*ws!abNLFz+ZhcVal{Qsv4JX-vgTsT473Vty{lHW%dl& zN#EcOjT<*<+H|?ir`3MAaYsOCc4dMrwI$0{W+}={o`@R7NmHgwms@=B_rS=w@c!i&-TeG2l_00^!6y!Wd$sk9ETFOu)Dq zM6ZZq1%z}u`}@ggVT^p}CMM9Hkr5a$;$NkxPglnPM=?75Ug)5?XZyOXhwo($y&oVs z*ai`@fgCBX(ZQH2`wnhiykzdI84GVG_6`v&q*E|%^Q343oqo9Ce@x-T%3KdiUqv!*HdDeehicwa|m^kUv#tQ5Mg!yfsbW)CSbbZ-QDC4;O5Z{qzo}$q+%ZWEBANE`+Dg^>c>rHcbR68 zm?n$Z_0Y}%jDAezmY`ZAsw(2vhk`*)Iy}fT0V~SM%E~Dz$s8{%E&^9%5pK>%x4p$% z_1!zSFIzYpJm9i2a`N)>i~N(5Q&ZD3As_BBd#tCnVe`8Ab7snehFne_WaKMc0zxCA z;}daHheoY0UE6+WEue=KloS*(ML|(!uf3~pNJMlT&jidf0aFr#bdgo@ao@+%CTQIF zJjN0oFaT5+O%@kPkufpLf>5%BS!xtsaTb}HkjLu(up`5Cz)~>N@_*TT%kZj_tZjIv zji!+(G>yA9?k>S4gd`9Gf#6PnK(GKI?ykf=Bq8qZC+;ps;tAf+J>5NXJ^pF9 zzlydUHYYk8Q+6cJ1S~1@b1;8!QR}dV%B~%I_8+}rYU|?Z9~>GUiFrm-Pn>0EpnKuu z(W68Z3bCE@GoJuT;%D;^9YlF92KVpjUC}(Ob?J#6>W>&oNO%NhGHq$t`kPUgUz8ac zgxvoSP_RUR#xE*5hGs8KYBI^|hzS%Z0z~tPMkpeQTx|`N%yUeDU;-r!fh>^xW`K%0 z6)J)(6=qnFRl$5~JQFZfpVjexVgki8Xm2ddhz@jjdtOeqIT`PSLoAK2qRL-VUzi-` z{N&0RoxoNJlT)yjTa~&72m8eJ1t}4(PcCYmI%D62npAdu6P}+))cNM!t3FXRVu!B! z=hW2HwcU}$Lh>d~{_?|zUk2Jrk|F~g^t2DF96o+AS;Vru8XHj}BI!^oTfvbs#zP`cq>zIqK)(dW4sRyPl2(+~CaYhxVg@ z9wSf=Bqbj}k)@wbb`*z&j(rtA6b5!y`RU7HkPdviI&7&t6EM#NY-Z!+<^`%T>=`^0 zuoU?N1OZSgo(Z_TqnT`L2A0TS7lPsYa%?xi$B;+U}LJW=)Zk8I3k>`l^d}^o=cT9bG9o9(zvfi{l&C zEuIacQ#o0=@pG1H+|YY$VrgUV2r+W#vE>9GT(N4=+_`h-FIc*Mlj`-`51yJ@Slih{ z$TI;0GKkPaiRq6qT){Df#xnsUh{Ks5k`AvM*R**i;29I-MvnYpq>S8z`8Q3?EUfGt z;UqzsR`7jYEv*ByCQcd)?H)c-Mq$#j>yHdg%*-vITrJIQSvOB>>{>N#{ExCDhJXM4 zNSU#d)?c~x;Hj~hIYXRkX|gm;Fe|6 zk;5|@tBdSU)0Z4MbNR--$A+c^#so6a_Q>NKSIwI?eypOLoWi8}%l2!Zzpe`^UqjfU zaP#0>2CJ=Ivv}UT1wSubv1!x3ljpDA)V;6&=m}$aLyxBBw5P|nZr`_Ga+maZ_cOt8F2S0P$q zV4@I}_|OtWVk(+K<^@}$7+o>(d=tnqmLMi4F>FAt0~>*y=L{#EG37Ejy)f`ZSV5c} zL`zANA5kj_6y+u$L5}#m=I4>o&KwWT2ZYJ zF%W=2EEfTS%=FacB=`=vMspt!;^>Cns81vf#3_LjvpQv1YUlupL4*;Q4S@7(0e3N! z4}|8_0e(I9Sws3ua&{s7inIjQl3$OU6r{fxpQ3O?wo5h!j79PrlKwN6GA3_nL?hj0 zUC2mb3k14UfHPeHXC)_Q`i~WWmJs?=c_!faxY*cOx~;9`VJNZ z2J^)LL@Ct3IuDJ5Xav?IiXIS77-a@=ZD=6Cd|_F@dz24IjY}WG8$%I9$Qub{3;^KW zEY}c&uowgP5p86WkrXcE{=tF%UQt_ZNnSxkvycmlO6A}Q9C-6~u)nuc*i=%Kk(Qd6 zRoRZ54tzUe0`2VWef{B=*L~d}2W+gVE-uJQjtY;-tHmFOTv470xUczt{`~N&PuyNx zU0+q4mk=50@9gSqYh`6^%`*XKXYowHOz)X$f1&@tT)^^yP$3gYGXvSr(gg=xePA_d zY^3lZ#7zC!3x4PHp9L!$x#fY_d_`>yHMPw`qGhQfSo zNb}>FfT7ovm=nx5eQTtyF9Dxk?KFuc_Z}1WSyPe zecc^(g1mwnA(cwe{?Ft*6R@NpC?nGH#%1lboA>TGuxIo7RYXNRZPJ8^lc!CcdEP}* z84#D^cI(R7U3(6yDDT_0X~oh7v!_j(4EfaQKi{+zH(E!9xjee6rLt?E%E8^+aQT8+ z(@^O*W$Lu)TW;x##NPg1cK0r6s%_t~UuolpmCKgSpE(n?ep9DSn}6imJyB=QGh6-Z z+D8xV-MfAL+O^9TFPb-J){Gf5X3n0cqgg9xr=Rh8nmKXzV6Fa-whW2!C%b=2~ ziprY0Mra=~wqPPPW!V^BzQ!{F)2)PJ@l3$9@Gw8R)JIN1HU#o9li13_yliA#lYTSx z9@2l-0ex^(#`L6P^S|jo7A`gf);O?pGPm5&`tTPfa4HahW{}(O^8zw7?P=mugWvGW zZ%{2wAm%iKX8-2_8tSnQO6BZ)z#Z(_0PIWhOu%;^*MtS??O!%`!%eUFj66YERb5j{ zYh$6G!P)h5rcC6SfO#fhQaY%wR1Y~zh3PX^PM!&vX96YH4Bf7O-M>iN=YN4KZHiR zs~*^g+*&9rGh#T1dgZkI0zxBRM90L&CyH=F0l|8$hy9s@(`AvNJN$=HqqjJCpaUyn z=dPECJE9J*hWv-&!-tO;wcOm%lR$VtWd}KYv~a(+@JztcnN4msc-gpA6`Vil+y>i} zNlAIgPbbaEuc?u~i(b+Wq!!!&$Zq19fVowJUYTvuO^y_-I0VPu+=`^4AvwhWC6czN z9%%=Xqh|0k6F819k}k>Blh-;}1Q4f?=62*5u#s`4Si~~{&qRUEsF9;(W!FBoaB}zb z4GannA^oQlUQ6hK)w9Ma$c!F2T1Iy99Rn*TH&5SypkT-en?)pP)L$`o#sozfG}%Qr z^-XP^-8{YhgK&SPWt?>nm(HC%PF{YrjNDqC2PR1GbNBZ4qx;VcP@%9T;=rt_V-@6N z?$3(FM!4{H+J;#p_ushVd37I@nyq}l0dYwXzn)F-%!A;w{y$K-F% ziwg~NvNy1?%X?vT`HtEqkzgSg}NApL_M=T{nXR;g4*_#E7qPoclzjcGkZ6m zAjq4OgIrB5g57T)Id}E4n%cfyN}CU!KdE}r%+}2(n3#T>i+CnrGT6y){kchVcbQp2 z0+{-2nV=W;E6dPp9I|VM%o}d;KteDK2UPlt{6hY2Jpa(|l zN;o-p4LKQUuMoCUu%xFwWmlY-$(d~h5n}@7nSjq|uHu=1*PP^;fK!r@UzD7jOxrqb z@bJ4+h7$7Y!4;L01v2rB3?dXK?ndT5hk;E=wA{}kB;Rnk`^>PWH}N0m9;7Zp6ozy9 zkN24AO*S+lEi@K7@Jzrw6Y$vYc3RswA#!9p{rj&+PW)-qCf5bu{r&G>ef90v!>8!( z8#QY9d{YaX4q;pBmDyiy(p@=nz4`oMkbgJ)n^AIe7LFV?R@KndqC?aYxMSScMwb-- zrnr03H{XmH@!ha*Wk-#eIAg8WZG9uN4sqq#|{g`}@?%BYqe=;;X-ZqX;mh z)hEI9YiQmfF5NrwtG`X%pzt-%1WYz5DrfR@xC}(3ASVBf(gVmv!Hj+iudxh*dUiZO z_eyN{w1lvRKGF_2K1fl1ARiLbL5?zWxd7iMIbDuDolb%jj>XxX1pukq2g?z6xuo}H ze@}a5O^Kkqo^l+dAVR1HVxQjo_U*fulIGg_;-VKZsRgx&V$ydbk_?i+`Qw+q{yuSY zLs?~ILXcNjavmr^ONp$gv;^Y5|NYm;{%%ogtFXDLG&emP*_lzPdHDqeg@r`|<*nk*wz?KkLwaUHpoz*nUZ>!gFd zvsXlV0SYbw+me}_P*_(V8JOnjeD|WtBNvyWH_bf!VhXFkcgM1_s>%wAn^XJ~GQAzn zD<8ISaP^8xOcT^%ec&3L%hgqlMUh^izL&Oa*R{+qsi>-{Z>G#4a!%vl)|UF(w!Fx~ zP;-@~#~;I21R*^`?x@Cg3M|i=AqD;?_aD04!V=8r3Ddcawi4>s)YcFm9iykJb^=$@ zo8%;N^AiH!vNmi(VYug0D{+{Hf95ww3;<*2dbj z$gL~rtV>t0K6qtfM&8?0n{jEwWBMJNB39p_h7FJ!xJ1h`GjpZ4o&I4Ru)#7pZLmBO zaAgHKkUPX}>4BE|=TtN_uh^$Fv`f8{bXuvPSs|(lv~e=Jc=XVrjmu^%RJAXnLSrct zIEfn)+#Wr-e?jx`?!DU=OdUUcr%_TtZhoPFn7}b@I%?9rAKy52}^)qZPPPBRD*oEHi6U0%{vYpI(SfB^MvZ&4a??EoiOEse?&}tQffwrNSN+&c#qPC z9S78o9zT9$_Yt+-KhK#rb-J>>PjF;Ryr?r+?c&+p>sG8>w|TF+))`H7+p}ol+zIlV z%^f}bLpz)%EW4(>e%&UeoxArcs~-S>(|WxNkQ4%(uQXO24pkWgm~7>zC;pheZw=I>jxA@%j5V-Z+~u7 zI!YbN4UekM94SNR)4$@}@=U2Q-^jcDkAJ7GIfC&JwoPlw`(1yVcl28U+&EPq}zXqV;oNNLNB5?(Q3$Vrk0AO4K zA_dbgyRxzh<}Jiz<|6$W3DVSoX9CW^jj3&T^~Y}?-@XP>adV|0Gdc`hx}I(>&Hp0Ka%iQBG=HWC+Nn-CSLqoJtuLb#)zu z?La?{62iiq%oMOq2m1MXdwD*iC=#g^sNfOMtF9^my>%uiVq?QYgJ4nm3Q9`LNluMt z0=|4)ZU6R-t5+R#tF8th3T$e6KDc|u5#BDQhPSUCSKGUD^Qx6AH$5$Z?MxJERYM7+p);01CbgQLd%Kw}X19}R%xJzYVm=pckkG3$b8Ua&a_{ZsSWi zo6)GGL&#q0Mr>#RLKW9=J>$NlU<~tv;6Q*gLsu8mTT(in37FjlTAu*>fdz;{j<_vD zx0n`!e%1wWAv_asdPY`uPA)MOi;ZqvKeS@?vguRDD~?fAP*5B*MscD4i|Dw-lr&oV z2ds~sTDNn_j9D`k$Br3`E@Q^Z?eYnVjEPH3>c-B|SARo$_xeRMfekY5$8qQ~Mq%1* zXWy`>xP(NSm)-sO=hS!dOu*O>SrIF|0SJRpl|W?$rYhIC9g(IiQrjq3mwMFJ{Do;4 z`whYz(p+9j^R2I=y_g#IQ?}z$7z9WG?0U%SQD8pQXhWv)*#I;?$w?pZ8QgY_4UJ3a z;RYkODs9$Oq|OGX5u_eNVYK3YK`$VR;HIvtA#uOZ^5l^%t5?pQHGbw#py`#BS?JwP)1#`Qf?b|wa!zIY z+7+v(|1@g`czV%edSp#`86aH+WLkCk7~D|Zymi&$MUy7*Ou!*N9`2sret{v+smzIO zpO~T8J0+c>j@J6pyo{9OBpAU+kwcywt#35NSs;NptQu+o)D4cjJn$&Pre^CG+fo?2 zBrAajSvdUKa1X)yh6p(mXrw}O&&dpr_CX>Ogq7_#lh|tm+{#v#B}# z#`Uy7P>-sr|C9qBk3sHI8h_oV^?|Jc>;sT~*VM%6KZ3$T>%(V_L=**c#Xi<5sAA82i|{Z%no(2GSNK+fIa1d$MmwBu(~5oLwCF9 z&C3tJ)u#H`nHgTwJaj-=<)He-AVA8K$AyOHnSh&90zGW*pFe&?egBRvN_$jJKD2W1 z@CDIPB<3w{G0y}{*c(_dP#9E+;5njjCCDj3QVAIhq}E&x#HSx9AA~A_hVuKEnEDO% zqz+%oNiwAW(mr3xIUy!18YPR+cz==83d1cd)Pw7QoQcm&pb#Q6xm_sf;+Vk6jzR7P z4NRBvOu#%7FwX>x5FpP4oDWY3nIlyAAY}rVv}A<3-c;YVV&05NlU7@{bTT;=5wX=n z(v%tDX?{g{+p4)!$B&!7P^VtZUEj=@zQt_?8R5^2PAhF#F?Yfk8HI849sdH_FJ7zTz6S%ltP*_&vepP+LviXz8%8nkbICbXP8ep+u zHLj)2sWCsVrO)@`zSWBsOj49VmA}G-%`os89TP5xW3saQz4vLQ6$|G6BsXe=jN+Id z7n+nH=mZNCD<97UEY3c=YvYO;W94K=4py<~^Zm=dzV?y?FN=FR zSF|*BGAIL~FdqmVJ^h2f{_*F(Kt<5goE!Db^6s@uJQFa_1e}XhpY*im7M7RH+zvbw z@Rn7ZwH|o#Ou(`VbFE^dV*y0Z9kgO1OB?gut{hcfw_w_jW8~!Jew@!U0b7D7*pVpS z;IV@@ysi*2ri_-b22UVrjR+SdzrNx8?2L`Yfh$fX|0CMP; z1HeB!EipDaD(c0H$jFHBaFU~e=?a(|K<)((S7B~OYElANnqp$2qZv~wqy!5D9!`Sx zMPgcBc19|T<;Ssa$2sfCA4DA}J+=^qyy%jgM2Ul-!(i8woPh(Q0~L%A)ffA4lER%d zgbq$0fyOfd<5t3f02`WT0*>&oy|i{7&jg$t>|$wbXkbV@$kw)Y_5iePXn=|}HP<7> zwydNuJ@$E^ua}o65^ShZJsasC<<%p_wz#-3CpA7gGW>a1Xh?8SfIk86(+J?KsfX{a zf_V6{(~=XAr9;cZb0()11_{38(FM63#=r`ZoEVQ~Bogm8Qly_27~~2w>~7Nk+^qCe zB=Sht2S^)G+Xad~s`5u#6|_4SRDdZ--0DML0mGw+lsW+YoAkecX9C7;!ku9}gA|rw zKuNgcEcgO8Qd+D@jt(tU=_wX>v;ce<+|>1SHUJ7c~yOEJ)Od3DDdQ;F7l=-t>uEYs<5fLIM-3YO1O+7jQi$KvSDg)Q2Sgmwm#f8bNAu zpod3TDJsM$1vw8Br=?xg^Xso4UJrJ+*VP~iKiJ*X#V5BILS%h`5pEK9 zzWL+h+ra^0eVrgbH9pMC)!E+0%IX98xFu6cQMcbb}7TN|6oauP$`+|Bi_ozc+H(1|a|VFy6mKWt8bvZ^b~ zj1Kg0HobHGl*ZwshcrDvN0ksCPx2N(C$&}yQiJ^*tsdUEtfc{}US*vqzA20kcAQ ztin7Kut5sX1pM%+aV?dk;MiP|^lOFX3h_X6R>Qno40iJ?mc|+)Wpo97MT{TdX>z%g1pSchyX8l7oG_i!FL4UVO_F- zJ`3<(j;X7F(?2;X%+Jfy&D}q~q*Tz;|NFoGK=7cq8_8L%wG~B0dC3tWeyHMg zadryI&F_8tkAMF8{?*I=P9%rcRF@PLq^C#v`?#Tg)XB~+GNb?PumAkxm)8T`O%2VM z$wk>&X-Sa*o+#3Fw6(GgOz8XNU;q65!^=KNL2+G0LtSxxR&w0)Ko2K-2L~HV+koi) zxBusV{`CPg$hGyrk*h8$&Pj|2@pi`8b~e`bexdyXegFICFR%J=_<$`@QIwSu_nb;x zt!+@^>g3_u&ocq{^!6cBvzy2)DS5x47RP)%<~&xm!!rRRdxuhZ(C{zAlEErMFfQa3 z;hBItk!w&>kdhP!~?WEX<6b=s$S)*vQ1h!rI=+)x(QYFp$nF6t>n^5Xo;sOhi}+LI415g8w5d zoaQYQ8(SV+IS2yqOu&>x%v72Z`Pz_tjf7xi?MXG8{-IPq4w|K%P<@5MP88JrMgK*B z6s3G=o(Z_K-Pgh5?xmAj8p^6_d$(>_wrtV-nKPzMpFVxg+}YII1)4Sz7Spn>wd?||hth_XO94?dW7^colhI|y zc^{q$7!yxY8)tJv>(GIHd-m+!xnt)6jdMD89vB!~*f_XQgox#=Hdf>$$3+DD`*`Bm z?}h)oefIbPDR~qbyY>ks!5JT>;oDc8=sJnhzu|! zae-}KLaN*b5XG|MLbBbnGBbgs4VXA=VQfPqL!b>tD-3)tc!Wx#oSe2H_*86kJQFa} zPHFuSmAHIn3LVl-_zkFl34ZT-=2n$v_|qcEl$X?7Isk%kwL?sxB>&t&s{idtS>W0D zve6J!rq#lVx$?BlbD12EP}<7)BP`H{idQ`#p_pT71OINuU+ zRr&LrhyWA&Kqo_UE1N49Z`{&3p`m^5%oTkz8^AP5Iy>rfgB>1+*gi0{eX4gs`}#Fi zm9rNw=sYsDvPA_8X<$P^?2AaRhjw-*_jIpnX`a@(a!t>`)XL5Y4+C2`&jie}3@8_A z$VbjI0XJqJG09^3+uikr{!?`>;^Iv$rR(MI`I8(3VxRS&Sz_=^G&R>8oN>kUKlLBB z9IarDt$FJY+%Z6g@aN@$K84IRG6$PP&U5ZPeJXA$kmkk8Iy=Te8iyS1p;n zgJ%M6!+MTd*7o`&`zr@FuUa&F=A8MElb)cP&ZG==L2=XxL<#Zv}13wVm{eNKs{nUZg@U?QE`^Bqt1YA!u{R=t! zZltNK2VMbL-TEaHAae1A=>YG62!8~=fe^NW^zV{LfR@tR-)D9gDoH*Gk|U#sV*;cs z{~lpaTc!K)6E1WuCl|HzOu*y;+|4zOk82=M3^S((Dz|0F?aK*eI2K`iCwT z3(4Qeb@(J;KhUfu4Iv-^a;h2NlN$z^xX(-i9VFRUK_fo7B|;-^Od!>PTs zt?1U0F|u-_Wz`;8yZZ$qK`9~@bB?4W$LYH<>CU>zj#aILX9Wps z0QmrlEH&6QHIUT-%pd5)EwI?rQ~CMM;Uk7U-w7+Mo2@H7fvOj zXiUz%uw|x$#z)fUf046ZJQFaPSlu02M^ZXro;Nk&0dS9qoN$uP;^-t)&HCy;GLYZ| zf@gudMo&=EUR&1K-Pe&BWM9^1Z_&`(3o8oux{*o6qWT68|Hk(G%y_2@r*;~5gZ>%^ zaZCn60T6>jtSXvk0#@2_?fw;A%|**sES;xz_rcYNA=pH*vE~KaxOrOYom%Z5U~z8$ zn)N%j%-$3bVE5>VL1a`6Zhn!sneH_sD?Qype+PZF1DlmLZ9Ekf=3sN-Mp(oPJfFHm z$6Hp(zP8@k{*ETc)%R@Qcv#EqnSDZ{cnq);lu68rOY#}eSxz>D%orxD z1Tu<0HEhmwLSU8+32+pEfy6cTE9d9>Z26G&!#J1F2gVrB1k4Cd;GeFKGuAtL%H7M# z+}ZKEmFXp|y$`*tpZaHmX0Wgbd4YLRR!5Irw+nMIdvHqi@$E~;_W8zH-M{@JK0YZe zOVm-95$a;AZ;%~qcUM(u(@vHB8y21MvACrdh7|soM3Jy8KF}@K%OcLf`1+9}JQFZj z9C#*R;`k(#L!Jqko$s*!w^a)2y85b3Rx5-w8R-N4l-5kFZ)7~+$X`|U*t)hUG4TGJ zF{@@=@9V^AkS%}pbiNc;)(QMmbyaP{&d#1POm@oh1Lv!nLD^D;+76LW(qz1Cy_Np0 z`QMK*yES`^tjq+Z*(0raCSaZk*c@4ev^%%A3EL`@l4JecU0mIqZ7ofWO+fMH0F(hw zACkl9r(=fz+1If#QO|==W8~@O;~x+h913t0GcF~<6We237e|~g~hdP zB3L^^a$L?c0rO12G_}xZ*QE3*OjbC^v-PfDT_-1_fUqw14%QGkr`Ql;--w{`Ou)4@ zl|Z5)t>m15xH%MGY$5Gp%LhWt)s^%XOC4j!0E8c#3G;@n4_E?tCg3b$V#Pw#_REKN zuRyiiR8^Rj_&mVdE4CCs=>^%@jApg6@#Du|-n{`#PeWBbmYffBpFG)j(foOO+riF*4NO`gz zuC8vbjwYt&7S`4EfJqXGLB-b9(Nt5G8~;2IWjY?u+}+F#j7`lfYia@81UGDNPiI?W zRY`XIa}X7KdU`ywdxTOyQw!j4G&HxNgGAg~kB1ve2XHShZ`UV}p3?O|2&ijnX$2#^ zu(hUKkRB5e8Wa#1;BI7KYyzk-Ydrj#dPK942hh<3p5|CUfIJU=W@Tn(ZfY!j4FcfSiXP4k6)>WkWS{gkzaEL0Y zucZ#90LsDjT&pRKa?rndPD_2?cBP|cne~7~DI=IJPCu(^1pXGrkM(#a;O*;H01IRB zlEq6_?ehi<4G~FK#G;zj)dt!1YP&YBUP{u%OO~$MVGczoE3d4s2={fgvwV2{g66?( zYga5>xNs5V%U1JDz&sOhado8>qzI=0G+OR>Cg5+r`F5DX(<*S-@=U;3=6NRIg1p>p znhJpa}%7d?+&RteIxMsnk#Z#tDo;-2V#7R?rQjbl}%E>Ph5C!wgmIs&B zcC7w+)|{EsrcRqYY4YR=^LZvo)zjgpv2O01 z8Pn&lRX%t9_JgP9wodMzz5&6IHMx7~jBZ%Fgfca-T(@vcfTFIx9#LaqOt8C=?zv;9&pQF#6mqH}U`33Q{(){` zT~?@%v$2l0nwt6P&jehM z8e*q^Nn7L4o~=r|cI{O^W9A(Y5*`&BkEbJ+v=(J0_&7X1tEsBIM`_!R-TMw-w01{_ z=P#n^{!7HAsV}^(@14<5RoS-#1MJh#v2lI|rr+=={AEN>O6vwpbN-Cg3pVCs)qs1hyiq4B`f=wC0wFuED`RaeYBbgzJ-wTBpv~Hv!iX zCB~35{qKD9?p2?tIx`{6RsS6HU)!BZI~kN1lfV4%;g^B7lBCE$2R-e>Du<6>OcqgD z1~M{8|2yCR^6`ClLqS}akJarHhgDQHwJhqeK2so;=6~<|cfbA7RhtzV;AM8>xauJl z6^$FIC?caA4YDK#e*5_6KRc@ugMHkeUOK9xa!5t(tPjXbaeJGAP`)~ zDx0_xKh)_}CVSmKd-UkhGv+)KFf8l~si~=H=^0cc+*Od5R99)Vcg^gHKgs;?{cst1 zS@{|2fr$FOh>jt7UP4N;&%<4drjAz}J!07Rqh)2}X6$wH@bUo@2oar@#DTaDo z9yP>2d^c>AtlXICyR95tz=Pq5!KKYZu54ABM}n zA2C{f@>1=acOM&@Sv%n=w+q{>PO5BJG(&#G@Dam?eLqrm?2L`)uIYm5*UFZ#0or*c zU`ijP4FxEo2*p*D3JQoPG%Y#lulmn30rO12UdDGXo<62=(gP)gpb8aB$N}>4_kaH5 z;~P<9MQ*s`qs!V_n#az#5mrWc1kOfi?>-_!s<)xMD9O)U@60i@i>^4nhlYiT2t%{~ z6%wR+I;sU3k*-f}p42#cOjF0n-8&FK*&^^D_YMrcdHbrTy(%v`(Ba9=6Y7VLo_JvI z3==cWbn&*wJo!q>91AtQw2XO!3;6PV%b$()igTaln z$B&&ndyi)V=9z#gjTr_285KALW}BSc%`*Wnn)8#qoFdNz3@4$f1*m?Rx1FV>r##l! zux0mN<+Eo_oV%%~Z)nOh0q5pqePM0`XuOHT3ZNW3(1y}s;jKtTyDu^Qt-PL3{cs)^xLZw5pbPBN7U z#gKeR+|^lMRFsxj*wo(ENVIE}z)K%u@+JMxF;?HWdeM@dXB|u138D?xe`5L-NkjqX zmG^I1xqQj2sWUe}%?E8Ncw0Zoc_v_<3Am{yD={;dX9A}DTPPmo=Tc)O1yokR6&8?U zRlvhY(Pbv*MBFQRv;b~IM4q1p_)<H-^5u&c&T5@FfBnwG zr>2%xHjsm-7nBkuvF;!<9>YP z#-`?$*0%PJRGGpCKx~bQx_BmFl;gm>r|q*?P%MD!5e=I!&JJwz#Rd;*RkGdL3P4hN zpEhsmN9D(;5&&8mT`v$&6b?H(Gc&MzQ|&Mr5Nv;E@)oLV=JtPDM7YxA3N#oI=zxMz zPR#ay&PZYVKMEvKLtR8A$8rudESDtvZIH8`@;{zWp=04z&ate zNRl@;3vM4iyJx}VaSG#QzaKXI`%yBhgY%Fwj+}9lH#FGYzkXuR{P903jFBAU6fB&jm)C&0RqP+aH$N+a&XGeP*YinCOTav$g{qfz) zPGL)JWl4TfR&qqJpNEU9lf9k2ovrgTAJizk{^jieh={98iwkm66J9(=5}>QA3&wEv z@bX28!r<%I1EPj%gvAQ7Q<7sN!h-#My**uA0K(-L*hd`(2YT@NmFSU|odzr}Kymr| z2LXgDJfasJP-oH)Dqo<(1N^PLI3Ily6BFX&bTt=eMEklrT38rA0S(y23+GOqIC=8a#hVWdEo_ih z(kW@kiS%@~F*h|ecyRyLwJTRHT)cekhTbD13mbcuchcEWnI6G20h0%wGT%uR;SwQO zptdd$AhOIHNazQNn7QPc7%%Nz$b>~gJ$C_o_>fcjJGt#i%rgOZ^`z^c(o#LJPx*lI zt}UBaEdF`U%o)??Y`X55Q2{%wv%B!=t*fVwsHrF)RN1>}&C;cd=gpciZN{uQKX1`X z&(GnRfV$=TrmjAq9!Q9z%=gwWQc&pasJNmS51nOKkzJL2xR57ku zy=v)_rArnqSh!^Mo)edJ??1sr6n8}2IlgcEmd%?tty{lw&8k(aSFYT!N8{|(+Yb#) z*=**SfThO(A}OKr0URqC4<`=76u|pr-D5$Z%vL(kvjkQqN8aD39tOad(p3e?N#aBcgJO%d2XE zcS8!%-S_fMZ)IYTJu1B;APpY+9;QipD`WbyI(aU%3fGxX!^|rlVVrD^c1yGpk zDk~CQjV>!6IId?Z62I(B^ofptW@Gfw+Sl6eMSMnSPI8cik-?e$YA0_S129*TVeXWa z5*_UA;^q?-67k&E-N^LGt&2x_CgAk+bYSpF8*QBkPgD4r@IA@Bo12?Uo-*o#7pf_d zb^wVGSBYEz7~Rj*9CRceg7`!P93k`~5|Wplj?)m+0Eoj=9x(afONvQ|_qpU9Xy%X{ zE_lM>Dv@sQtgM!q@&A4Qhl0cW$1Y9eI{$tDCopAz9|K+g|FZvMnfP-1rtb;+fGjxH z*!KSaasRIyvJa?ofGZs!1A+cte94Z)OpXa0^#h+jgX*+7CUBk!_{u|JUV`zSt(%w3 zTY2QRb4YA*Ms`w=?cFQtYZgoyJ7Lk;yP~QWj@mnSA5hw`dgV6Ny_(u*&R;vFwspzE z1yjaNU32z!hd9;d;&5Rv?)LS zICa4q^{aOuwbOEy_w?AdwM)0JoWF4S&vPb?pE_mSq`AwKj$ga`@G)i-HlDgL-GfTo zR?l0uXx^+@v!>6OziNxxnd^7;4b36tnSdFeUnHCiKM^O*uwOtwX@f= zOh8JAMM#*W^_DIt64*h3w}nr5NPwfQrE3+@TC&ZczK)zGkoR@lr}tIorNz0~7#lqY z%xwd<3Bpuk*Z1@eW|`&aw9q`S#oom+a(l2S6VvU76t@^Vn*gDm@jmhd+(yK2&X z?G5hSxN+0+MSMznR#sMab~d}d4}SVrJsst-UY3S;u3pwL3WZ zm#=%8^5TMB%%0r3boGgUcyuBzE(WI~i*P*5a5 z4HF<^AZH!=KqTGXLRn^rm9d765AYN`6EH0k+%Z9FtiW=Cal#e(eOf=bV*o_}Nk*@q zQg|^@s8RJTTUvM~U{dcY<}~G*fB~7(-V(NJ@&wsYBS*>bOu+1*K-ou3TfTT9og#~V zsKXZ`POZ4WJQ@1+UtUkg47!%YWFB(^)O}(CCp&-|YTM}?!IEAm3xIp~%U59AupS(f zFOz(!{~RkJHv$eH|DpeE6`@lE&jehKM*2^XBqAjZmn}1kE^jzaHkEiq~S&jlYbHq zRSTgH2I)_E-1UW=hGicwY9kylo(b6Yu|aAU4Df7`uq4LcJ zQ0dx(%eNyEQ!+9$MB?V;faJyq8=FU$j~=)3Gd;gW<>2P+>Zh;yzlcjpO~>P^e&L^( z>TCVr$o|95wvXk`Vq#)r zfcQ{;Fn6)Aw#L-4rU6Gr7Y&tF4|>GJ15QR#9d5oz)Y4KOnq#dW5@~vM@4>a3A9?!M zHz7}$+$i{LVNtBFm$B(1Tf6)ei_`a1mK*9`i!H4v<(Ys1B!b&l8SP=onSrX)Ecxdm=of{9Iv;|l4+53*p9=QKa z8PDzVa|3KmqP?si9X_~j)w&Z}+7F)Ie_(Fop_eGMd_;Xmt{T03}ni^Oda>lExCE&Ac_!=}s`|IM&(zxwK{??(PKecl2k zLmOug*cT5M4BxPM{NMg2bLHo6{{Hu`zCjIwoc0C8X}vJ#LzMKs?C)u>tSJ$cgH?$teJMOh)j;g>-nVbxy_7W9))yDOh)FG|r7$Wh zHZCtG`I|p}fsa_++)!3onGoa^mYfIIcqt;Bh;c&P_rL%8*pDRhR$+5fX>K~t1k5u5 z^Gv|7ZK)c!lzHlCYulapP>P0*npm~Krq<@PEE*wp6UmA=BYMeeND#_FOM2e%BR;0$$P zUUHO!wzW3arbTXDLGz!kpmDf79p++(qBPZJT-xv$a=bwmaYK#VUTBRCHD#HZxeyIC z)`2DDKn(o@%8ekO5_g6=kPMBkWr;jI6L3WZ%?eRfppBE!#iNG~ZCo~Ep{jim6&iCu zALS%&NN{`fd4{ahg5g2TfJuf z%-P$ll2TIBGjs9^+dD*U0;iLkcN{o$@SwWp3DvzDmd%|yVaf&nh!{kaJ^VvEoF**0ro4XLCZ(M~pi)16PW$lY1KZax8vmoblBtb@>lK~}m^2rS{KGsG zFxCQY1puIDRe41jcW-F%qdk-nA+vIKN}xlE2+ssuR#r}t^s1^lQExBEV53Ntg3B<{Nww#FL8!ysTHKfg$04g*v-Y++0o70^BFF0YW)S55A=%L zT59r=qeDTp>*k8f?VQ}*-D-fr&^q|bThJ_bwl`Pir$hz&c|Svuu8WJEt%H+GHRSD* zx1dP|Rd0P+Zem1;uh%m-SM;y~h=(1?c_!fMItDcX_`ULy!ko+$uuTX0`FaDbsD!9o z(W(KjQG;V~2_|)BMoLm_cxX^yfS)hY#H0!WB>?c9pa_#5)&EIxTr7fcgK-d`#!{ZR zf9dF43PbY?Vq{DUcX&W2&l)Gjj7H+^h|Cv{mTX(5zoxXJa_B~R9B7vZ|xG3o1Nez{qTQ+Xmw0YaE1BbQFUb@aR0rO12wCdH{ z9zUaU5Lxr1zyI!=ukk;g3HZD3M~~A+#2jZZo(VXgG5PXLz(~VpbbZ9=M``kCLiQfz zfJ+{H&fXvzGU(-vM&?tRJR_yTRX9O6btEtNxSH!PYnYv$xB z6Q*fY4q|j9hBP;^?=Et#M z1=4gwdVftBtS)5#7Z$N?y_n=QVY&flLBw#ltAuh5vy0iSreW?q zqVZ(5G09lNq^9{OVoq@K;4(;5vWcf_+Y>ycuKUTSs#Hi+^xiy$-bY6AmhSlq5O`JOiaz#aj z*)a|5&gdDU2N}cTm7;KzSx$u*`xQnl=*=vQ`_bOY*bQ z(?O9$1xh>!hpG(OJ+Y*YPu3Efk(ga00S@3j? zk=dJ@pNAszJje$+?985OY+S!~&h!c30SC>OyuADj-}v~%#N<@EUSe|lmfEtFOQud5 zhr#9W2@3M_oc)5sBBEnyeHygVJhgVmqG{8oD~wT4L>C1`nay@C-a%mzQMCUJG~PJ5 zbKSz}6DE!yJ7%n+yrP2KqzlIO9{z-)M+3ad=b3<^0MH0TGeL9#H$QPpW$pm3?BKE8qVj>^XE~qTAaj@$t5YICKlTn5oK%NOW%<1Xf zTc=d_?LDBpPg%=2BP$Daz&W&93vrmL&ksa(?e!Cf_UzfWZ}0vS1}Kn8Nll{~0n{k9 z)t2Y@n(FCXICgN?u08wqtDH1=5u1>dl$1*r6Z?Ni#md+$Eg(}uxe z=#iMnc7w(eo(ULgTUUe9?Sib-q{Kwrx}9e^6T)pwR1=J?cTFzzxs)L!0-ptpFf_Cq*K&fU!ET6{#f_&nWG2x?B28g z@acyp=-}$UI)E=ItjtObbK;qR z5d!4+ad;+R0u;oe$mzeNB_q`Jruw!O^JYw%wA!+zlciHol_OgoBu$wSp5|ASx2>8x zb^N&L3w7$nlx{&jI96ZOR*(_?%;>bzh81%sjFC|oH}7FP2KX$mDk#niy05uq)uQP? z%E^rWaq7xY35P~ROyJ^nL19^u`&IP~%jQo8)ck&~jc|Q%m!|F+Z=R&-dcK z)r%HPQj{4bBdaiBGYousk|m@+)wPw~@4ZhetynPkC%I80WE98zxX`4e2(7RHwoPYQ zb4A-LwW3d>6nG}!181+?0#mS!4RQgI@h9}%wPyLeiDTvDWk$-3pYpTP ziHkaS9~+t4*r4vW1zT*J!=|P4rj8%`qk_WZ*-LhvyaEz|Cr0MBb_5|r34m?kr+04O zuC#pCf>k?C-naw&p{K@XHg@*dow3tkqO~;?2}%kR{oJVv5cv--F3v6}1|+x_mbuf2 z{l6CAUIpp#FCrqspFa-`48#J;F2;EPmHCZz*#FB)it;j3k^nLu9~&7N`GV38F%7Yk zar-}l>E$IT7EDV`NllE4PoS@c4wN~B{hwz7#?4F4Z6M8VAhwRm3Q$qg0YEJ78R+ek zH1kZrJQJ{^sfncxh|EEC?&nWs?Z62F3=`Po1*N5VN#OzB-rhdI34nPM0s{w)99Ret zv^LjPfhsI9F8T#@Ga@1auUOz|2-xt5FMtG~vJ?u844cFRA_|C&X`>F*a5;1oUY&9P zJY;94rzWE%C1P=66BOYfcm^;)RTYxca0tlCNJ~jcPHLeAhO(eBrE#t&YKaQUUXvCB zr!`~wLUJ*O%+X8~HsGiMMGe;@D--bw@+Y8CJPC|Rl&3=!IB2jEve`Go@RZ<;rIIju z5m>4vh&~qM;N zT*m%>d=Bm-AvzEW34H}7|HSl*eXg?sOrXhv79m{c9EKqN)S=6HCg7%^`{#DA=9z#O zEM31z^*YZ44CTfu!{U`d1S0fM@asb5h{1~12U7o>tRek|k-{?p@7lP2;ly!c6esSA zYoOFd_*&TdB9=tocQm=Esl09V+;Q@wWyVb0o>yB5Z%v7S$tBeeHUXx$_wU}YVER}& znNf1%CL7daA&0Amwl3=^fZGf5O-?vZF@H$|%e-3NE;fPTZKYN-^G$3FSY-(r%IDg;AU*Es%6E@WdQj-HcJiz!hAu>7Z{!18Ms z_Wbc1a{RknnyN~&lA`@RoE_|KJc-OIk!J$tnSjYUfPDZ10p#GvB`&jg$eHgk^Yw5X71km(TCTwha=78T@f4=?#8hmzXA z%4tcd%t?Rlh$L3QOjXD@c2iCPx}i6* z1Yi|_ep7)ngzSZild1leX9DJ#fWH~9Z)R!h=u*=tI6HQQj`8J{vlPaT{2tX0-=P3v z#HjD)-F;+iW@%GX+Ze63PWj-jk!MH1f)`vF->yRY7PWMXa&KTmyq$az(@ zMT+ATM*T2+S2vdHY+CTs^f7WHMvopPJ8t5X#aop% z&)>NF*w~_$W9&6gSUL2=j5aHhOed_vS5Ky?YOzJT)=1sI7&ZwnZ}M z3i2`&BLckKT^t?k?HwE(on2gO*Z}k$sAM199vMlok)c5W0sek|e!jlGHMQh&V?*HN zEkM);KI(*+7l;9bg@%L#qi~Pp)ZpAN69|e0g?ZWO1O>?Ykm^gNmEg!`+#f|n6aq*| zijRqUf%}I>%FXn)lC2O$SS7#|jZ_1k37BAZ;o$`mKcy;CK#ghxSZgB~K#~%QKl1#0 zMQybuc?A{CLU?=8r1H+5-hnr72m5wbj>%@suI*vJo`JFryFwX?c0&pOseN0X)zwcqf%pWfD84!=%AM9bLHOe1`7Ly%4Mpr>)3Kpul32 z&NBhuyQHbMeaC*KjT=@jTRMN{%<0poO`SGv{*i0!j%8vPi{DE-Q5FDB`{W6rbRr7E^Gv{$njo!MqC5_uy2F;C zwNca`p=RMn`3rQ&z}W(gB(#p=2h<;jKXLKyK#Ke)G4-Gkh%!;wlkm{j$4AsqjpPPg zz^-Qa5+8vDHiwXXZNZ>pYFpmU`Mad*?du?H2 zpsUU6TT05;)HSs{z?te#E*w0nmr-6?R#{Wu#PU6Xu#{tZWy`vyhqPYRh6ZS!+^~GtBhQ4) z{NnQJ`sUWQrXpWG)t$@c&0fzV0aFe?eL?T7&5d+6)PDBzwV|<@g{>n&`2>ejn|j*) z*!;)@X{aef4PRm$fO;_E;^X6SEyvK-M%H&jeGR4fV*6A6R$5v*oH#dG zb_ZqP0jLyrCmxayIQNjqkE53hFd8bkPSfRB9#b|=1yTe z+F77eB%TGC>1ZZxm1g0QfCmRmwDf?xB+=rL(bGbj1!>g1eZs!>Dp#e;PNcqbT0VLC ziOeGb^GLwxWk3T^!eYSPlP3+riOG=(qz#Df^)9Pek?)i8jS@6WYF@v%jPVENlw8ny|cvucv3+)zy*NOU=QUIK{nj z1CCB6IDH1)wt7ai|JfU8BY{YCqKdm(#I03cmrfaoyNBtdvW<}YlS8sH-PDfmH|-F! zKY;QgBuL=&+4Yo~8X3vWl9{pim6-0|g3|!Pnw!0=tG~$o(b2uj=A#S8lEluImc}|H z8Poc~wRCx}r9+s?`lX9yq@<>(Rig)aFPff3fRbXZnoED7fO#CH)hP(2^06(xq%@vDmo^X%@3B&XxXixj~z2+ z%(w{~P3=AWf*F(@=vcsbwym$$&;M3t{Ft%0!Fqjb7w-UKh-S|pbdjh*XT_Yk(&NXE z8$WT6u7$HFQ?MgBI_BV!fZ4{Nok5lcCDhU2e);(oWY9k|e5TM(L?tq9Z5|1@6Dko~ zU$j861iN~=)K%_vviLp;NYeUy*IwhokZ*4E)eWV4HfqJE#FJC>}N zJ7=(aLu&^o7k4i|`gq{{#4*&- z-dU`14{#lje?BI^7vF(aFGIjuevi7|dAbk$~ZWY-oUj(~%z^66#>9XJwNgWpL-I z{2oJXt&|$XX(;mxO)9Dr?Df>2x;mK{*_b^xe0f*(!ZR0Jvv72uMh|zPs3|AL{>DuW zD}Q@atvjku@7+*1>l$XQ|1vW-H?Ocr+)T3I3H`&+fnY`@D?@u4!vFV;0j|6OH zYT@o19NH;t6@)k$1V_7DUwiFgttNkP^QP@8H?Aq(H?ei`4g|d=CD7T}EXeiAg&XR3 zfaEJ|5hOTy`x>d=eDv_nZS`9!m#$pDc>cl@b4SkrLi!bo z+tWR~y-c*Vw0I<75)_>JkJFV7K~7yCQ9~qfNzi|D$sy6){3kjmjfh!TquNXYrde_& zQMh0LP6GVg7o*X|(Yb3V$UtYcunpoz)Yp-EBwozuB(%uLfRao>I35Xj%bs&*w%^gb ze%C!PEFv-*NS$fH>22|jj;|hEKBnbvaDBhL{N_EfN;hmhe1pQM%TGN_n}TqsR~L5e zwlLFvuygyqt!ou8+^}@<@CgjTGi^@`C@Tzhc(rHeEv?%R&ad9I>ATryv^8(tckm=6 zQDJ3*XGo-n@$C)a!6w&^Z`*Zb-<%y0!4{8CKjx8uy{LvCXJ=c3AU`!S(9h4`$HNu< zL7ZINP=V{2vz= z&ms`a1IbzsL*qoQb6Igdj|6O~s?H+;8#{aX2L_9}3-^AlT5xtQj|BV`IwVbdEU$3})>(O%qa~vKy<`>4EQJe9XuNTZ%uw~b_@0PAx`|Y^VJFnl@er=2{LI9e( zGkNd1DSw@^=isr^XXNG2D=Hn^c;J@C3q3| zb@UB+Bw!>OGQ)xnY0fdnBLR1{m$o$3qhiKdQL?z@_H?;tZ z5}FwuC88gCTDp4c%R;Tpy#m6bv+@Pi$PQweM`XQ;J9#8v9tjx12MQzdNWh)KcR$pH z*?WgXgoH&WrG|Ukyw6y`9p^5&_^|Y@ay5;5_5Ej*)zIn60;e%T@)o$P8 zk$@%UAddtL2OWhs^eG+p} zxSf@r93SOvto{6=oYM9+b7o9eF*ZG@^wiuTB(DNMkVyQlEGsU^@H4o4;^L*v%cn{$ zd7^LO7!s3M0Ju&J@(p9vl_eP7K6m*0b<@X7pV7`Q0Tf{aI*A}b1aV<<5Z6?vnBBdg zwsV2Z*p+t?X+{GS<{YM~ND7QJ)zv2I-jY@NZmN{b(N0Dr?+rXO

      f~q*?e$d!DUqRo>UDK=ba07`j*O`0k$@Xp(5n+!4pfYp85b7l*08_`lh zZd!a~u&<|^i?fr1Lm7wj%Oe5T)>o$cm>ay(vx_cmsH27^WhjI!rmat-wk+E2<)a(Q z=T00vplFiSfR-rK5kPqs*5AWTwdGnsv6HF&0`0I1$Qn>TLQv{RS5u@)7w`D+W&dn>>~ z?}?g{?1AlDH?3c{VdHN5D$olE>9@KnD%LpL^1C%_*KgTz;;D|l z5v4&?m3Z5j=s&%G^YV$ko7b&bwQBY1wHvqXQhxOG`76kOFeF=<7--zTc1do}rnPIn zTeW)4`mNg!sos64sa?hx%6$xUAE^PU_rNybAg@`wY5UIIXRoQ<)6gm{0~TpnxxoJE z6V=P-P94~>g+~HLoFOSOIy5*CSm9W|l*@>MV4!=W^$&;k&2<43pd|^>S-)PaV}eIS z%M^4(LB=^HAPY)(Bw*U#JQDEg6$@ui9XIZ)ze&bdJQDCF6;-v{>O~j@1w0b)03ogp z@<_lu67c+ab7sxmtl<_Eo0^rAm(Q(>H$|_N)^6Ck0^I@T&0n@lMbF+lG&VUsJ3E`B z_xB5fuO8mGVa@7I2b8o-Y+U@pq7zawvU746eSk*-2B0UUzXSGyQuKg60xtrbF)cml zfW-~g1dGtGDTR-6Tp1lWpFkm$0ClolcuJS0ND<~j@&dzA1$}rBiRH|KLqZ=Wv3Us4 zkYnH`;I8kHya|R|$lZnAK_p~2q>^_?-#8A+OQ&)QVJBTrXrd#7F(B)mBjf#{hgUxs zxEc&%WAB5iF({i6|0Xa>8UzVG$flyd8FWg%7$Fb6>nyJnRCf&xfrW0u{=T+8%ud(7 zt+rL#vwhvx!*|jK-u4k4h52xrfsWFUH~o<+`wnhiykzdo=?ibhbq{dB5pam{NWl06 z1`BSSJG^4W!nw0%%$PBK>f~uFt{B>R1w}-~#&I9mK;E-6OINO$KXc~H=`-eSkX6^U zaPkTW4Udebj}sq9U#H*2-78ltS^V8jd37xvBWov5Dnf|H^;lX2ciGdD6X@dL84mx3 zkB>j<`(xsPWthgz&m#eIJDP0?$~Yo#1C>-@NI8fP(O1@}WgS z9(-4At*~SBcN@P~OYI-z+6m%ewl#C~H+>PxyT9Ksf8m_zGIOs-i+U+pnl%b!rDJaz zj7}doxO(k^Su?+#JbB`jlg0f&j3+dcS}H6X6!J*GPfssi{@q-NhiOx1uRe0^(Mw}% zM>h{IKYG6XeLa1hz6v{*FI%*5<@VDz2;_rG_&u=CL5KK<0O)DW3wCqziHHdHc60Th z0$!Bx$HX%6O#*@e4k8t=EI$+QI?2hvIR(y6Dvk}D9ykuV=s2K0TXlf)0nTPVkT!F3 zb4Y%W3}W)0P`$8kQ0On|!c$lP666yGi5LVsC=DN*iyA>Q9wM>9BLQJsEv`>rT7K7x_~UsIH*n+5hV&f;onGXX{{|t4)b<) zjjVwtB;om6~iZ0+RX7Zeg6iFHQnN1Sb=r=g~z zs3>>rlpM%5j_%(6L1+WQ)*&shd?!8aXPS2}o>#uDYvbhV$-0DuM^L>u71tuG0X}Cz zR%Bp6PzW$oDZPt|D6wRzFO-Nx2ag0y50dsjO9f%&1d{mg|JeVOIZXwPf7<_OW`K+w zZvV52$JUYk4`I?&mKhV^>JnB#x;gH^BLU~+(#jmjBS%f~Zt!c65-Rua7rEh#20!e*To;Pb?@nZ*C+c@Pdq_ z*qG?(sHmtg%JRoDW>q!qqz`gP;CWeTKnP4qPK-sOKUZ3#R3)uNj4aM+KxbvS!07eL9wQ|qEwkuc0C2vc zAVWU%o+^y)5+3pLT#*uQ+{6zK_LzZo?_ zdeYQ|M=b!Q>F(h{O)wh$OrG30vtj;J^cWcR^*0lwrDrTYp=V@ihy6hwDPhplJBkO^ zEuK7n^yqKC9yLKqX7;l4&vXn;tsPyV1B%3X8aL&4ZeJxmW)vyzpO(Lf_Z=>C?YtKujoMLPG9Z#giGf0gl>0S2m`+YsR1*m^jy|lQOVoaRLjgTzpd3g_@*CFuaUamg z?I%Qs6ISI+^s13J2RiBwo9h9tjfP5ii|qO@=)%_Kni3uf*x&q_y4uBamwf>wo0gsq z2xPbze*NbkfBg2bue~}annwcWk$`z5;1+@+CR9{v5=rg@e+w5Am*-EPDR1Aob**rVi#m-RY<@JqA7tEZ$OjgiH6*=sBh^W>!0}2xTJ~&rD>ncQ;Njwtp-q!_4bgu$xy9DW1)Ya+v@ZOcH zJQDD%N#oH-Ols1sm5-3%3L6;yY;?j6($-K`KDA`_oM{us0<24F@|+F#k-ukRYDP5z zE$!Klt|=VZvS8+P>2YI5j~*{IZO+cSkDtFbG%SK0z-0?1hn$qHo_y})jXGeQm zOFIv5U;n^BY+g}k-;W;#yF|@pB{|7aAzp4sZFjV@v2*bP1B^xZ$ooI^idw2+y~Tw2 zf$r>JY;11r=lRV%uEOm@bUC;cX#(7FP=BG%xVP#XSmi@mE>h4$Hhd3hXTcqnsqdggrPF24)g(T zz_C(Vn9FedVxpoVf#gZR(IlZ@gHw2sY;Qq9PDW~S60rVaV`3T_npt@Ol?Z^22`Zo# zqLT-#ca(5|&M{EOoaRP4z$*xb7sh)=T557~Qe8c|O)?1wL~7QWl+gu6)QT9fP9P1| z)q#TmfHu1LXQij18yGi$`@tgtQ-GHttuQ@__CH#AQuQ2tgsqJ=r3HCK z6@d7u0dyoQF(5jY2$7pAv(m!69c;`zcqHI+@~5xDg>LKMR9D|v5tmq5oEa19VQli^ z!ObfdFPuBCpm^cV3q$A^j&-zvi}Q-pBE217Ki9Z(Ls?1j!lg?WuBt!fk$`z5;NsR+ zu6&iu7!1G@F~1>76%cTNodZuG?N1E!JZ21J*rN%06;&z}3rOlRGJ9bR9y?c1^Gm8YAJqlUiH|jxicoulpZ~5%;*VHTZ4e~iYO4|cO(988}0j- zkFA_JeezW4F>s@gkr_KhFEt@PE-sF4FD%h9w|jhi<9FXmNsS#f>YGuc#!vieS9nNR zctixqMK&*Wy@MYgo4aW0mWS3x08k4}=OC0Xv(M5R7YNdlS$B z^WSqt2V7|a&BH_p3sz!&CIGs7x4QMZlYnuc>e~Ae6FgJej{DqElW-EhgpqvX5E~47foP?N|P(ODEJ0o3f z4fXqa!6mRHNE0D)Q@J2LJ~}Ei+~3L0R9E}K^{XmZ9@_9ozzX+uOuD*jTidGhe{@giv`3tI#bxbL_zo9%QCdAj-&eFj2)sx#du3x@*QBmRI#p@5B z8=6D9_q3E`#`rkfo0%EvKGRUYrFP@$WfhgH=<=s;W<{TGS7UCZhohCLv9aEB?Z@}- z-c`GG=iWn29Ro8fTW)!DR%JwZJK9>AnHcK6eE#Bg~gpmYT>!MKl)kBdj6&P1YPMS>0kpU^jv>IPPGjQ(URMM1bf|PIh*778W8cS8^~g7XopRmw-nC#x_RZ8rr00@2dZq_NX7jwu); z_6ONANK(S!k$`>8tjx`w`v!mfXJ1)ucefCJ(Yktiv<TiIMzS=v}#oL8Nh9US28?rLG|;3)>CanIY%0bxsVWm9D#uQKIUoc8++&mp4G#8Wk2K_zmE@zx zjQ03q5HP`euu;X?Nc$f8nO%S`KLItW+6^k*b++7PsF zBn)Jg)9z!s3L%AKa2&%7*jdB$+L8UqrobeWt^DN#pZ71fHz}PNcVH8cuJf7B>GfYs z@PFL@JQ6UE1dK&VGF!|e0mGigz~1Lf2&AW#q1g}4H`?BKL{$2UpT69EFg0lZu|1CT zTxv2vX7hjB|1jLy6wFS-_Wj@Of3W?V{m;&2dilTE|2z^fj|2=~Qb&ELhU|gwx31W* zX2p^vOBOC#xn-aH_4`^c^-V!->p-v4hGg5jr}l1Hvvl#Yl^gb+P`L3xOV7m0(Tz}m z=?m^C9tjwVG7U~7u;ZK+;lc#r><}ub;TT9#Fr9*mcp*NR8cmycm;*mh4d1^(0+mc5 z1fD8%fiI9gC6Yig1FlDe;NK&G(iFH=JMQo$5-5BD!i+vhDJ11`S~_mP%?ja#ULyTG z60ob0)`Q2I?#Zc{**Up+`T4oXp24FTY7PJJwx>43$5!v@!-tQ|qY_dxva_>ua&pK6 z)-%8(0ZY0;@JPTU7oepx)CnF5m?9JudxZOuM*`-NfZ3r56&iVUl2jF{|K+-r?xJcF_H~=TmKQIV%>dhkRYI?b8 z`J!1jjWKO>*eK36wb%ZBLOoL znAy+pfWb#c18eGOH1qu0e1EaT~s zdD7KUSKib+(3urzTi$MK*4W=KhJ^uN2BV5a4UKMoO&tYU2@Yyk4;%Jk(IdV{J`?hd zh}zn!V~V|O6Vq)puP7Wk`Pw_E8BH5_B;e>cgt$BNgRERU%r&oW_47BoadO+vL;IHQ z@%OjUxu6#r9gBxA@G{Z3XJDbJQRHX$QvTH51ABH~jSjW5QhOL05rxmEKFR*EMT(EL zSB{^(ky_`34$tvi*KuD#-sfHN>M zpnkoEyvomKa<5M(vi-@bq~I?K%=&pxU1Q0OJ!**{p{ z07{WzY6;W#sjY@&5I2tm%#isYAB2K9A5TML9c!C{RI_W(0x#r1tX_m1rT; z5O1idc-7U@!qn0JzJ>8^<>N0rEnoZP%%dk)K;+_mPqx7lOO(5UF>*d&p#JR!g(&(kd4 z&hY+)3)e65NWh!7sa#W%Q+cXmWMSt5pzn+jS4&%ehev0XE-M~7a_G?ayH8wEIDYZT zOCt+=R~!$G#To7)!MbY7YF88$l`ft;drskk>}l0!`sTJypmzv6a=e2=jPKvRb?f%s zd-v}@c=Y6+>RlaU$`K-ZM`u$(VVtS1gQf1P*M>$IzyPtZwsUZDap#eMIn5kyIcV-s z;-TaSg=j`xt=vjk56z_3Q0_O&POkMCl1 zE;<1UA%TlU&S&m*^!D*cz+?Zuh(`iO4xnkLxa|1&zx`$YuE~Grk${mqgyNKvf?O^G zF%5lC{{p0545kKy1@2wJOKt%uW`;P;I1LBs(@~W!DK+n*Wdlg zO3%Q+yH6k9cf{HOsv8VgJ9H{=P9Sj?`Wk`Z=N|@=Y)xr(p@5fG*9ZeOJ{lPM`0;I# zl>x17?m9X@XwXr@BLQQBV*~L>z>r8h5^w_nfimYbzPGhD)V1eF7KNC~tyg*le-nr# zW$B!doD>kxS;2*VDksm}LFFVa#onVGN<56OxxFzVCRX#T{AFCpuEHLsQ-G!*o)&Ri zcDBpi^B35^*d_*pgiH)=wUyP;56+z-I(|*NoTbWh{9veWY01}=m!tR1E+Y>7%m&=) zNSUpzu$4PS?~Zsr4_LGGj@z1Q1W~g4s9247h6gi->5pn_s!NaDzlm&Jx`OfGmCR!l zuTcG3UFPjwujt=66cMW)9;DV{G&RC3qGI@7TdqP zloN{3z#$g#py2hw-SJ-wl8vd@fTSj;pwr`(oc~BxMuiL04?t%v)H&e`ryElsKA-T28)w^{drL7C^1y%E1q3EUTWC9{LFw{dW?p; z{M+Ae96g3~0%m_o7?@X??cSRBoy$j0lV}aVya5A*1cja`S-%OGo$mWKzW|*Z)YF~~ zc!p^-)K-?|W*3lw$}x}|j^qb*$)x*p&N{m7FbCUSoPa>R0FMM*EGXoWfC0T&*ZA)D zUw{7i{!O2_1z58&p+UZ09xhIf{?soTZpg;wPyhJ&7aj??v$?iBFCi?z+r!<>-POfZ zkC1$8(E_IhCP05*cY9MciZ#Q4gY4nq=5C{dQa@uew1nW1fMG~u00)$VRi*4;g4!-d zmkfv@vb14q(+e2s3mtHX0dxU$1nRi~j8KM&OW&YaFkDe$AUgd9O*v>LZg3pj449n_ zz#+4gh}wFHJ~B;k51L^3nra>i*z&o$^6BIIw`^Fy4r9aCyP4=Eg%6~x{r*yR;fHI?B$_BQ4(?yFst{eJtV)vH&p0e!>PgE}@g zcDTHzD#FRu?A6mdD$25Zwys|dB;Pgb)@|IeUlWdS+`pZeU<; zZKkiIjq&2;D?I^iA{xL8%|+v`w4{XSU>{cp8!HQQb8`zz0WpB1un2KI)DEX6CB()= zhEfTYtBZ@XGhIpyMTHdiLuZ&YG!ILNkBJB%JOXcTnw+B-6tWv+FpvQr2^e@!B|tjn zBkyXoP&y*VX&DWzxfBiMu=-jTTCc1#p>v9#hZ&^2crVMD`ur@f~NT~scyriUv zNj)A3m`4K6$&YH7P(I7+rr7}$#Ky3qV8<5w?*Gq;!#aXz0H)0Kb#4-C#O_GW6yR7=~zYQ{XNGgFWO|M*qk`0sv*> z-G}!d-Z2*PAos%cz}}yP`u<~AO_XFgvCndROOi|!AvR9Tnvwhc^Wjqovj|5B! zJLD!rs&HK`Z3s4iKPxF;q4FAu8?mvGaBjb()0*YtY;Z)ttw%VyGzhN{*Tc<9F%GU& zk4FOLk$|y$2HUOlU!LE)XXlC~bEZw1A~R|7lqpk|hi0%cAkYWI1`qF_*|c@TLLdQ6 zoicgyl&MpvtoDnFiBC#R$I|K_vb=P4$KiF0mMoq!ZR#|zOr19Ah<9LQY?s^KMuo=h^Nr3 z7nANFp_AYsm{aaFX6}A&Ha1shCmq&=Anda|ii-i4=lJ`M>5B(@)*F*j>5l4ChH!r)x@caxbmJ^eGbT4`z z;vT%x!@`Ah+S3?%`}XadetKYdBE$vfYa8a~s5}zzy7_Zv$V`@*G)ZRiWSJF?zCoc8 zF>&;K-dJ3`y8Y0a1q&BWo;rC7SSC-A+H2$F6&M;3O&Z-$(?gZRJ6128HG3w>2bn38 zC(Thav~}|f4UYi1f8brg^|J?;FIzDC+iBAv941X!BL7t1+SwCOoUB7$|6m@E1k9E; zEm;IGn0`Rb=&LyxVZ73^Wh)--@z6P(?SJ&E9hMKnyNlu!NNwl#KjIL>@_}|7w<8fn zf#yKz?-Z!`JB1)HBhs0!$YLb4V<9?tB;eC>vgd9EqNO|rg=o8aKMubC zsXETh{PnY&7fzixC3{lticK*_Nl7u2#sghL{X7!za~=s88U^#%qg0?2?yuq^q_}bU z6j5jhCrm$r1TGd;rFd$qDk}0wz+=7ac<{i+C9Ad^QhE4P>jlbvOss5dS&Ab$^x7K*#id0_zOIhWE@-mu=;Y++ zB|r6fYqz zGBPrX_7Jpr%Hd;3;FR|cs39Quq^2dsCnVAy1OqY%sVKD`i9-mqSC#=Nh(`j3gQcpH zj0Tb|;=ZB&fv%Run%r0qx1>5kZ6bOFql>$Hhd%xI>3x4!XI)x^y^fwsX&drfYXByK z4$x?W)BEA4fBf>phoRmM=-I|kwX`j&kR%BOyn-;Ox_gFx`|Te;{qT0Et3eQEqx)uNzWL?%fBy0D&0t?kUbMTp*1g+`=T##M(J3}3Tin%) zMw-9=>yQ8S+xtOrZGJS51bpw|{b$A&Hjb{I-rl}ARM04zf=KO|Uit>+Ha1Q?5-^Vh z%p(E6_Vo({6c97SIwD-tL;TFoA3d^T{*=km($bS>uhw;TaRZhgrO>wv!*zA;-M_wZ z^_I0Wr6&QabL!l!dO+I40Y~RgM`yO??d$S;S1esNS86=4LZzn9JAe{gTSpgH%AiL( zcI!LW<#%sbHb-W>)P%8PC(T@PUi%dooLpIAd`ny26ZxZ?mn@k#Nopd-jD=fnJ$-3t zZf);Ox$$s$wM8lI+Oc-&tZ8UeGHK?r^$HI)Um2NO+1k^|M-B3uf@C-GNWeJ#sW1SJ zAK3PkF_cF6&MXvwZ~`IyF@!s!GmhMBi82j+8lA75_ehlpn4zk&x)$nA*+`2(j)!9)-)0rj%Sv$}JD|BkS}aEGMoUX0 z-f49eY9NR{Oz!D!5D3zfikdrs*hu9G=$Af>mWo#Tt9;J zi>CknH&373w|V2bCG!{aNWfClmOiz2boKD|L9a#ZU~iiUH|yKmS1jU@fPLOnf@XH0|K=%~8^OG7=drI8eto0b5e(6G>u z;GjT%KZN<&48W{uz?!cFxLjdQdJ0fLqa!22!^0Sz^m|klkVhBbc95=c%;h{u%_44}6G&7YQiC7S!v{?E(KOixV)P2%gv^aOzi z2KbI>{}&=-F#T%|KM$89oG7X5A&^Tp2XS!~4eXDgh$NeUK=Qa0;o@ROk-RVlmNNW# zf-mWfXhTwutxq6+lmSd)h@(s11$}F%V_CuM77{uYP0(L~PXUjEga=ZN=}lq+x}K3i z!6yZVM#5tyLy72u5i*;C%|Jy^5~R;!hV)A$vZ2~2xQZFbIN*_h!y+T1qGRx2cqCvR z33#}$5JCo<7Y8p>A+SG*fI;$uwm(V>u{CM)(&ikdvrSHH#K0!N_U9PbwGg2k3!4E9 z)TEqaps9!VKglQtS4_K`wm)vc=(K^^{+GN5+W%kxU4Z#H@xXZ44A6o&J&2G%alM32 zqLc(Jj|6<~+!?u3SKNI30)j&78|oV(a=JTeaufWW&GcS8yrl$;uCr&4D_Gh)qRUNP zLuFxjW@l4jOq92+k%7h?)r)7(0mh3*0uBuDhmWrYhb}|ohCWUGw~CAMv*EgnVP3${ z&=4#|#(@{+5u#-N{Ot6Ur1;pFC>{xzM*=4C$0Gqh*t6=}g;OVun>cZT^o-f_*6u%j z@#aIVSB7SFs4asu&wsXi(Y*PyzMVC5&iqBow;htbc%7X1uMLTgDy53bR9L*ITfvzo5KsspH8~+R zItnT|25G!ixChSOvJyd&0AE0gPJ@L2QZ`z!59>TWE;=mG&ks$IDTfgZs3SooN-uKaTIy@iMGHJhNmObF zsLvjxkb*ck(1)TBYH>jvMFqLpS(z9anLH9ONf7cQe$FHy5yB$@^GLuv5-^VhOqssI zZjzl45hGFpX-TY{p}iG!ad#i!SBQZPCP=u4`5d zA7B5#U@9a5nSx`4(pyteoR^Ud-+fGMY#c^B70(cz<=pc~z)(_QI&d)w7BBb$>623s znC*ceB3z2VJpTWO1j-`;yLk8f@t;45vLd2$ODd}C(C-FKQFkAzsfATJ;Z|mrmae^T{^R%VMxm&>I48ZZwy3V9qid*F z+*q8K$s+-iicd8&_@9-miAC-3Ave|$#4h#aK#py0JBo6-fn+fevh^s$L;-Gi(W!Yy zdk2NMF$bN02!OK?6f8#yQBj}{2^K8I&f@GSnjvrs7No}%u=YVGhGEkj2-nCpWI-_a zHL5B~OQ>%lSa2a3?6mWEBw#o<$hXmyf5kM9o<8^Z=?9Y41TRRBsGy^Md==M8qW^FBQtQ1p` zC22owP#Gu&*d88rwy|ywwT&un>B7QjZ5yf9ptOkASiLJoHa2yQ9T{Hcfu+@zRkig^ zz~RPcjWyZXoNc9l=N^v)OplTZ+;}8l3UAT=XMsAn8+jz)oC`+TZ2!V}H?sRknM2M+ zYjbPaPMK$ZMDIe+5{}LUEu~5!YS>aMyXdYlyMAPYGf7O1D8bQ|zw^{nJ!A;ua2UQG zvyiAJb+B3Fxa`^M*W%{VE@~+9#r`K13GUUhb4x7@b(=JI-3zVj5#owgYQ{^B6Nsnw z8viTXS83?$@<_lu5-@cQVT%UJ1BKEhk@!jqlEb{lpa#Ft0-$~no!SpjQ~>%x>1bMy z#KJ9brmE06#*QImZ&DF1=o}M%AYF$x12Hh&f++NRZqP)G13&Ocz$7T3U7&=ZukTH< zmzj5Xu)n>vxpOtjqoJv>tO-0Wq7U@iW(?Hir^mZk85)G4&21yX167FquVZ7;pzf$K_(TVgv{(Q~a#-pFGiv&CbaO$^i}T`k@-T-Vgn) z*^xXFaBDemFfy~Ua|sDTP>izj?(W_J4D#|1p!ZWpE6V`mx?+~21O)9Vda0KI4IT*? z8Wlwd$*iC+k`E+6B>SIJZ#mJxxJFWVhb083-_o^ElK%}7IJFMIAbW+5LCIzK#$*#$ z07&nFRBU_=({adRbY!wtN|*xlHqx3oIvv4O4AO0@XGE|Q9GwIr(TOVVY7w_qd0jeX zAnqQfQyg@J+@BnhmFcE-biZkbnBAXZq%AxWFg_Q0fZeUH*7Hcfl7+$g^KjX+us_Id zW0oVRw0W?qcqCvb(3EvMvVXaKO0OhPn1=k{=xqPlp_l#J1Z+YJ11x4Y?CmuGM8p*^3QT{1Wf&V*;eKLJQ|Idl;R2VQMyV2;lyv4qyL+&bW9aU^1qAJ9#8vy|nC{?Cczo zur$`sHQU8B%v$%B++|gTO}oy@9=P}X&Xb6w)Xc0*k+>zrKcy+c%1Y;sqLPKL@y&g5 zvU?AnyLQhnDn2y~oekGXc^!Jw7<`+N(<& zyL&rJLd}Yr40X{xk*o*!L+SIu2UJ&UY+u`$nvw0KAh+dtpBPueiw9o`ySlZtA|%)H zWpJdi`f=Iqdv!eg8k*rnAvX#x=aGOtLn1wlZ*K?>Ho10u+pZ(~=In?Fws?H{v74ti z9)4Afn}L>{+w;dY!45A@9o>6q?=IEU5KDa(Rb=7e`F9is>Dd=VI(U=@+UuV=e)#b2 z^D5TBN>RD7kRa9(3H3TZ`#0<%fVidljpI>e9|A;Xw+Z&MT#gVSVS z80+SQ5eqox$!&Vpn?0t!&@>Fq zmV191>U`5ynD`eXe+cRDgP#UE-ZTYRTUoUsg@wdAE~PU7u0ZA~ z|M*k4NR;kkX9cSoCkClmXom!PcMt2!%p(EgaIC8=E+j8lYdc)%G&uh`^AXbFTT@k9 z!suM^cR0{T#i~R`tBvwfoU-IfgTI@6;uPkm=roa0O^>o8`VFgaXU8$9R7#JQ1ki1% z8n+DB(IC$x^N_GpAC)% zp^aLJ3|~)mPq>^GM{9FcNwRm4mv4M=Qyb_bNZ@UVF$hI1HBHqS5&l7TX15<(hL@8d z;|l#NaJjI*r>U@{BrQD1(cSg2isBvPpd0`v2}(*4A;avwKfD!{7p6tVq=W@K7``^s zdZMM{pTUAag#sQ480}hkBw$+NY*6F~DPY(OJQ6V33$OkSWPVd~Y=fL5k$4{SA zx^du(av=Q+CnCFeTBPXSh8F z@CT6uotr}qL5N&QMaq(LW=R7NWJ%}cP$nX-#Cv7c&UjA{JJcVOGNgGV;7lF~xVgDq z)c^k7P>;B!t~@(FEXc>h%^7}ndn030Gs~KWrlwYr7*K3IJQ6Sz^+L}QHro{&aSm-@bQ<6Qfee0(6 z>o#oMZC?d?0VEq9d{#z6h=+~dlUo-~?cTC|BLVqtkxMUwfFpWMNoHPtgo~yAgDaRG zP`w1?yJeS634%Q&Q)>!yiUl!VJQDCFxjmcKuK5l~!|S(hKcss1p{8~j2VU)Cp!-No zMM3VsHsBzyS-WZb&fRCPsom4iDy5^TtXyFK^oi=_bEgjM*s^iM`mMWm?>Tz$%1!mh zn&^l}M_XCG<6||Y^Cu4M+`47c)*ZX|A33Ld?e_gA&*=OElx|6hAn*m|KfG`Eo;`cN zKXU54vg+;ok2SR=hb4}~;!O9aSLBWyIdtsIrK{IfZ>gir$g>yRSqd^v!vY=&n06Cs z0+gSOA}B#Ax|dM35}p6`l)YTnfLJJ+rcjGe&`XHUMqP86-+lShCl$5)i;-VJ&JV;x zN9YX&dV6O}jTt>^)Hf5?JxFP+sX;qLd~U!gr^_4TkIkMrebU%(MvWfz_1B}98`bbg zz)NiXeEdpE%d2u#PAJ|uw0-WRabN%aZ-4tMzW)CAZ@w8jTg$=DzND<8GEaWb>a`o@ zPM-M9SAWCc=%dEXy7gM$u(Z6QyhM3Dj|9vk0TUn}2B7^I_8*HS@<_mFF~cJPzbkog zMRxuARm&F5pU)!!lbf6jd8!xYk$?dN${~R=I8gc*;gV8PKBXFT0%sIh#~h8lG6vMm zaO4jB4j`alAgoZ152%Tl5_BqL0-dB14T1z8WGn=)2s*_^N616(I?F2s)m=kFV4)c( zk)fllkAoN;d|Pd+v}gOet%vWV4ZQ6mI*`H<5dj?`uQ&aXD*Fy@Uc6-P%;^hn#&r+S z-@pKyaXHA1W@io`S+{ZV+}X3HPL-K6&zJYl>^lI%V(y%2 zK=PbCZQ3N+VrW=UZ9(UefB}a|$B_{3v6|9??2HURBxPkI!#6h<%ZsfSgemcPQDGL^ z&zF`!VW%M|A~O55*?Qs}aD))UD(s-$6O9dmmZictipXIa2J;K&)-W0GfZhTa5F=w@ zG#&|zZ5yEBQO}fzyxr5s`ZC&{7 zl10Gj1;@h3S|pgm!hv)d?D5uncy{mpEo;}znKfmSwDhE@Q>BjQ73AmV<>iAu)M;b# zT4DFj?aLO<0uH#e6mB82$R{BoDJdn5uJ1B>@>qVuW*!Nc^79*E8N#=nPfss5H-|fU zNCZIMQ|Trg@DLH4c?zSR>*Jr0_dTSSCN14C(5k{SO8fkY}^NTmB`T z9e6wvFo^{OggA-JBLU+<^nG~y)30@D-Zm!s_b#3}#UlZqPBph=(&WehJIyQS<<2YJN)d6%P)*bUwfl!3fBvDju`oW=+v3UP^Kxe| zDx1~gs38rXDiHdA`1I@VJ$2cU{+=cemCl}#lT&z@MsO%NAVB7kfV*021Zkm;IuGyM zR62d+$gxujYTB<&EbV~xM<+#Bx3DTZDbztvL;d#U(}#|nlvBRg8l4KH0&m#fjsUTX>`-9iD1DjSY|8~-ZaZ*#JPG4&A& zkbsk&`4tur61XKh6DdZpfjHMBgBM^qfV3SUCh#9~TTzlR`7Q~5pt=hCdSpvyu3pzSw)sz5bAtkSo5|QD`;gNvT(_ucq#{c!7fBf;=$G-OJoM=x&ty|YF zDX6$bN5{s+Cy2Yq$MEw%{`K3>A4E-+dExdtcdjU3ymZ|qG&C$CJVMk%^iMzk^yyuH zV}&5u*HrWRC5&6nekei;4HdCQ`0sxF^r5e_rZ_XwS@)5Og5sr%4;);*0)j(AMbtrO z=*@?Z@A^8b^HT!sbRS(lcV6-Gb6ZDOFaIE-_w^0EdHc3s)L4`m=WVWeSLOVLi#H7| z9b7zp{DZr?-~fj2cBrSNrXb1RPVb?r(j^tuXC^2B^z`=i$C&{a@SCBb-pcG0Kl4|b z8V`6RV00*;1Uh8sWpNb%Au5@k&`?(gIl<6F z(1nUS(}y`&ia$E^5(A|V4R3xr$T0`iA<*Uq>ZpYN#s23Q=$%0IW%TN*FZVwVf4JRA z?WPInddhJe)*HIJ8-WCxQrs$p>zs6F&`Ex9ba8i%k;>71TlOkH_o(P>;pq5FsR<-T zmoVhn)d$zkoRvQze@xDzlt%*Qk$}xDZEWq4am@BcV?$F-W_)^PLZFM4xjA7BJ370f z+c)+PW1#~LC@zBh^w{vA0Dm@s^A#YW!<=KvbqO-;(vtw;866cB840LQqGKR!7ciS_ z=LrbsGdVFnE-p4U20)7(6@Ybw!%3}up_}LDWTvGiCjx1Ny*n}%B^Y*s0@)G?JbeaeYgPQ_^-P=32K3(hZ=H;^{OOKy0UW!KoMs}dK zj;@|QVAdGUBwlK>ld|$scqCvhjUO%oHdsjkl@)M>1x#tD;08<52M0&uUfHP&{Q`() zEW?i`_>xXJemoLza&SOWbuIFWp@j*GpzJimjWY1_FF(8;5H{Bqr=sdFP?oOFn8e06@{!DgNoVpFX`G>}svADnd50 ztCN$BLtHL^;L}oZfOH7^e*g7{_iuY!o2yH+lVkkc9PMnaJP6DxDG3#%;_eT>|NQaI zkg%b?xF9Vd)YI9~*2=<$M*{Zp^71B}Sky>kkl|WeRRWxkI6!@e0>v*VFffSW%%Vsa z4q@oEFj-3ra~Y0b4ATH28X8+znJ%(^D7;9vH*|!I)Z`>;_7fA+(9leEzSQ!Lj9yFt zFmM4B0Honsj zG>ie^1s(~QpdeTdGa(J5sT)3^hT6jP=s;In9gUl}?MmzZOm9K!*{a-(Fi!_V4UKDx zmtN#m!-y*?%+JT=xW28eskuBiDa6IqRP)|-1qFo%35B`T4JsYaA0C@dVM~2^R!o4K zqw&-GR~61Hp1J6jmXrvvI-<9>iN$Tz#c4sl_7*Q5-ceQnRPX5r(UDQn(b4q&ifg*W zt!4S)p3Vk(kJYa#p5u{#4GfJ<%`L5M?Q3Dm*1_JzxrL2Wl$RD87U1LQ?&gZ&1_Pmn zOkFULyNZfJ3bNDUqr$^NARKrkV2B7N+#s8%_W+4NJS~>uKb%5EeG4GDn3@4&i2R16 zKtPB9$(vm-(a>lP(m|NtuMz`#3K(#HG6jw+jpURRavY+zhM}>L)W!aj3^oIEZB$oM zPqkt?f61XqI~1kg$k1wOY+$}cQgU!Uqo$JPhk$Kw5ybg=2E-M#w~=oVbO`FA7EB3x zQ(Hs$on2d&Eu6Pxqk3$8JrY0&=tWROh(>q|ZMlz+u3Rv4>O`rrV<*m4D{2^~v&$RX z(o|3GU9oDK^u#e=j~zQk>S%f`yL&-C(Rn1`3LXgxpb>c^^A?Pf|z)gT=!%dC24nAn)2M-PQi`wf-^9w6mgj`Tm zLhtVDANug|&0v4Gu(?!_nVy!EUDY8*yLjfP?BbDt|MQ=iOenwzkfh~e{~P6F`rGm`};I!%fmLG(?+h(i?VFRYmgUC$`A zDJ8p-b}l#w(1yAZvHeOf81U5(bD?hmgFK@?`;-MUytRpIWP9WCCi3rt%CXC;RPxY^lwB-MzzSi@$bHz2Ce`hC6N66%wJ zXH4X#azT20bW~`#zmuJ*uJ(iLS5>Y&wBeC}c_d&S2^brjM*>#AdO=?9w5;6mJ=@k3 z5bvS|i7CJ_wC)gXUERn+qP`k zx_R@iV+yM3PhRL5vjA~dUA)ypr zM;GDbEfO#ZfD2FVGX`oBk)=*WN73k#T?jP#w?Bwq47Zes5 zN!?8;i;2(}+v}?ZaH^)nMMgvc%R7!5@gpsbdYASBU<&4AJGJXCDauEA5P(8>Bw$Gj z5V!wfBv1;_|2zA?BRRX0B>>@&;0CGwRHnrv0T+8czjf~VOAFE4PaV;rzS`M0cc-UndEG6=kzHhO=FSxZFiD)OoF?W!3#?tOW&x3%(C2+Kr;iq>nG(^o)`j0 zuPf8kAvrZB$jiyaJ32Tb%*WNhSoiTQMa7FZH37TU(^#96l9^H9;TT|UXX)*1_R3L5 zPxa~*m221Ty)x?*b&0Dh!g3@0jcfxP^i3_S?%sO%_`zj`D>ts+eQ9C^SmUnl&W5}o zyH~;1&-JZeYpPwjfA6fE>MgYgI>r{(Fg{@biW&>!q9Q$C*w`37(@ZZ`!Ois#>DzDEljikDfZPYwPCk&mO;c<@(KgSLOGwTfJ)DjQQJCpLB}TtZse3 z|E#?1iDL&2oj5Oh=Jerx8#b<4GH>?0rAKc*?GSeOYF@jjbn(iOV<&fizx&Ys?dx`} zT|H;k>;+4=D?WIR{QS0vCnr@-9ND(#EdN8H|#wD6< z+Rt9THZ(S~fcFqkKEa^`bqU>-+`WhdHPn<9X2TZ*pdO64`1p8+vq21P$N)ezy}qWR zR6y-9p-7~qrKNKXw8Lyw^7_+&uYq=Sx6ZU7*?LvM!O^wwrXcv$IZnt6o8;QEx* z%xt3fLV+I`8XQ0am$u?KFB>cO;OO|IWWYLMgF$~F1)l-^4g$!RM*@bx0Gd7E?jerq zNi@;;?BXY3fxl`Y)6G0J%R~QQ2H!?YwD|CP+zdj>yc-&(F!q z&MiO@Aklk7W~L@;+vduQA3J`+#Q7%Sk#UL1*a7JT^oRL-tDoBjKVB^@HEzt>Qn3J^e$mYN(wX26>-a zQnmLjp0oYlOH&&MCpX{V$T(V65(e6NJQ6V3Ae8jR42{~lG3-=lCI%BzsJW#RnjP_E z^fVHk3=BvyNwl35Wc-d|;7}tNop=Cf3KuD90#JYP$cs_J4~7vsT@9t1k_Dh&cQrn2 zWps8aT}>4Fw1_Sg4W_>8`GQU)qC&4mnJ|w8>}LInM*_BR_YDs16t)UNoD71a-L0>^ z_OMoyKe&0*c9k2~6z`kZx_AeI-jWjNY-|?f`sBh5^*i$NCypH0D|=Jr>@5>(7w;e- z`3hSE2~OU=M(Q^oJ-l;U{g%q5E7vcczwpG|(K8@~=;HQt4{t9MZ7r>5+AnqW^z`(N zUOaqmsJwF6`QEVW$0H=k|SD z*D7APVd>(5K3aID?P&pJg~1N5_Uyc+b^F2j)tfebH~WmX=B@hi?yJ{^Mi?fhrWV$A4o)uaUZnry>}+cg{>-$Lq=Yyig~ullRygxO zGWJpy zM8d9S!|!)mynMWJ^i-3_OQ%Xp%{s7jyk$u_3K-BNUnuI1IyHaNsuM~JCM?{#ZOz%$ zGBXzMl3mXu0Z$mS(%8%jdSu$&rGML_v3dMX)0LzC`q#gGGv=!ala{R>KWf@pePcuj zTLTWw_`AXFDSw%Abk0{_jT`sPsIR3bjGMh^yYiEl1}2^2s#kNq`s@A;qyF;O`E$pO zoi^@ofBk9-nj!H>z)3WivJU+nfq6s*s06U0qyXii6rLcw@V}61Pc90k^fMC)y8_wV ztS13V7GVmIjA8>kWJ8e+A%W^-^kFjT_6(}*&vd#Rhcr$K+ytNZ=j*9nnDZfa^}ikL z>!_+NEv{%F&!nV@5IiXC?)@J>etO&0QrA!-h>A@stV0wNe+sA-6-58=`;YJui(4AY ztEv(MJwsFS8TbnTV9H8C9{A5cejY@Ud7H4Mxh#)I0_Kr`MLlSb-urf-QU4Ate-_dq zvlW9K7-EDCx<$g4%A04%Hl|L3%&vttM0zOsT+!yewZibU8Kcw6XBXnT#10k;+nZ~0 zbMncV&T5E>gh88uQt`<+=8=GTBw*9S@)7Pv2A1xA0l;nZ@`*?hgeSWAS=n2uozik~ zaXq49XY1%0kx_`Qtq2-tr6d;BH$(=cdpK&{lGAZ=Qha3M<{Mj7U58>`O75sGFDz+E z^-Ik1vb%ZuyqTS|XLM3}aa|KtnbSbwQcZP}Aks6$=k~sX8s-J1JQ6T>2GTi5CmaTk z1l$CJgT*i)@|durLw@A^M=}J=4TLyYmq0W)C#gn`KZ(};g?>PCQ;0M!VM}#!QAu4p zTKSFW4|Iex_pG=*BjEpI?>)n+TC%m#-rF1rV%Um`Vvd+|&Z2-|7R9V6qF@#fiIQ{9 zIZDns=NuPtkp)aUb~=5(@4N4)xxn3h&V8Qy++X*{tZo*#MpezV=bEEx)Cliuu5(-I z!lehcDUIzS?<9+$5Olh*I>5@o@ZQBUXZCEAU8`(cgoD=*? zhgZwYU3}CqsUSDMu$U-F$@ZvC_tJZO{rtHrXOxfb+_rtyl4S=il2TIBGjs9^kwMm0 z>~L-0k<(`s70+I}s(f1^1&FBzzraPTGacK7uQ2buGa{l;vmE&uc zuasG=z%v2Q6q7uT>>6et(%wcy8v;a@@oao}dsSuMJTb9V^zBo_wh4I$)olt;XI z?)JsyGsJ#Effkc91Ch)}o(UKRFjSG$6ObP1TA#_Gb1gb-g(H}@#W0ky&r6%POX_g{Yg zGSuIJQjvJ1)OxtNxW*A^L^jX}s%r#){Pp)=zJ40&>!>eFi-->K^>TA|@yy3jjN~=- zegFL9_n$uxee7weF3w7f4Ds`FcX4%&Edb63&jidf0pkg(XHXOMgaK5TlbM2NEWp>t z%hSUhPA2w<0*wTKKRgrgS^2|(|TS{~NX*dGG1zSzb|9QyK1KZ)5)E(cMdm2X|~< zyLRn5$Tw~~tZQRqho`TmD#FRuOz)ZcHC4sE+cvCS4W?c>xlKC{JclE^f>_>@QPgj! z_27o8{Qm87YgVmVvu^!{tvltP=^7Xl1$9*kQJg$`bmyx4zAdQWTfKJe`c2z+t3G-5 zT8|O^RwY@P8frhf!7~A;rzFP5L`Q}Oc)Pp0y0|zy6B;~x%>eZ-Ku$3-kAW~6A0HPT z5#ZAu%B;G&m4IMbHms$b(i2gvsUQfY-rS#)0@1;m}EP zh8O@v0T2n%_QH!;4uWMUS5ZkxO;rt0byyd+&*BA^iwFh~6M%Mvr1(V$WUTQ_z+=Xa zoiKS#QW;>rpdD2;5s%FrG>&dxDL!fZ__1St96M&r*zr>&^@_7H)9LG|ae3k5`AlWo zB8iFP#*O=N%vg*cH*u;$YGPb$MQK@Om8Gdii2kXybEc1*Fa~4sGiKcQ$(utyM1OVcB`JrDjM<&73n&X3hSSO6OE>-c<)%U;!Ec8S`TFPRhy7UnsLw zZqLaxXU_wy{Omf>@D*+`*Y2az+3QG51G5dh}5TOn~D(jVBSMd2@dgS1@=u zDU^>(u=hCOpL+Bnsg!2u?4bFFhW@hy_&dSOM($WhK(>nxfP)eBqt5|)JPLjY@%e}V zl(o-azkL17rb0^i$3CPfvCk)xe)$H%jq-+mK*q4o)DPx2TmjkZH2u)$I+xog4(&Ri z_$2Gow;^1C=I7+-KKwEMuDrtDb#kkgEL!_0dEgV1;{&wuOu+aCcqZThBCDnF0zK1Q z9F4dvcqU-Ry35fmvRB5e$}<7;Ou$ZF0iofMQMBCvzpcN+@6w)CE0-==vr7e~fyUNO zo>YVo6$?2%LZAx~WCyx9c!o!Y`uq6!qrN{nE+IK3l^!bEu-JaaGXZl6zKB0EFC#Ew zX}2btQ*JkA?*7sJpB#&{SrdF7ld~-y0Rd5u4!9m^yN4Y6IgACiqvM6Tup7?F@$E3H zLSz(R5o8mv-ZVeQ1WugEBR%jNQ_@;V^TU83zjP84c+c>#=prO<7m)3OHu$;P`ohjF zYc?Icn=&xS!3kpi)@DvV+#jL3=io+}#q(!NF1Q&b?87JEfCAa@;Ra)cLxy- zPcvprmpWBEASBvpq*<`Zc_!f8X9`PJtXVK;&YW2@rPm(2@kGbO+R@F!%a88wK!0z4 zhwp`*E0)VHUbREv_M@k--@UhXaP@$mLyo5dUuthlZm^q^PecSj09`$(fH#cFoS5ZG z3j`Gdf@G$lERWE!VFah3C8v;WOOH4UWARMD2s7ZxWew%WsGjDE{3w6}bC+Te^yf$i zL?lM#Bx6w^_M3G9?>W_Qk2ErVSi?nFypR<*k4j0zU6?60{>Js%cqU-n2%ZVJEcJtz z<;$BFl$GR<95{50X95OSd2ViYW@Z+pAfZue9#Z#^0zwWvf~Y3H1`hq46k^0)89o5d z1gsg*+D(QKGlRHQiDv@tZAuSvvNqOIJ9FyvsZ+rJ@ecxxABX^9YxN8dcC|Mo``DW3 zJXBRYaq`sZGgqDhMJoWA>P+6-)z(-N^(7E`!1U*bO#vITprIl?(p67e{pQ8fCyt*ub?(L+V{~x#@TUE! zhi3vN01)C5s|Gj-$j0EwOHV^IATf#Q_jmnAh>#Ib5X1=F{i!Mcq5rH*0RDaW?|CL* z%EM&(-`$)U;qm@~!ojU8WaiFUyjHWJ3w8-BVr2GCTR}#+yWx#PyEm_xH&blJoRx3d zVefpGR~MAz1-`npf9tx%vn9l)&z9K|(oN~oNM=Urb60zDVR@13!?U|Lu37-7|LIaP zOK#QD*9is*dj6a8^IAUo+>_t7ZuNXAF;w}_n71#owt~J%1_Hn{0b5*C+PzLza>}GB z6DCZYDh~hrZ4GTO{aRQ9ey6RyEBn^5J)31`Nq|RT!bGvzJQFa8f{;D}E}?(df1U}r zu_hQW=@b@g3JTTzC9GM~yWBy|I4=Id{N z|KpdhLw)Vlv2G^MUc9oXLXso`niay%E&zoO{q1jm|LvDgL){HUVK#4{J$dvfsD(&Z zE6W(d$H1?U|Ndogu&X}Z#puOTb+zk;mEaOVo)Y9h5c=b9fBz?lz&^H@CU}~?)O?_N zNi&0pzzXw$&;cUcKmPI0e}RgiUyvK+ZvH~!{>5{*A`7s@XJ>VF_YDjV{q?{8^?&{C z%V1Y+UX;K2%ZGO_oxSRtmXeanGXcXH1rSdPBDH0B85o+|*f`mn7@Gs(*Ui)0$JdVz zo#@d|5uvK$va-CSaDO5I^Yth5CKv_|8ac3#%h)QYLyC1qVq7#>J|iO{BJhd@o~EGl zG5iOp3aDbLNJ_Q!Vt_$O-4&fM1cW27~OOu$Gy;F*9a zFSIQtvaBiJ<-tXTovRnko+%+IF?$uy1k5u5BWH-MOw0?{252yF4zl!PWcwg;L{3)edH!iE*y2mpC7Z)I7Fl`*i!ADg) zM6$=y0V#WyTm-C9L9eJ$-2r9uAuWyMT!#@M{bn5~RZx@yNE7^7&NBhG)>UNlOu$YK z4vuyjnJYOaz=AilV@eKgi6FE@^``Q~yGSZQ` z=Iia|;ri~4k+}_!dcD0MZ-aH+)zeyCkO2h$;Gh6schh$!mNxcIuI`>*C|9EkbaV-u zN^?`=qoTrsJghCOZR{Kzokj9y*x(%KiL!6Do@{qC-7QOy6kUQM+{h z?70gU&#S*Nvaqpt1kG+kLve0#YNWU0yVu(4w^grPJb(G}`Rfm#y*0J6cOo8MA}dY^ zb+^`i^W*^t1g>6JJ9p{!qZe-p=byeV}sp1pZzVs2pt zIV19|D~)xvGI;&$nYNb3ef39LFJ9{bW5nEYL=ISq^1QUzFkcsIq~99wOu(oK08?%; z#SRf&WG_^`OZB(JgG)9*32FqG7^j9NoE_+FfUFi&0aTOi&P<5Wfjkp1&jc(vZOX*) zW5-XJEG{wg_`_$q#_ug*QZ_UM-%(asCpBlrw8@jEPMA1(n&jLS`!C$md};9Bsur~) zHBCGdFt%Nu37EVDF!n~=4LlQYU~c}vx4-?@KY#iBX|M+#n%bJuqJs4FNI!2E7}*Xs zHjx>F-~RZofBgDosIR$E09`7|&Pq#)^!IRfbhL*Z9FXww*MI%j-+%k`vAdw8zOu2t zBtI)T4veS{wsv+_=GOkvgWvwo|N7T&ph2!{AjeKoNls!!u$Lp|wz0Cb^$i&u`p7c@ zBS-XOKZ-&K;sQ({C54F5Gn%mUj4qVdAWac9CzLo!Rs4;0)fFX(?GT$-b{6tsiDs-H zAqJ$k0y+rJ3i!{6rk?eIhl?fMLPqJYj7>z)NPz^F=2Ju-HV(yz3Jj1~id~p$3n)58 zTQuh(Bi|b2lv5|#rD4kROu#T$C_=jn!wybU;F;8D`!;{6_u2fFPk{`2=n`e^@V9s{w_FA+neh>e|%T< z0*Fl&mCmT#Hnadun6RUvFf}6B$e*A)#cWGBI>(PnTT+oYY2U-F& zRZgEedSJ(f4Xf90I;a`Z+$$tJ7MzDjTx@jx%(3J0M-S}Zzk9>Fb!*pbI%An5Xl7vw zu-K&QTvt^-Ew6A|;n@Cto7b;dzC?EM^1Y8dGAgmi_w*IM)8d(c)h?bndGheC9XmFz zU$=7kQdwEqCCgSGQqy=P6!xYYX{%p3t8nzlft~xdZ(6f@^@?RHR;*aP{(!3bGaY&) zcqU+apt(coLr+wFnea>y#vJ+SiIm*OupQQppQU7MA0Ys zlbq9Q(G^%=f@cEu@o63UO<0*(i2KnbXl3ap$eHc`IP|$QH^tYEX99-h%QFE3s*PJ7 z2w#lY0$B*2z&uJx``Furr*UT~WXg z7=kiFfcOHt8XCf21QsD;5x|#227Wd?0O@GoAx3Fq zgXUq|tEnmn%O|-2a&w7nkNV&RJVxX~rw+(nLzJxuWjr9wr6e z4!BSBU-=*UFWUdf4FT|D=9U}T-v4d?r$FqVwr~2Jkdp<++W(jRzkb9%;0_RE#n2V# zzZV~}BQcX>0_QkFa22`+A^dYp;5-vB&jbt@d7cTF?cq!b$x{ejVhbJ_4Xg+k>mT%n z-lKCj>p&|7vni;^cQnn94di;DH^`CEMpO7WEyuu-4p`X!Cniu)2aLzk)!Hf=hF^4L zoXzPX_#3ZnxWXutAoJw)cBTWo2O=i#?8F7Q@sRRNzymE=k*>xsG_{_)CxJsZJ0~|U zF9+E(xHUs9;a@-X)~5T|>OXt@_=)+4c+lu(Wo2h)bALxgtf#rbvxn-M z;N1d7a%Of`Ru1lO??*r|efrYhoEI15WcpU?{=>I^;n9icfEkcL4qd$Oq%0b-JO5uP&1(L}P*Uo;$W+mw#WI*ba~ACS_HN(BW7C17~t zh#c>dVvrcR%QFF!Sw*G*-4?Qay1E5jEmdBZPaA?32>1siCsSfnJOH|^47a<-54>;h zg0)97N_v2tjqd3!eQ#{6G!HaCdR>Hm0I+L<=I3Vb?j9&~e{y`^av4bp@udkJl;(^q zXf%BJba9>u7*#Hz#}>>JCltP;I%bZZ{((UuVUc)T=_`PRj%vRmExDQE64S+1bS+(d z0|J6VBEa+seLxS$5&ddPdbV@HoLw)?T~L4=93GvJLXSI%(SaObg2wz1uh3AUNQ+HK zNzcq?$6JxozzAohwusQdI?v0?2ZA1Le)vD_a%7~FecMPhHOP-gDQzk7%+LXWI|9%# zfZ3m9#*>xKGXeki{hw@bIz`*{P`7fxbCT#fDSg_C2S?@qXm*W&;+gMF(QW! zqqRl#i#m|JV{|-|f-}DR;Wdp8hD=WjBrJ>({m0>_QCJ$Uy==KgCs@Eb*a8X-7wJDL z!1}`VwRP3@u3f6#+R@V9PA3eC*5djcGEtY;!6WjErvpJ!yQ8JGoxt$x@DyhwIKmpD(0TYj&){ zFYe&hQT%+1^sL!D6L4cQTnTlweN>aSj5LO8!4CO&IvFC!cR6fjwq?O;;F*AJ%^C*= zU`1gOXre<6c94cfH@~L#{LFZVyVs8z^>tDF5LQ}qGZwHeaEMh$7kk+zq}e=IyKv;x zJMW-ofOs`hAO-UFytt512U~qBo4gN(>d#d68ohdvTmv@_K_3IfqB`DQ|KT%NCsSh^ zGc6;X2e;0@bg?xHFDW4m_fBC`cC`KNJK9$M_U~V)-+HET`+~A-n6-gUMovy{L19;W zRZ6IDZ2j&65@jX4x=nm|!nN4dOQJuCxh$ zw|vO@;hBJMyj0p`psf*GRtXwH=r{DgA@1HDaLM&Fi?cI&bpHI!OI!DzJ+p&n0#>?)6k`iJ7Z81? zhqzkW`a3*PzH;^Akz+@W9NZ(XcH!iur#i+K_O7t(8;jH3LxSJlRlTcr@#2+BXO+)h zIIpO1>!pFYtrO(!o$cA)K_MoO?%%t2|AEG%M?mk^xb;BS#M0KmndI#qP5C?%Fw9_V zm6U=56C4I~HdYWU5OlhxMw=|;WPD@C1-F-K3r<_j&1BZoPd|ql^JAoD50f(k zAEz|#9M{#+K*>P;Z3zeBx|y7O8Z;1RXu>LWKBLjz*Kc`?X9AWrarW{L3>Nkj>>GD0 zUwMJ_kAE4r?9|bD6DQ7=m@f6xB*_<+wr<4a-RHkS_Mz@Rsj=gwR$o~)ZNiT~j+roV z`lL0_O)Tx)yo6nC5j$tt>aLso!-R#)=Z>8)?#Ca096$A^#Vc1IGO%)V1Jn4M)suGb zoBNl)h&@;{7EHclCrp?mp|*R*tno)JtsHQ+v0glJ%v9;0rtNiJJs$EO$Bmh^P+NW) z&jd_yMrOPt)eKDF#mK&njfn~i^zlRiqPL%a0O-J3GFlrVR;a$IDn;FHdNPOr2!sU7 ze{^&-JI%82Bu<huWzAUm`BT9)DEbdC`*fns>) zKyOn)Nl9vWkfXb+*0qc3CPCRn1%*X$qm`9m_P$>}3CjyoBcqeUf*p+BnZ0=WLf1b% zBQq-}ub>FSKmSq}X73#m5fT=am=f-7^G^HzwewbyafvBu8QC43-Mw}G9T9Xwf?J{NA9_K2ZVj- zN!zl;z)17noxArno*H>37Nm!p_l%6oIVMnX2td7z= zdxkw++~bF!Fl*GSqrF}r$a}7$G@60KJ)k2!@VGVCR@f??W{)=auILq~Sq#GCSX1;t zasSBUKhlNHx;zsw@=19nV4ewh@~Q`jG`EntFef~@k>Gr;t4(-&Pw~nco(Xu%n~?|< z!?dS8lsv=K8fq)ca*R9y{;M9oK?kz#g(5xaPKxMO&8>#rC- zG|<)7Qk$0?9fIjyP-N_A_@a5N*=f;Nxc!L7Z-PPs2zLANkIpVO50=QuZFnv>XX?A=VKAwk% zo4bv!o}sac8E7gS1;FZr@2a%{G|#bg0QdCta(=7(j>gx3g}1o{)fGr6tF0(bkBJBg z^bhcNHPkmUHZe7`#FJOsfM_-h$&Ti#;>_3&;bCDx?iQw|@87>Sv#_E(4_twTvr*n# zk&h#MY>1bOqn)jdwY8NMF;Ul`0~S{Np!~PAC?_>8G8kmjF3zarF5`H9c_!f6`pPsP zb3;9SyQtEJI-Ff9%BT#QRG(UHS(Kg5liR916Y%DZa_i;f@l;0i@6&TxOGrgZGskrQVw zU%z?l-a}38=P%!kiixr_J;Kf0(9qo4%s}@Q+8Z4`eMZwu!@(DtL&*J!@lnA(t`0U< z7Ut&W7M6_17gs32ctCseOu!dk_!lGnf$AtwNKpdNF{b|w`TF~$#U@RhFk$?3InCt8 zni|j<<9h?qF3B6?PDsz4EirlggozWzjhnc_7})w)K1xLEM?=i9)yt*licJ|mZX(!r zCrpyOUr|l+A~tzl&cz*D<)r6ILNh7rTq)r=+BiUB1Ek%1tH3)eC1$2guDB zM$|iD#=B~ggXy=rrcg`!@m2Xfi)T(7KW+@h^Gv{CLKa(k6ADCdlBKCi+cwE9k(Qn_ zY0Q`($Nc!ik7LG8nj*em?bZWL+{2QRa{KKYHY}B0Fjrzc;u8o#jGZuL+I)qJSMR9f zD?}+w!ELz>veNTri%%L0uH=9z$bCg4$`V4ewh=jv6m(hH>L&6_`e!TfD;EJL9f^vc6u9B!*0Qry0J z-TH-#7A%mSFFk+ZPiJG3vvTr_iiv{xQ_E|0l_T5MEM2~2k<6k6^A{|bx5_j$Epz8`=qEp%iIPi2Y5(HBx*bp@QfLf5^_8fFwX=GqS(4xrVmVO zn8F~EMt(vhr&Fmta?FX?mKvqfFh3)U(=j=sd<_j?=^be#X2l9L0rN6K&NBgXRKGkE z@D@3l`Ew+(_((`%30mps8x$H59ZO8f!xopW?>Mq<(c;B3X3mg8ml;xG`)r)N0z)IB zz{5K*)b#k;(Vc4-&y$`zYvwE|NvRnU^Y0qjy7_@fkhZ>$pYv}jA6~J1k@QcqX3d&8 zLqck)$}47d(rbxH3dmMm8BkKb#5lsKlp$*XJl>SbEiZK*|K%?|LG=%E8Kd_qcOu(pP7U0gH z%x7?@ud_ZY#M{wGQ%yzX>`hO=#^YwSw^EG&+{>Rn4|kR)`8ZkXs9#c6Ja_eBS})dG zDrF*siSFS~Uxow)F&=iN&u*VPqo8y}%}T&B7@Hbtc;E2QFTXWqhd5anYhMSzo`T}# z=h@91+ylfs6L1&L1dPW8VL=f60$>{;qC68YD_v-584aLzwl|e!L+5bPOb&H?``{+e1bj;2vQA`lOl*8YB4tnaeg6E3X9D)HeyjE1;Wgzm zDpwvn*LepT17Z@P^at7!g=N9^X2ymu?_9h7KnE-VW*{1Iba8X1)FC`$*f<4}=I!g}Pxd->LBV-*V=ZvE^3xMzqNAcdeE1L+8Xg|O<=z2q1E~X4T3l9A zl$(hh-o(VDgqYaaI7-b!&Jxm+IQ=JVU!Dn=6#}!&W|97b#WC6Q)vb#cFW!8g51>yN zQdIQaBGUivmW&YRCua|CUMV|&{xy|;FB1)hbl zD_8rD%B~%&#V1X`@QG8VOD@=;_T+`0k*TEv6sNtjjb{SJR>t;!V&Fh20P_4(QWN9i z6Ij*};Ze}~0O(GF{{;Um&jbwj2r>OuGNxZ=y7$z@A1g?%lY2;hI}iR7`AK zd{_5JwBP^!Uw`}ktFWmuH{4!VT}}1U<(n>ql@T5Rj32b0fB)_0&jXDWMM=KzpWnQU zcF!5d_mI#~VJ|!=gP(u>`D=eiO>st~^V=uaE?m5PNfQ`*0l^`m!k#|32!_9Y``q7N zotGS7_x8!vv*#{eeQoRL>g69q^8Wsz;ZL6igpEkR@iu?{;M%$Km+lx@I=FcH_y;ri zrorLipL#($ora@b%(k3!wesp`pIYtYkmHkZWsdJbv^N96^q* zp5ETR@HGQ~hi3wY%MYMHKq*8_o3RWaH-XR!VH}g!0gV|Nl;nv7{5pc@1MOE$H7Ebx zgQfUmpRKnY_A+IUjHlYA~}x(slN88=n!yno(VXfeX%?f zFuYwUdKY%@KYmi-*3GN8pFGzwFyWbic_v^HlW=#3=VJ}E>&N!&S}Q$grj+!txJIN7 zRpGG3)}OBK=vVf}_bw?M+_qwly7($b6oaB>l)$mt zTfHLkyNjNm-o0wxteN7|riqL3Ou#%7FnE4bk`O2q`TFaTCyPCm0DzG^hsHAj<2Do` z3c|5P;|#@sl3<>|K!K|j_X%LlBu57n!r&;|)zJdzA3;k~LroQqSwK@Mgp?5YJ3G5S zej4a&Ypf{F%&u&P_E%SehN-9!tW1r9b^sTA`|az;E}jYaLkO6Dc_!eLq{M`{n3(A3 z1|YN3@tr%uGX!uvHhI|@X~{qvkA)s1*; zar|fUqCy~%XQU)2)q)fRWe}Xr4SEoA4B_|Cd5NVT&hS!r>gl1s4jy5%LM>fW;!Ab4DO4qKy+|N>0xyh zdgGJuOu#Vjiwaw?tYZJi0^24iiuLsjh|O9m zVN5+hK53DJoEVL;G`Hqx9p{;Vc_v^K`{ZS&B*c6m?GFhK4#L{N%1p>F2a^+3D_9}2 z)01Hxus*anuns&Eu*eZeFy^d*nFWU%avW0r6B-3-Src`j3>@I7L&AZuBOQ`DGhif| z0rI9slEQ^NI6O2sAZ)8E%`2!BAZYa`Id}qxzJ4198E$8DX;DU6YGPJZdzY{sCK()+ z-8};XUw-@b%f~*D12)yxloVtoM}^1a)xnpFO7H64{(+zW@%zui1HC;!{Hv=j%g;&( z4fOU5h%cx_A$Uc9|Cj&z#~qIJX<2dq;NSoCkKexx z4)h@s-3lI>qP*mYU|&@6IypM^$T0;tI#7aI}KP|fa3D= z3k(XT97c4YG6qoj!c`+6p5J`*NyOe79~T!N&jKKbf$&VgT$F%iJ0QuJfvMnB!bJz5 zmLqBl&7x=nG8K`C+|)!0JL2QV?~z`BouEiS6X`dF21ZnydXK6%5gpK2NSXSx7wSRU z%fQBIezpMg^mYr|8f)tWojpAw2t@^(&XkE|Oik ze9ivn>G?T5?LKy9FYaGcy`Z41a`M3LjT_gkS|W>d{Kd;xth^kX(oSc@OFMwQT@tP;*T}_rzp-#FFRh5p(D=8j7h~cZ3E|QU1NS|Q8mX5H?%g@v1 z<^4-4hmV{(v}gC0jT=@iS+aOBiUk*~IC^g03ePyY3X2tU+CNtqpxOcV^i1Ip6+EHSXy0KRa=ixB!jpF)NgZ^m4Uhj z&jifft*)-N+Jb~YSDSbDuBhI4`25B5*9JTjFwX>RoW=AP&O1>fdkWPUD8p=SDcdFa z(vRdI5c{tG%o2lVqFGR@DEq*KjUV0MnR9|l53stl=IuKDOdl!q){@LH zVhSX4zOVmdpKbcbn!L0)7b_#fuqJSbB0N9FSE2jjd zV&I&U9K*ZZiV+~FO!l)fc=}X7CM!FyppZ3g{7{Wu-&dXq7;%=amh$|ZOa=mX+7Vc>BnGO(`)r+A;q$&jie^Fe3X_RM^<^C#G*sPI$@G$elsk;K;jpj;S#waBgjC zZ6Q;AL{5Hwc(XH50LW};I`ELy%*kmBra)kit-di>H_0IjInM+fpD4r$1-G@%&Gx3^ zV(}?cCQX_=ZTfyYH~)|bP|ZZg(E5P~xT_;daU0~5Crz3(W!k3q_8xx0;gKIcMA7$x zF5NvXdK+YZlAH>Zy(!Z+7+AY_gNYY2u=@|WP}rcma{dDGsZ*y+oxb<21t35ILqN#O z$uS4d1k9d$dN5gR85wufxHrGQ(sN4~KG-~1hwl>li=-r`*j57-;*5@G#eyWI*=cKH z6V%t!k@C+CnupP7n1m7&|19JFM(CUM;Gm7!d!zOP+0Pso2%7;KJX*pdQdDLlQ}7KJ zpv)uD+kg7Srl8>df5@5s^Gv|v5)vEz<5MZYD={TKgY>hzv#U+@*3;WN7D|av7ZVd- z@W#VCC<4kE9UI5w!k&`TdY)QxGf>4leY)5TWorjFFTX%UxFbpbyF0rp42`dyS~61t zMBLNG#dqkLIk%gdJ}oR7P+ zCF1l_8Pqt4OYVL6(#YC@X99-($Mow@FKwioJQHw7*7=kkO2TR8A|!Z<=#<^vQxcuj z)!Xl*qpROd7(QSsr4Ep`*OfQ*eeB2#v@LJrnSc+Uy`kavAucI39SU6Y!7nk@$MW_0 zQ|BD5b)W6rwr}_La~B?Yg+;|Cra*t|qg=9+TufdaI->FFf%c_!n>KG)sq*6W!#BZE zu?TT@K%w%6a^M)$maWK;}pevy}{wuYg_bL~PuI~|qN z`ws2hb3H25&g$;t(1;KCe(DqLwJef-ti7`R?2WISJ#l!?IaO14JM%~HgF+$zuU{GC zZkV3pVQCuaWoL3zb&&jj4(prQ4WvqMJpzpc8o zv8S)2B-E_1$>=T66Y2B-Gl@ceAe*nNHL^$k=^m7vjp3a|u|A$gCc4%(`6*^R6Y%b1`{(bB2)58t&;pPM z^uH?F&G3bt+iR_wV23xSkMBFOZ}+W~5KDt=w~&R0``=y=q;H=e>EKZsXm4=l7{YMRFWAbCO6gjQ~1k41d10P%*ZJnH-8}G5BH(q!qU{kGSGsVT`9a=WkvIObe z#iivHox+|Er)4Bo%U@YEZSl74>y+0@&RMcsal@8(si_%2+7=4)?yQ_OVb<&|r&g`m zJZtF&@v-A(sy&)FWu;$4L`+g@ca!&VsqrhOiyQ0_pD;mk)?d)3tlS|vX`?4VIKra3 zIxJR<|Iv7h%+={*e)vIp`K+;%XG={JpFD1YIVZM&8vXfJP{EN))8Dqvyn>=}@ z%p9?a6U1hVjazvYazk^hKiTHzC!e`H=P%N{;n)b#iubv^F;}GJbDvW#`B<0b@BrHZWTEz^B3f_Nv;_ z;tH@TAsLDz8bw(ITfhdsef#-Sx1g?}r07FTYC#>GA5;?sM5GFmfBna=NW$(CG?rIY zB?NkgCg;I5URI8uvQmgY{_lVN4i>A{R-Or%7I>ZsxRYlBCde=-5YGgRbQ+!sxIsXf zLmUniN~;>`+VUa`L*6TGxS~g%SYT9Q86^)hB_W|7mgmgi0>5jg&Zq-9fg=j#T)XH% zy_(w^%c6+7FP?d zt77)fpI-2Nkd(xzAS){oIpf(d8p)u(I5G6Qyp+8~?tGC@RIN|-{H&UvGwaECr?ggt z5XkB$TnnBFm}dg!nSf{EFihuO#HF!BVXH3An9l{0faP{RNu5`YcR$STmGQnfPx3*e@}!bLD6fXU*S zfWd&=`1v1y{r%gQ;r=cGxMrh6!KLc~DqerWi^lzGZ2tN0zyAR)-u}*pnxgc`5S|H` zX9A854*?8`uMg71DmcBviMa-K&PDlz{*#;(7a1NJ5)?=XAy8UsEai#&O-E=w|fv-dUAn zdw3?`b?Z0p*tJLb#$65V7p1f{m6aFSKYM!X>eQG#Qz#P@^#Hk8ElZve%)5_A+BSiny+Bwt5O&BuKZ9QIY zUo>Ol*m2;|B{9g1CoQ%0^YJSwEw9SCCV%nvksS*prjYmt)@A(o$6ufeFgf)ZJAUHyIchIo z>5%bP`F`!Dt?TAUh>6ksxB}!n6R^f3P0c4y^C?j~pRs^v5$tP9LR?g!hlAyNBZGJE z^!4B6vsCVaJYe=^5fM0He?>KR+)g8)e1m=#T`Ok@(1<06$-Eo(Y&|0v`U)YFP-I;Mb$mS!=j6jyj>s?4}Pw;zOZx4noS4qrVI>naDte>wV9I-_eZGiIk-_~ z@%-763vNaU`^az1fCAaV7xwJhv3&77DM<;?d`U`5%KF5| zCnhGR()e!Ur&=l-c_v`u&~0k0t12r3&nDeH*wh?BeJ?gH#x6eqfDS(ovo!wAeQ?gGY|bpSx!Xny}!o z57Bi0yLl#Hh9bx_0fQB?yO+~{R`JL)0n@@b^ta#s`cGj=jIXQBo7?9ACwN-vs##W6 zRyG=`*TBzTe*4yv6X9lS{_Mut(+Y|_6Yx_)0YVv~2c;m9Jy;&fGXZ0-{oZh6kQ3HM zyYKfm|NIvdxBXd6wZMPs%#9xzGFm8%-JE;x-{iE-l2wmgp6&nC1(&BHWN!cO?&)f8 zZf@=9fhUIRfsZ$;|2z}0%lWGN!hajz_9(miu`-?5>j|3;Jq7G%FLZLd&Z0f%jAw-d!Y6F&0E9w);6u} zFhZ$u?rTA1kT>gep^?C9v| z%RDJiLG zXOu+Ug#wfvYBC2y=Kg#d}JPcr%u!j_vmE|Rc`+IqL zfn%635Q2e%fCiW!WXZz&UsqjTkU=y)(9MX52)uScOgVxG58yb7#K1BrFfwcs6No4v zHl~d_P@~hT07Mal1D?-Fcu=C3ln9fS_C5g9U=g4RKp=_GcRr|o(on^h)IvGblzm7Q z?=1qNmZ(Grq{roE6ICQI-$cM5kRx%I<@SQ2hU<})iEsrW!?BPA&jfr%Sw&vugpx(6 z0LcdAVXI(}ny8EMw{S6adHw9A>W*F8WS3t^6^zR1tL;uQ&x&%fGt$+$xoO#=xiZTY ziyDccg^hj>+B z)E|<=uBXOkAcq=l&4gxNRap%`Dden*o@QZvxDEufhSVVPqcQ^@Vrp1NyWmt82paK8 ztE*50L2|Y{LfYHY04DIn!e-QuswXCG`v!Po6w!s@RPA8z1Q!7@NK~!y^IXH|xob3&*xDnmb#3%A|=C zr;5#*zw3e4YnaII>2YXoX*R!g@%XkCb7zVHXKdnB$+@y8?mk2ZW5}VLg7)0Uiu*S% zo;Mpb$p1dKYsr^N`(>!7yS1jYI6W@H+u0c;T$XmA+42tzgz^eI`hWd8*ez@> zE6GmcnSje+|EKdzz%0wSxEN5ljBFb(T;(H76r^4e4=L1_ zSW}vxTUa4z#%G`pfayt&2}s;jnVA~q?O(-DME<%}wlF zO$ErLza%FE5M;Zg{@9!e{DaiHiVhFlc&ck=n>Tf7sOO-JP^thGM+bQNI58z}vJUKU zfE-R7zd_|o2O3WI>6LY$ngr-KJTcIJ8qeg=;8xHRwh0;=7+ex%a6rF#Cg5tL>D%<3 zZO^Whl$tVO(u8s2P(Ltn^4PgLrsjyD)ixF1nzdQeNPWxF8MCHN91G#taTBIY8^7{} zu92y^Rc&2Uw8~Be#bcXfW=`XofO#fhk*lDc9HMYHFs4tW+mHi*a;!&u3FyZ0Y0&$l z8N>9C(Jnj_FwX?+&NBhS!v_69gJMu(DfZLmMi7oxb8LZ}2TUZVoI2WXSp@_3S>~Z* zE@XreD9(unb_<3UF50=_w_{G`(HH6h1Wm#SCj}<&AM$PF086i9-w#gDKP#YMuxy&3$=npVLkV_ug&iwxEy~vbBLOnMC zK77dWN?SSgX>X&{X}IS{w{6lV)h%rgO>IeGH%t{pozu3xuu`BGU~ z*(J+X9#YeIg~(63k+%AkvkFI#9N4*U`=&LkSFc#MV#SKp>kp`^Khwc;CJfNLd*#&O z1E^x$zHRFUxeapbR`Ji8))c>lhAdw1^IvwiE4cbP1w`iT^DEdSoO?l`4cCOA3bvP^o84+&t3!M6L|u(f6_DC-c*^B92XJf=k4L{ z;pvGVFCREKgQ<`NVhWBCOD~wfbJLShryCs;6N?syOm$L(Hki5yt+DuhX<=?wI#|Gw z0g#+TpC65qgvdXQ*cs0ROvw)1VIPNiE%K?m)N$z?StVITx#>4)kr{ ze)CMgF5bQW`rp3^Gb5sMN-C=B8U@JN6n6D}{PcC8DlyR3-qxe%@BinIj+Xk=57F5L zRdtQ<*Z1@f4GncxWrtgtSz5aG4gary^fYz~tBbSK3Tg}M1nu2JeO-;kxf#A@R_5l; z{e!>$v%jpir>7Hs(YpF}1PmK$^9wWMLtPwgO)On|2S0uO`D5Qe|6pxpb6I6+V|8(E zbw*ZjfVaD=g^7b_7dVZ3KXrWU6ckrBRTkic92K3E6zk*S>1kb$n{?7Wi9r09r*C_8H(PfIhfnRxR|z?4KpngKf;ra9#r!oo);5v2tj$i&NYD3h3^2E|^maDWbJW$pbzSY+jT;(zW*w;TsjdjiiSReJ z4RA1cZ(;S|-eWDzs~6O6-+Z8BYSoSoJsl0XL3Vn<)~^k$-#x#p_DDln>DIlwnz|+y z);N5U!P!_4`ytZvjg5`*OYMiMmu_f2(0HzIVqxQegDbXho(Y(OROIQV(?9c(Q;-c& zHtYvv$+NOrX2$<{_r(d8_E&U3gZYnbm~nWKubrl0au`V56fo;ML6S&WJ|pt)`!~A= zd%&cVdqke$92=$36VpDVL)&ioY{4_(nfCO*DS zY<%^h+KPt`Zd79Mc z8^c;`TwEN+avfS*Nw*qs1VD-}Fnb8n2W1#oenbt4>p%}Bq$}jMQScZUcsY4s+@K|j zZQgk9k?7Cdb8ya40IH}6xxvgY$9!`vwVi1m^WIU03!ZEGc%(LHw70V>AjZZ8**N7T zfL~YvREoeF4J1G;pDA^Vyq4&8) z6R{rngUCF=#yKJy?J?2;CJCrMMQ?xqC7J=_@gMX*Gl9}I@Cn-fg9#LTtexqga)pS= zJ3Da!ZelE6;A$Nh{Al_Dy@icG?tT789#}6Cg7pL z{?Gj#6|tV?2G1Uc6DH&NLCme{6LxUfOhWcBJW4&yw z+=HXy5|cpd1ORll|k z)>y>A5HS z7C9#X`9QfIHQKwW1AFY5a9oP%p28N8}ez z7n7W&-OB*fT3lBAf zY6wzZ?O!r~hlb938wV#h-{8nt$VWPm^0o+y!+nASP~jIG78RF}oWhx>q~N4~EP~!p zQ(l+{tZXu0vZ33eE;JJ>)x!a|p%&3wvd@c{`O8F1j9H+2Warnx{|2BYns{^$`k;8S z$kUG!RfcfQ^`P*t}a=@sP%A^`{YuDH)j=!Y)Cwe{xfVm6fjg#VZ!RCU^ELDemK$fR*o=TDy1$ zg+V(7Me$DFzQzylJbA2s|KYuBm(^}wI(Po5xua)52+6zJ(mcGqOkcft0bJv^`uh3? z#%~_KwsH0L3nn>Mh35Rc4>p#bPS)18gaLr!e{vrLg@h5)F;0^>%+?ndWW+{>hjZxr zL4aV0{D2aRSTYW}goq$E)|40IWM+WkJs}SMh1l5m_=JSSB*?LT!0(0)g|b#f#lPuk zX=&+b86-!95k>*e1dJuPoTHo1a&ha6r=h=(?imaumdS~=isW4(Cdn5oh~P220ddOYMmjvF&+p|BUo`>p@sq|*lUTlX>V#R!1}2CQwgeoRGsf_~)L*2I&mTK>%9QaF#)(gx zA}zZ^^{I}bX-8L;-u$sY9N0MFFF(jEm@;|RlplW>D+Ms5ZP$1v;Kam4c(ZANK|Vdw zi;#f?41kjSoJ$Qqe5TMg<*K-KyOn)Nl9vWkfXb+*0qc3 zCPCRn1%*X$qm`9m_P$>}3CjyoBcqeUf*p+BnZ0=WLf1b%0}k}Of+7s(nSgmFV0y$^ zBM*Bk(Hplm)ulxq*i6qq-eW2v3Vo3W9=_$~x{Uj~^{6{GMXbIf4GKW5sj;>^Gcy;W zk;Xbe!I+#j*sAj4d}QWu)ikUZ{fm6dln1~w0aFBppworb0agx%_b#3}vuC61T4mcJ z_(DeXpTvy`F1l}D-Mw_~_{qboW#%qEYM4}zn_pN=`j6G4qc+`3@A38X=dPSlKDu+; z_Ek%k9kjr1o1U4ISBMOihor$L~LX9{SkRQeB*t7#ZT{xD{+3k%@-kVw0G&D7}2)jNGf9&mO1|30sSOCg&cqU+; z37CWFAqG>l+G0O5BfaP9S5!_N-m`71qDxH;ayUwg3psI3Nrab^iNVu{S5!_O-M4ki zmc8#PSfXYDlh;%xhWNO>H+*^bg7V1&d$(-bxOvxGLSro~VDr}&q{amXSm-~!dqwfk zj%}Ma$Zg!T$G!^keA55ws?7BG5Dy#ur}xgE-m`VbCL;3Ps+0!8nLM^Wlw{=QMYvcR zXsTiQotsI%Ve4+25+J=2AWBU^c5zX(7taKIS!wU)_3PHGUb}9?wjD=qJ$U^5RarS@ zQk44`zI}4{+6ARU+c(Os=b3=xq9efb>ErF~O*=KV@FH*)6=1tTYBK2K;TcSb3Jnef zP!XPA=ICSG-QWV}nSdvb8#iH+Ekg-^ zNg>n!2J0&~l@yURKYil(u^dtFgch+%WaUA28!sUvA9Fy#!Z?wU1E!(%H^B)@D&z;kvdIf=?dw&QbdCQ z9MdUNrb{e2d`kJ!jk{0{QjVgen`;*RG)sIM+SF;tvvC4|yhFM3;q~NX;G?pd4^fW6Y}kAmbxuH3SVHM{f6s z6u(&qumb{KfPA@lXJ~jB9ZPO72pvR-&hp-FsVNv`1^~=}KY$~LL6Ye|27%!53 z`PN-iSKiS7Dv(GG(RUt_u-H6cjXoK zu9I7}WYOA3$pfF593P+!!y#_6S5-Q*WB0n{OP4HIIB(H~s$tBI#1KI<`|LuyyVvCp z9o({M`TC{vrKRW1-5%7#m4!Fa)rZqfHLfTeK5%6H@{Mu}7c87Vf8M&JHo%_2O-moI z-%tPP#pCh^ckfuSY?1WbdD1cq=U%|NPH2aae-hdo>s>y2^w7q&%N9yY&;3bSdj5R5 zcQFL%D|8g~l@hw}ZWx(f9U<8nI|nMlD`hw!Vnnm!w=rCZZ0| zZiM`}@nB12jb{R$H*==sEQQiRWRoJTtFeKYNO>mUkGU_;EL*isX71d%v*#@2nShae zjyM2C0Y;T?lp>45@J!C?0Ej`98mT31s9cT0lnD&q8Ka*oT)tNv-a?HUdp?@ImM@+u>h`0j#l&lAM zHE06%H{`uNY;B+=gj+U{Rs%7&|I>Kr2+st}GXd{hv0QfXsvQcqA3c5j?!C2xtA~$& z5SC9o9ngZ_mfT=BC!dIjU~e~94=UhA0&a8+V~EBh25S+3Ye>Z_%gabfPD-K*LuA0D z;K3TeBM5_&RELtncqU-ca?wdO5nK@l7xT<%BW{mdkCi8adWu4BW4Y~;FsW}l%zn|9Izrj#ONQC5=0pfBl^q4 z)NiCGmoxNTPLdJ*7xiJ6ruSJRA=fA$i01n@`G4v^bwJL=xaZ`e^uegu~SN_8ZY0PSlT%f(=Y8c-A!p90-WAy-B-V^ zbmFwqrF%RRFrruac{y3oN>&s*qNbz2MH))$*XVoy_6MiHe-?k&f7X$87#;HO`cH5F zo1E?NBj*Cq6|q2k*MFV~_|WFnD}IugHbqQo=Iph`rSME+AHb5@Q!c1%`^+-|%g&Mz zpFU~A|IOZe21J!D?ZRhf%!&l#7%`_Yr!hO`hyfH(jF?bS!Hfu!bIv*EoO6aI$EImw zlM&P8liu^)d%vgFZq#$$`~LX;eLwb^5s+GYce{I4t*Tm8&ofSThT`gzns;^dj7)89 zQF@9Z)Gq(SKW?GM)^MBZX>Gp$XuZ_)Yfil((D$R~|N1h27ZU`O*Vjw~^ zMFlX|1Aqla>PUVLc{+pwM#LFx0`&+$uRjmWkY-WQktnSpCbxj?`<&ZPt`55c)j$$V z0=qZo7(hm0KXnNZ91=(9LBYbv;F*BMg>hb1&+gw*JEs{_Qdp3mpV!^f4NO93E~&E%PYk!(Ac_8ea$V=H@mSDp!&C~z>yztVr&@`2QV zI}pSPU0+xGwf@7!N03hw=mty<`!J&ay}iv~0!={h;>AJWQ7M>ojY=P zyW-R-a&mH0=BzewqdwID)*aFnWuSjw`})S!Th`8!lLuGlw0T<%?VX6~o}##p&b+60 zt}E|Zv2@uy*-79Em7OtvzrHD;^xQqDg1)^~WT$mqdDn(za}_4Z%1oFbKWoXk7q2nF z)q@qri`xnxD<9dsWXXJa*~u6)7jC)z@pXuT^*MJ#tJ* z^ZKP5kDk6VGP8o%iQqA5MK%jX^*KpdImsdJKsTm4ySRA(?VHN^X$Ea*jdh^;&Ps?1 z4Gv->7(*(@4OkB@!uR~F)WrC>xLB}-M@L1G90SL-XsRP%UKAV@=47U&Bqah&FdjsU zoRnaJu%gk$i>wVmNOCeW(o&L>lGwYWw+A_PM4CVZc%{V!d6<%(#!~ro7%Y|JC8#uo zQ|{dCO!nd=C6(iufO#fhxk)mUWM!u>ed2`h*v}tGNYG$Ed!7lnr6Dghw=g}_)yl-k z(1>`DZS3qF(9Q~mI&zAI4GoP|6{XpUk-`2xKHjJ^rUAnOR4^i&6E=Zc8bx0PnaS}n zQIQehVWA;Gf!NE~9mIeRL+-7HU_9~}&_^7Zhe#%87`>tj^5ovcf5<5V ziT)k1^x+O1k*D-}>ARqBKv<*d3-%Jy>j9g@{uLCO;5o885K|z%M|xq7%8xE)rXC>Z zta^jwJQJ{3+|f1gkH7x<@qK@5OI<}?T700Vi=%_BcRC6|c_v^$cC|1deL#ha8mmfj zlB1yC-dS}o=U@|Zx1_GW5X)tGII3I@@kP!<);t~Ca9|JFj!UmWi zQh(?_laqNMAB#<3aRuu|f}Cdp1`~2C=$6{*1eu`$PS$!4wKP;!R8*86#NmW0E{>j` zpuVTOwX!J6$IaMK=iXH{6_qomPhRo#55!?sldwq`o!{G0UyvN+W@)JR@b*O&W#zMH zkEz-?xd7#+QCL$FmD4FIiI4SjFg4cI(o{c-+bb(wHMh2RaCU8MYOYR9sS)JFM|hi= z>pi%6Mg6?WIaRguT6!kd_D(K{HHAV!p&&EH&*jZaU9B4$7uC*RxN!dJy(b3dwob0_ zTAS!CrbKwz>FYhZ3j%>lSFfB?zoGrifN=hqTqtbF4|lUN*3;G2(z<<1Q{&Q2?I(I~ z%&e?!A#X&u+SFK)=wWO0^2rljo%?sRv~`}nd<~2dE1MBH5HhNYvJxW$-0dt(jEsy; z%q*;I>>Qi`^upZ$hi}x>m6<^Mzn7;6h9_)9J;V)o1xi;@Q%G@MW>RcaB+mpaRd5R6 zSp0>}n@k|j1UzB#yj!J07%*~AN}2o8+?J_%V$X_I(~-d*jT&{?BUufMhq$FCN?Gp0JPH%08fGLT z#!Q5~rK?=u%2DUo#vi_ym7Oql?3l4*Cruu;Gl~wdqaiM}e`VkorgL=OqG{u0!1N3G zxCvuc*t-GBE=VZinSin5!12b%RZ^6j8Xe^0;p*h*;Na-!t5pguhcs9XUn087T>|(JVv=3q@4HN;&X_{AN^bQ25V0 z2sVcj@oWOTW;ni-9>6_-7YOG*IUz0e-z`5;Q_P?;fP%b@dmn!vuYpV-ko{25C2iN#3U({JG* zeKiA#KDr0Qz{zE)fA;P345R{iCg75qM&xq_20s4RKmPm~2=#b`?M*e{he!u8kB_&z zM__VArC{Lg-~aWGKR&)4>_=5pTVo9tPs|(Kr%rgNKN;%I2%ylLFdj$bI ztQ4^lIFulR(X_p}p;6q`+slm^hV3IZ9(g}~s) zwW2bxzeI<4Tbb#-)YEs%Y3=S}HCe(&I$)ACROBbe$43NuIXjvfywJU;ZOAhLpE`X; z`G&DIXu>3&!qUv>Fjq$}M=O&j_wQdnucoSe_RN{Hs@nSIJ-rRBZFNP7Q9&Lc4m2`; ze(#2cx+-{zm6gwH>gZd5z(p*q$_H?Qo1=}f#p}m+Zd|{luCAu4u73UDOA{-6=6&Mw zoOpjXCre8c1JHoozIEg3rOTJE5(=QDEq%T{%>^;uF18kCW`-|c=-j`1_ttH#`wyS$ z8(Z2suzGc#379MUrLS1}bn1ug961l33Am4FWLkqCD4#rW=%;P#*RNW;ao>aJmOcsf z=pmy?Vu8ukGl!2HKlIbyy*t;hS+n|wjc05M#4W6Z0W3DzudZsGJ$YQ|q|)KNdp528 zVcFtE3zzNI_Rc{%SkT*F`UXKa&jidf0V9RQGXbNkfT|4W+yo~QP@&J2!!Sw|Ku`-5 z%9&ZlkY@t+_ir2iT~d=OM&Pq>=LHiKVBmsz@ylW~V>hGZvxORf; zFE1?uhaal+b1+!53hqHIYlP>IU7?YV=@{-tQ9(gF=bh8eN_hViGb0U#Se+6;n1uR7 z_LY%^^9zHiZu_Lnz8tPNVEvVEVfi}+sOv@hQvw@H@fp(|c7A+g- zc1>Af4i)EcBPE4_~ zjOO4As^hS)u?f&^*axcdMp+)KlAi)s+ z@gGTP{GXaZuCg8hzJQFZnN+N#anSg1Rgr5RW1@XTWEr4eNria2a0rO12KpUVj z08kCk-IJ4!b2kjQ?UX|X&oqsGhNeN;Ve;cCfF-}5^3#YRm>7TWVs-A2(`HQi#QG5njOS&<&eeT?%vZgSnBoY$ev}23i5JGQaZ(AVv1zzhhj&vFgDJSm)9>{ ztRO4PGXWoZW$EG*6apr_7%W%%2;iZ2w6&M%teYk$KUq#$-^L>#7zIkvVEUx?Gz{$H zSn+93w$Gcn66~BdA3`E-@t|J2xLqtejRbND9oL zT3en8m^D7o3jcCyKk^f>>sTzo@fWb)GP6R@n1$e^8Wd}=37=bdNQ^;83+!->6HA$T zIT`nr?${3dMI}QcOui&xmaLx=DKr7EAa28qkt@l@_Vl#J4M-=D96f_P6EImfNpJbd z*FHMyrc9NSnLJr`%2_*SPhYUmfsInq33*pfwXx~t6N}McBQt5ToZPn8mV~hr3_3cp zUr@2G*6@>Cm&}|ZJ9*M%S-G`O46U8P69^S0-K5XBNAzmb@lAFvMCd@DCfVdsIE9pO%{2a-0 zBAJ5Q!CJJ73)l@>{!Pv)J5BDY*KA?dN_r(pI8Tji1keNHb$d8DcMbVz%sQZ)T_PFi z$T*zT&E#aX$TGo)hJa@RR^PH)<;*so3HbEoCul=#bn zhMQ^MxqbW2-TT_w4<0?fuX$JB%*MgljpQAjqT-T73j=2xgV%3NOfh&S;EvXOSP&Qp z@;_fn#;GgnLz~BC5^wJZDW{(>;Lw@qq@5d`V zvvKewChz{B^^5N5@0mJk%+ysESIUh2_S^5qj+;FGho^wi^7NH-w?}WE;-J4~!Z%~* zFPk-L?C5X5{dUZx?-#CEwcp6r#S={9daK6o+%xMhf04cW!zi8!_~E064|N_td8uz? zVgU<5+gC?>S9@(*dSZZwtDC!vot2r1sfCrTBTxps{n+-_ifo>+UXYibl$a0~8G;@o zZ=ehY1&4-3P%$dvVPXPrp#H+V>~u5-17LtUxZ>mErS+^>>)_cWhyX!RZWgFMi0gv} zi)QtfQ@U9qoV&n6?V+2EDy3*Z$#`PaiuH9Ia^m!}POVL7FP>8K7MJhd+KDN_DVc%_%JOqW``-^W8)=cp`ngJnlOhEV%wXh!#Wgq2kjqO* zg6v}2i-w6D3O~r>K_gG_)JBH-qo(E%jgrAz%7S5f5{2@r6s0HP#49;3fly zfv&?KZ-N|Xs53d=^|ck{OpYdFdJas2ZIuSZAfvCUV1K2C94Zx{%Lt^4iP24uwj=rt zmrJAV7*d4)bT)waA6-X06EN}`JQFa_1T2k1rNJ=I1T2EX!C?BxYH|)b87EjK@~Tm+ z40nT$Kp6}*YnfrjSL$_B0WYC+F*!N?K;aVC2};Ww+a;X@hQP_G?-7=@v$MNBJJ{;g zjnk^?cO5dCJEYl36gl!tz>D`78W`L7r{otG7v^TACB^!gy?Cj9`r@`Vb7#)DY-Vxb z;u9<9@WN_pMsKXIsT34v2O3{Gu6|+j@@cY59vfM^gvX~8HxN<{HU`3?s;x{mxubGu z-?|x-+R|8!7~9ndw94v03fUF z-ETjCcsJ17A+9aXhzkwy^>TA_b#=A3b98pChrFZbXV4@M^tKDD3R9!Q5CFToVTLt8 zJnTu{&NBfw(XpKfrT^6xr3JYeSYyEf{=Psfs;H{2p;8iT065UAud5IggFiYWjb{R; zB56Sxr=L6%a7lvo%LkXwC>;RN@Y=O&*R5T*<+yKTLuaN39V}lz z(Yma0YWLRlt5<=kcip;;+kbj$VPOqcirTt#CkI<&ox9gGj_>_(-482Q{;+24`Yqd! zKhZZb1&wcYZMmPlxzQ8to0pF7*}QHIu3x=&uO))nSir1 zQj-(nVj!W^G$`;)!1Zv33GW1~CuAO*la9`rHA8;F zn6cx4b2DzaX+8L!fVe0iVO^at;qav!hdaWpz!V z^6u4ZH_V$ddCVxJ|G)i?$;VEdefy1(3HG0=a*g$?S1g<}ZQ?{Gr#Yj>jGH|3%JUbm zDuDM^W3hVUmNhfwWo1X>+Hb%82J*2JWS3mm)_F>Bk`Yn%gd{req6tP$)b6)0X8z=Sk#fhWg=f-Bt5_gfI{f(4Zdl-U4n zA940kDwIy%y1}G7L^OOLyxja_6XK&HqY_wsZ((6^u~a^Q9$$&1qgB*U z1^g>I{h@smwuR)haCs(Rc2ZC0@t^gd&LcP`Ut;Ox1}QGhAqydukBE3C;KfVk&6=_B zW@7IUtSAznC{lps92$DpYe*gacyFsb*0s}_X6>ZEpA?dr1T~#$@b-nN2NneEI9b#$k*V_+ucBgcmzo2 zJNgBNw0Z(n2wa^6sSm0rMwmXS{tiVB+)U{V`s)`t$xy_==HMH-2|N=po-zUR^t^lj zaadfE;O%Jsc(sqR(fQ>TmPRwC1{d#Tmh#wl8j8Jg;)%;NJa5 zPhZxvcJzdtH4)+w_f!QqTD-iiaZdI0;e$s{s68~ZbM+1k4M!6cPN?w_b?4a|>fXAn zrba}e5Zk+W`30c~F&c8Ld<>omSPBI^QY}HnPb2!z!32-!KNSmLd@bjU13gC=64;9W zCTGu>tcKJ~(FD{rePIHn4}xa`esf2SX97NYQuWr0H|E6j>q7;8Jv_+0$gF%v_-d81t{>btUCRAurVTZdtQ%2B_?2C~gk# zp>in{)L{Sb?huq#m3iD#*|}lmJV5DSthumL){^ED6e z+Qc&f12~i6{i71Bq!5JwS?Ov2uK&#pn1o96KuLgOgS?zkv?0?Xr@=D;_jER9Mmy;n zx>vLj79*J3x>3~K)7Ss$_rL%7>!;!Vj=DrovnS79Sl6OR5@(0i0C@oh%<$j-_V?d^ zeLvhIEQ_=^c=AYFJG2#qW#EoMCZo51@HfZ-=Kr?4DcjxT*<-CMSB+~xV+yD@$Oi`B z{`t4R|MS=PLqi=EDL$6ZAKcYYe~<%O89>DWq;lZxyFdT&&wu^=?(G221Z?0)i0T_74oc8y@bj$x9Ekdi_-Q!TpEY&%qG{@4(M50I4FL37EAFQ0XpE z3bAO3kCX<_1Y9F!3hwR7cEoG6f?#ml2huVC(SA^&fU>O|JC)4 zOBc*iTz0Cg8T2Y>vS1jI-OT}hrHOI&sAImgbIF_;Gv_Wb7oh|iP>Tef(A87w9$KC1 zZe*-;<@Dx7Gp9|FpLMRB0l}70k4jf}ibqCok&o#c&6C@g&YCJeRes8jL_rB~A`1`) zq0KoWQqWftW%>N9d*DU*9L4K;- zl;zfm@rg;v$s}!WPl%}$6}#V6Q`*im0rO12gdfVOe?cDG_9?N4z9h5=F}gxj8rIf; zHk5*iFLHueLi-TceFVxO(2_!qpq~c21Is<4jGWNS=>`ND)(->GY`jx1AhT1UE-r3pOOw_KRd_`w#y0O)FI z5M-tYdwNDx5}Y)#wV_|LwL>!S=byiSeAnO6)F8-83iWVv^(!nVD!2SR*yOg(?%)6R z`!65g_Ov$DmS!e}c%Z)CIk5ml@R=E~*gO+3LfdYMsG=}4IW8_T)Z5P5&fd`(P553u zzC05!xOi!Rb%WRkC_G>!yFei)F9BedBZCg4D0#q0ESU=dxm1X*KJLV-o>;tz#+zsk zM&wW;HUXW1xC3M9l@f*0^-RUMDcnmCB_d_N=njnO7YfKuH0dRHCgAI;s;Uo?OA3(U z$p-(~jH&OqYcXI@g54u$yO<*G!!`MP|bI zNnjzCQJA%Sud3#Q=SCK`aFpunMb!^>ulj!BH2H~>C(FpqoHKvzPfF@HA3l3+V%f-O zc=(>t~U-A@}g@7Q_+^Ha2$lsM8sK`~Kq} zzr645YHh5oC@#xOj}8s+baiufuy?SxL!Hj>@bJgqejY~bQ(swLQjnPv8yV*3i56nq z!^P9b|LySbyN@4-CC&9X7AwimNKcH82o3c2^>%dy2v57u~<^|96g(Z3>GWP zhm6W!+u;beh?|k%A{H>Pf>VZ#Lop(NA{3S)LkGShO7ycWn#+JuZjE4ykmjQd9@Uph z9dSnZK`PBL647@8E|Tv14Ca?5nLmG_sU&7TK`V~X=??TJq=i2qRUz}h?t#~W0Fuuc zKxReAgfQwAM7JzCNMA6J>!ZmukQZl^XB-S5d;8p+FH`U+ai&4}L5Oh>B>h77;ch|y zRGtaAt-imrwlqK0-zUJ;&DGNI)id2YS1z0f*Y4S~%7$S*JsrKB4F#DAMwXsVKAtva z2G1Vex~8st`s~>=r&SF7aDdj|-<+F~46&3ubP;lHvhL zQ)yOQkUQdOCmg{&yrrQEVpE<8n9M^8w`z%40?Sp1VWYllGG%U&PirQ%*tw8<_y*W?{UbjJuX5 zFqAOlwpfr&fjpbSTK-sL4=EW2s)$L2a9^GY81Rfd6L3&chP%#P&BI4eomM)&Z}+D4 ztClX9J8$-!d5Q~vcx2TrvWbgu)xW24`tb47r;hB~vVQHVB?}Z4=g(JMuyC)=D@nI+ zppX6YJL<{@4xZS*Yv<+->sKybj9x#*1q)W5zyDm)Ti|8)O8bf$&jd_SG$L@EtdagB zLN9;;ApNI-{SaF}(#1_kX3*}>(#xeKO#d;7PVmShr_CLeWk{0|m1tEZvmQ)Nn>ax4G2$*X96asQDz<}j}8#fW_AP*N28+Rv;L2` z2wW!cEBznQZs`Ql&M)c<`+&je|Hy>@-{?Qj1Z-v}`S77V&FJn^uf*J9L3O4<*M0?9#+{{2%%+-Tx^K0q|o)A^*ev z4>JJ|f;k9Z49G}<54;TKru=`~|C>hq1L_>&?m(|VEQxrNovFDT6FA2aBAr2@7{>(8 zGXe8Vz{CQ~GXc{c&IWXty72IK$~jOrkhGFH@ANxN<0g=$U_J$Q6T}!Jm(zvZ4E#Vn zd~I!?OZd1Kz>JXzl#ys{lm7b4H|YdikJH1pujK5#^_7iQvbPKab*=1C<%bEMRHu;?J1CSx7DNv+8RB6Y?zRjU&O$lN=v!xhwB~tKMl6# z#dw%Ld!X~wD=i}@FTbF$sHgxv40trdtx=!e_cdhuI~YEB`0$ZcY;s0+US3{)em=W? zh-U(pqCxOXz+@NLApm`mHUUGII6cEA4mlTYaI^lK{(qkEKj=Th7UCL!|0n(DY*Sn7 zf9O9>0XlN}88`^{4m!Wqf0%&YzFr4IQ__D<&NBfgrxFt=V&q0ohwG;n%1xX&e*6TP z$$K3=gTkXhH4~pe`VSM;-5GakE94W#j~_o#W}}6ZcVJjlOl)i%m?*Q}$j;plOuV=QTNliC6zZ>-J5O%Xq=}Oz?>4Y@^9e+cel(bVG00wabhJhs zo;Mp*q%yKR6ELOe2-&@nsnb_qxIi9>Jn4k5L{hzj|4YY!EXViddT1llTFMfVd(7RS zsfl^rUwwh*Q3?Q+`XKVfDc@Hj>5n`UaAzydg0Z*LX)RVShfLJ%yYJxfg_C6!rt5aL zwsjCMQzPx~q#VGR81z4~eaVV>bEhcCDJHa`0T}5Qg21KyzXz^}WWTEG-bIQFrpe1s zyx6)4`;~U%NPnDYt zBHnp=-hP1Cg8C*VF*(l!Oly~7%T}=<%0DbPASgI2G7d~m8QjLjf=}oVJ3$xLSCtm! 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�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

      QgG2;L2m+uMp{SSq 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

      OiJ-L?IIU3QC6P zl!?{G`9_=31(sAYl9n<$CZHO;a0+y`qWCyKKZSKbb!JGQbkEJAwyrMez=!_c_NFR9 zPF{5jxSv7Cm6kNa?kegKi~Bx*`E@|j)=-s~8XBBZTVIQs4rHZ;veVMuB_8Rfa zFaP-R+oyLx!>TFCNPO#w_ zuI@lC^2L|kf<*96xOCc^8!C&llOloi?fvEr67hX~c_d&86Oc7d!2t6CPE^MI0)dPM zzU4etb;P^Gys{01(TPdfM4XC38w`sP27iN2EXeUcZ6Kt>fcLl{Vxkk3F)vNQ!4(sC z%MytgoiRAuJceeVn}i}HD8>BW_@AqNh;s=pBIAFIm(jr~IC=n)9fzACIz#%U%Y(!t z0lQlnzj$y{TT4?@L*vLPTW43)>1z;H7e{7yHW$am_&J)J>fVMMPfJUSM*`-NfY}Z} z2RIVSB)sXM#^8tO8m9FX%m5x!ZrwWK|K?J z0P>UD9+>>64%k9S{zE{M0LLYfMK;vf49v7qTMfu`$bTBos9d z((KHPl-!yQ3F66_p;Fr2-~Z{$AD;#g0^Z(SUsqO~n-&`xU)X>@4ng3xJQA?zfB);t z#{o%4L!Gd;tS~t`I?&bK)!xR&)|N*C&dWtm8%62&QH3M~5w=qeghqnspwl7Zd|)AB z>l-u*e?=K!NYWcDsFG8ZxD4MxS!Dx!b6V&R0JRQh?@jCsS&|mP7dBFbp#Sh`wQqo#Ft=fb7@9$ zLeyJtYl|1pUl_V)w@O&S3s@^KoJRs~uTR3ONOweb~UH;Ho(rgn-<1AdUQ&>oMI|`VQf14URX4&(772)T^|gcqjuPGZ zxw*LzL?l8n1*#V{(;h?iDNeSRmXs7TS;nL)6U!6_j3Y3Q1Wcy9ruOXo8d4CT1jwE< z_qaMlEPS`6;)zQMqhptmHRiEHFKjIx%{&sYt!MB1|NS4`$W*8m^Wc$yiSfVjKUg15b3kp1T3TyVr@xGl z?GLgIg2=K*39sLawfaunw+HJ3&BK%vItJlsw|2DXTsE`oM2ZX!8DBRTQftugZFC17 zv9h<9G*wGjdN#WdO~L3m0ChY#Gyz|)Z^&2;BT|9T*oLQ46&tA4!>#n{bWs!CizB>EMmNok} zEM2i~`Jx$9XV024WAWO(+V}Kd7{T0x4YM&^S9R|%o0hCyxn#kD1#{;u-MCxx%6)wU z6H8E0y}i9d$Rh#sNWlH?243p}oSRfF$m%Xc^bpb?#Df$rg08;y8c*%>ZqNo~bh7dj z89svEzQKaH#7Nt>36Z%>rK3tKWKIQLio|625Gu^9EFRjr`n<`Z!V6+pglNB|;pBgK zFYvYUiwq5Nwzqb#MS3(eH5N4?yZAt_W7a@jVP>L-otbF_%G@?JAeaJXTsFS1|9!4S z;h?m(FviQu@Tq+fikrf}DL{ogqWAUcNg3^vb+{98uN-WY&R{ z6ICJ=Nm^@s&mK3GbPv%fBJeA@KQ$~T+v~=mJ(e93_6KmqTtH`|yL-wl&CS)ODNUJg zB*CB1j$+r~Vj(xXRN7zi=Ha1Ti)Je+DlACu6p5M;=?M0&CH}|%-MzLh5f|1hoUbG= zKj8+nc~+(v^_OWp$o;ve&sm>ZHEWV0as)R-W#<>dikzEYgd{+s_lT`5U*Fg~Q)%3o zadP8lzmAMXihfFJI+w&Rk@VI+cMN^BLP37)=rLpE6wd_&g+;~0#U~`Ch;c%}V{Pzq zyrMc+VeHt^qsPdN-|ggu4uG18i)ZtrOJ}U=CeX)>9zA-j+&W8V@4(Q==$M#T&|#y+ zv*nS1*}}%sXRcv#vQaDx&L4Dc8)6JB4C=rm0aH{Rxe$r}{=JcoO$Jj68Ty{i_`jE= zGj)J;6}6$q&{!-%wD#COSqGwbejU%Kq{!dBaIcej0C52kIU#90QF$cb3&-Xo!A5S} zcm;(mMpiDK-u}UWqobz-eWp8b4c|8&37Cv&JQ6UE1Z>440cY_@z%5L-f&hY& zOjh=?n53@mUYC21p2CSoAxzjj>2xA(uPtxt?(HlKw<>8idx`3abb5e2lw5v@CTOU) zaISB{sn_k4+Q#R75*90klz~Pzy0x_`EZ>$#0=}t#OXtLj_3M{USAV8=^S+A@A&GWX zC;5a$dt2OE8yWig@{!Hk5AL3^H7eBR5sw6n90}6@aRT6xfXU{D!y>eI@FTFj&b8wB z-O@s8J=AaZF*uP~=FnvzT89d0%_-ogTbewq3dZ?3INb5m2|rBOx$m%wx~Ard z)7ppE?Y*h{!r08p9`ug(Vuv^PW-q??_{O!{Iu9N`c<|`)ljnveW|p>&E~LD7w0E`F zq^2bVc)GcJxY}D=n3-Ez+c_Z#kw*gNk$_DfrAGPszKu;zP0vVA%c|;n-yv-fl~xKu zZT-W;!wsy%LL%d{1wcwW`l8s07tzkbvKp#_1TN1kXdar6hm~S~V0# zL}q<%b{YyKaH_I{gnpWssW5bXGH;SrO}TX6=%nCK!yh5jy8n$%_s(J%0T3x{qgd&o z@l-DWPX}aiYfDX8s$Yn2K%$_z4RmgLSWuCe2ePQHxi%{*D8$L?)+5`5 z!ZG#R1e(iC-1HTbvda|SmiDHkxOhDc&GQ(^)(6zLZ)Wh$4w0lSH`n9NiIdz2T0Si3 z8^2P6hqS)BHdaSVoqoX#)PY9==E8W0Jy@V7;KcusEGWRxo)v6ua82#hnLCc@O&zG40S5}_ zL7`Q_BLVYBz{LMp3M^J0@{30TMx;MNL{J5f?U1xTG6qG(HExkdJNkRf4Se zsIa#|!9kv;#%AUgudQsMiPj6@&4wb`*-|6ONr;JzhzNOO^ZK==rKOdP9mRP7YnZ9v zNbjvG%FRkk2=nzoAtncV9H0n^x(*$f)=ufa<)!%m zBlN$jyd*y-Jt;OkIKbZ*)r!hxNZEjbudjzKnAEYj|9B!Wi`rl zl@zl$z4~I*-U_xcetbh)b?=r<>({JayKaYb4d_LvMuG>Jla&tbHf~u* zK)xH*GAmH!i|BP_*#(7B9=0Yr7d4LT*}9(SYc_6oCXVrGD zU$t`ivK1@WY}&H_>YWFA&nmbj>u>t<;f)KY)b?%$4)V%XJQ8qJaA1JHpPwH|a+2q$ z`p@Kfc2-6jj|9Bf9JTe4K~*N;c1B_R!DWl4Pn91#V)&?0!-tO?t#qrZmguF7-jIKK z%f{8yrz(LqV)*c3!$*vezfnQuR!bQF3+=V9sHrZSHEH~)5yO7Mf5V24RDM-UbjIH$ zk8~fLKe}V?M7a^ef5Lcl7&&HnYDEd-|C+j}2Uafk4s2ecFnR>W{|G4GVI#&W83}T8 zvS_=l^U(M3d7`;#jv}r<{73ErJQA?V>GRib<0~vH6BJ)ty=LC@X_FO355p50K76#? zctsuw_>GsR2jo2q1}`cCC=k+$voh0DQ?UaiMTcOA@bmrg9boi;WQHhp)&R3bn0;g^ zhI1FE_jf?W1%=`4(F=h62=$EXfo`A=fuV9gpxh3_{-X(Syr=O5wnvDo+|QT}MrQ5` z;x6_XNBph_5dRoBVOJ;3Kgi&FWu4g7fZqx5Dsl_rQ-JNF!yq+o3UnEGM@YQ@iNPl_ zsNeTI67Y(Jv!+j<`t$VZGiIz#Duaczyc`ejL$8hAqm$crZe6x?-t?K%r%jtNW9E!a ziFpM@C8Yv9)c2oUuHD|Nx_Q~kRkP;IoH>2Q^ck~$)=Ef22y&@_9{h*a=eITYZ(6=! z(fm2H=ggcjbLOq&rfBZ;u?FJqRxP!b?I2o1#XOo^@ zetsTXnxyg)@;jw&!VbqH0YiJ|a&3l?eBnTZ2LdqwvUlcJaxSncf;e`Rr_yrCn38IQ)dsA7gm$jkp^)tr^DO~g16K6MX>cPgNB0^n$l!w`~d)H1M zJ#_f+F|G4YZ5&)YeEg}(inLoS5>{nJdm8E9zH<8b;X{Xyow)qM939*dBMVW|Ep4qU z%?Nijd~o}^whExgc_iT6tgOtebnGMK)<>hz9E$pbi;xxA2gYB1UT$_qI!6T=vIsal zJQDDX8JnzIyGdQ4qZp3_jG*7b>zQVaz>b&MsB*Up}Z8UrJTkC0PPm@@yU zvAM02hu0ef{zjYclCVbaid0!7(P-?UTONG6Hg7zEbU!A5qBn*6zE>p+_q(z z!swA0K5FcErI~9kKGZiddu{6i$=A`vT)U_H!o=Lh z9y)n@hlEE0=J>x6RYc*kudWaj!+V?!NB_U`e-m^JLfQew9XSOHa|0yEt746=~3eW%%PHM)aDmHwQYh`dbUbnz9toI7 z0w#z$c>S>l`6-U6BLzJc;{wjX5fHG ztvw>Wyu0P?vulSotz7i8lHvpf<;6A$aS13yPt^AI_~;5A3E1e=_T7h$s9e2r{@O!5 z0}~5tTL(vH79J1YXc9KpWhZ84C%yGRbz{O9CZIrXAMg+BLISO!wxYB!Gd?mTIEb}i zG#MQeFy$J@_q@!Mgt*w)n3(A3sK`j7qao=AQSDTi7lOASJ0mqYF(Dy7J}!;|_lZi8 z`H+|l$5&8Nm?3T9G zl?$>Gqx{_6U7a0moxJ@5g5JJuZfO;F_WkkeJE^#(qAV{pCd}6hq3y0t4o)7v=zzl_ z+(!Ms_KHQdg*oYQ5rLq)yI5FQ+q=H;4+sW(QV#OF-VR||b`~Pn0{pzZJzu>rvvzRx z^7Zoty&Wo=q`R%QI6EyNIy59W;LYn-7Pby3GsYqP(givt;^y*#jHKAuh!Agk8+#rJ zm>mx?Ggwj}v25cRGQK*W_nK__&zq>Rvv5_C*0j zAR|P$kmhe`X=`h4smxCa^YFCPyLaW(sZ%;h#rZJtWM$&JfyJh?OVn7I6Bq2|YVqX$ zrBf$PtDo@#9942s646`RB$BpTK}JY`v(1YKx6hpdRIiFoEXsVw#?t)>>ZFp^io!@A zcT?j>cQ2jR(o$DDe$fl|zmTv-VWTiAue+lzKPiYu0xl`Yh>rkLIHGIOU;*_(?hQ@` zU>>NPloV2wn~{h#ps)}g37AI$#{K}eOB2p-ZTXK5EuAxU;&}NnW5&akr*mQ7L^KYI9>F{9-VW!6{FQL(6y=*=R*;}cg8FPk|W*vvOXQ2-9LYL>D0-}6BS0oj6Pav%mm}~B%Co5=<;1{xiKFF4@<_lu5-@Wq!fr?bPpr5D@{3`q z=Nhvpk;{=49pRCHc_iTf`qv*H2c)fy&?Tx%bJG(eD8<#*4k@lKUjFX}cqHKd0YqB% zQvEtAVt@+pRb>q27a@kZurBjRz%mygT!FIfhV~mC3D`Nmq^P?au?qFY>8XjR+#l@j zXliQs=*E@v+GkE{pFL}k2FwwmL|jvlpA{P#8shG5V`A`7_lowpQzuWJ#5ut^sk^PN zx3i`sFU8*{z|Gyw%Gf|(_twR;Cr@Z;X=rE~hXOmHyR$w&Bi_Wy%h|`v*5al9gBzF6 zXsT&wsH5UAuDW{CRWc&Yinx@shI%JQ6S#TCmOy z?PL4)Ad7MHrj2V>uUWlv*^1Sh4xhiJ`|KqaqNFqGiT2TbyLau{xpmu)%^Npv+OT2! z;Zs-dK7L_r!QxM(4T*LS&Z!?idie061N#pgKXpy#$#WF>v~zN!02NxZ9nIDGX^BxG zfqvdN_WR(UuRrXUp;Va&WN<%2=>-yaK~`!~LR?&YJdR%pi7al0iWkAjz#{?Ej)0>B zJqxZ{O`Yawl-$O#N`#&G&I3p-`$7A->#ax zNINTA9tjxn9SDN%=>@tBT7FTjfp!Z(C}ap>rCNu1osW|4MI5b zc_d&`1&F&D_x^v&{~f8h)w0WTH^FuYiv4G{jpo9OmIaK?N*-{xi5&)(4cdcvBw%C$ zScoMbx>Nk(lHS;vzOePT4Twp~uEK1{ZuN&hny17g-8&@20>{&@Xw%8kyNgcV53l;MnF8W{Q(hP1qG<0 zipH#J1WzyPKyJ8l_~1&4QH2+kc)7;h(_APJ^V1;|sFWyNso<}{Ld+Y%Tu|lZWkkfL zg#Rd5h@>Jb0f^w+N|f>{FUR_)zYdQGg*}K%zn8x7YLI+o@x2JoMcECO%k-W21|13# zHwAV<6hB5CYMJ<^FygQBnOy@<@qfsF8E4`uB;NmD%Kyfp`B}-4uL^=p{&%M2aQx3< zgy2flgVL!u{^yZ^@4VZ_8%{oU4NXYP&P#o3uYX5tGin-5TX|JqTpQzj z@xY@Ta3Z=8BVwWA1c5G8XbWUoxMH$ygHFPj*ok-w4XPAgLB|!Ctf%lqM)~fA zhD)$1kc|7EbWn+t!8;Un0(#TXP#Nuk_ z^yE5F59-Dv0pqz+Ni=-FJQDEipOwaq9s{W1H752Re!&D445VN*raKA^m&}-{FmBw~ zapQNsv~l-g3U)L{$FlF}XbnF&a~hyX<>U_-Sh@NDW;84UbXw5hDu~$j(nqT&DkzRu z&@{C53AN}g$<~=uwmr4>Kmu}TWL$DOEq8Vec;AStnu@}F!^0z@ zqGJ=1)3b8&nDQ!P8Z38`kd5%rA^4)Oum~0ONVwyFl5$KEL%&5#24cLRp;IH-O&z}S z59#@I5a3eWcqCxi)91bew)x6*-%>X6? z=V$!?Kj~~L9toI70^Yj*o)?b<%p(D3p=EOh2GTlkB%!*3m1QVmMg`2c#w-v}T`0Ss zxgcwq`JuRo6*|k$!;XZ5KGXPR@9Y<5R-_XG(`=Z&Pu4kEBlRB{`Q&E%rui@xh|C%2 z1Z6Cr3=*h<^;I*%4{Qr#pLEq-Z6QsmMMfqHw@YHf#m{|lx>AnLjUhdaw%IO<84&k% zq#sO_Fgk6(WMqH}PI@4Z1iW#lmim_4dROkec}r@pSR%>@&1_3_bv4pCe^~#G>6Ja2 znj3biYF~5o4hV^$DnE^>VL3&SZbm1!@366Yrn7C!?oF#spS))4;qCu649~PZBe)lZK6%Jb_07mK(F_0+4=rBw%nIwkRh5Iq_dzRR#+vb5CTXr>Fgmb5Sxi z0D>DPbCUXq=4LeskP%4IoRh(vs=#%?0Y+Bk2lOE_9hNA_854X@r{TB>2yfaesm9L` z9ntyc3|hnSMuFA;}MF(mvMV&))}994*=6 zB&G1X4if4x`1$jP5<64UI=OLBW!P|1C3{f*y!XRElgVw;ScgE4i3}=C@Z=T{cZsU6 zt7Cnm4jCC4iAWYK{3NAY?P{$uQ?X)nW+tG4)Y#47oks$Oiv&o*w13do*9P!V9tpTO zE7Bss&)w+M$%8uXUOoYq2Q;JJn48+Z2@D2qo3DRVT4`jmN1&av-Hqe=9v+?tFE}~6 z`b1?F3n;_1ASW%kq)`|hoaybVe^brS&F%EV*IohfCAAGm=B40{+REZGQF>rwzo>AKqH(rBMB|69BF=~@?+R}+#Qi)3Lw9fZ2@NWkRYYHrAk z-m{+0y3ExjJKECm7>)!y%$A1iTicE3ck;fockUM)YtfpU>ML_{3S^I+-Z(1x_RvVc z)>I0L;0hf2YS4v#0~rF6zDtS}VxN5X!e(ZHL|n-rHXEbE=ZBGKl(SFHM+)=dk$|Cl z)Mxn`J-Br8gtofIfvuZ1FP*>e7n{`djI100)gb<>UEp$I*Z$+`s;XLN&TAamzIO5K zX|rwwM#U$kW@G~aGRy76;l11UAJ;sst$p&)NzFsc7cH4RSH;mUBsxAx+#RBM^Xj3k z>o;uObwumjl{4sec;$-4)0B2uI(r3%b-GMjdrxKC)}4C~paPYa_O**Ab{*fhZROO- zN_#EroZRos+8<=4bN{)Gi<_&ngN@nKYd20`JbY-|=A}O?t5{mw&EIAG($tnm0%mFe zUP!-$r;kShru~6O0xrx05F~S@)HaIy`+GWD>PoT_V$;j(5JE|X5OXUk8I)>`=+ozq z@A||owSt`F@PObfvV#INHJ6ZB>74V&m)}0V?~}CDmgJ^H1o`?TAOx$dm`4KUk$~$O zc_d&S2{_E(%hL4ejZ=W?-ML}i+VwmV@TPr+4h~LL)xevNa&xpYdUE^1In|w;)~r|t zq~6u5*KOUS2SYfZqibr@oE_~0P7u^JWquB? zO`hDpe*WmL4aneIwqnJqb(^=Jd-&wJ5d-?INws@zs(b%3j|7a`|4H$2(c!^2`Iil|~g3UzKq@kqeK zM~q(J80a5ZR$f_?f8prqYx}p%R2)0}ryqa(0snsbX~c*z)Ae1PoXaYzstYuCu2{8p zrt3sji%*{!lq1ok+H%FZ zjVq@p%FEOIxB}=S$H*_Za{rMY(p1XIGt@S%TRngJ^eLl%`U%tj0I=iHV-kf+1iL(jkCmIjBLO=&*dx=hsH6lX{mJQ{o0|imUsMl{j}7b1J=b5nIN`h=_?d%OwloDO-;SXSc2 zMjs-vc{q@8>cCCFjR(}Y>>kjWy9)ECh`T^Xip94^_kvsqx)J8qq47wb_|{kli4<7k z^fYjP^n}oX%fX<2>^>+P17xBPJz|0cf5)bxzZrD$jeaE$e(bEQF0GXg4x$TPg#CSO zecbbZ_o3ENd*_zbn-1L082Hdfbd)zi&=lxcAn*I5FYMm8Vg7=dQzy^8p3pr&e*^oP zFdXD2EA;~hSFf87ob8Ddl_sf_zhmDKe66^-fx#}Di>H6tvU%B(=?kUFctj1CX- z_YVq*h>lB4PD{^_EeKjIl$+CBS6;+Gw2%da2;cntq4iH(g9u=JBV>4|{99_JCA3u7 zx6RfQ<2zz(F2fF*H(QR}nq>OpblO6ABw!v1cwpdN zm-U5{yEkp%k$|U60g(9g6$dXrG_bID_44)&r0ui6ucxmw;MCT|i{{N;x<%#M{m0K= zQ3}5|_#AX>|JVR}S_?wG-29`WLjAlvy?q0MLy^KC7tgkD+8`L1b)`5E~@Adk~}saX@jhJQ6U8c-jvUJuwvD1HA?65B&Hn9%HBjMFrDG^dZp@_aJ*` z!4{+-e2WhP6_ZdyJOGR4AOZT7&WPNFNwCZG6qbaHNr{SE!Z>MfFNXxm6rr}SNT8C= z*7~B<2tRkv=sIXZDEy9+1ki(ImxVZPD^Cq`H`l*)MK`=dN|72&4dU*qXP{5qToNDR zX{vke?B(k&sBZdoJT5JLH`v?Nm>cHjYNm5hQ&a1T52k1Hx8RnG$<;L_jY>y~o{^cw=w0m%Rr&rFdOA1Gsse};Xc`xcV-k{y zjfgH5)mIk8xfi?1d zsRIR9aGQ&a{|Ob6M*>z;SJS-eM>(i)a^jIn2LJKpuYZZl;sZP#UR*nYa)QUz&RY?C zqY?l0|Muz2=hplvFGuSqm$i8tR(b zck~Qiy|!UUB8dE9A%xtpb=Lfozglb!PnI(LSc^b+}M!7 z0DnIpA73=TfWRP}F;PdTgU<9VO@stql$8?CaKd6D!XqQ2WNd&qBLdH4NZv<~6{aZQ@fv#Y01pT1&Q#N~H-kq4R#H%!rxgsGZz#xkbUYF;a@L%7u30jB>ZHla$}<=ih(-b+ESsdbY(A{bx2cB254QIJ|uF@c#XfR!ZZpu7D*IHDvw({m)PD-bos>Jk0bT-@bUsw3@6)<% z@y~z#{$XICqdeKi>Z#71b7yq2xl&OQNl)Lq_kaJ7fBolYm@h>Iv2U#P@7+3m;%am; zz~1w6A-?HJ*OyW8Pcy<+7{$ z_wHX=w_@X}sS1j6a`F>rZZd{`3;{>-r=v4h@75K~T}u`&nkheS{FpKFlV|NUG`Ded z_3)$!dKljAZ(q^ev3Ai6rE&6dW5y^>U2x)=5jwbevcPyzTft+^LmL(>n58H`9&O6p zjW?edm|5F9yR+bVQA=Bl_V%r-7EYTqQBgs0>Y_EL9_SgF6J9S#K1y(J4pCjdapmI0 ziAcVyOfmUfoTBLS0367|h0U>HK9I)u59jm)kGB?YCyTDV#P z0UilhPC;IIfk{|MSa?JPtSI0>KfnA&9toI70%kH1YA1r@1cG8j3}y~Tr9iI1;7nNL zl$c9UpbW+jy@tq?*DDjMID-(KZ6X5MX!gcB&;(S0R5ro)bgEDaBL?CtFatw07s$rT z?t^p$G#7<9}2O>1+CUFalj$MWFpMYvd+mn#CCw7ytkkN2P z=DM)m2lvVLK|BTyi4Qb7L&4BODOx-du&X1F1WZsLFyhhbY3(2xNA1qGn8JaaFCJ1R0+f&}B*t9pOptRbkZ@=Aq0R%m4|Xa~)XBPZJ67Dq=lkw`66cr>3Sf5dNA> zF4LJ7XHue@xvB2$t7kL-G_0v|2_|$$ z7q^DSrmBSGYC(2fxVOdY7dqE3o;j&?;?(Jrw_ljS#_rlc3s_Jf$cXlHef3=T_O)}` zr%#?ed-BrVCof;yIlE!eHWFBIa`+p2!xs+X68Mva1M*@cYuBFf=(gDhlm`6(G0EkMT6O&Ls;3n7AqDnc3ge?#-feRKRIS%1WwM9sQVDg}Zm-!3Xm2=EC`C~9vb+ac)0{~}BYdUKmF^7i(Pi{{Q+uk)y`QDNS9x`_xsPrzVzm^;j%*%~d)(m;F3Wta*7H{6Kdgcll@n4w{9a&0M)u}IzY+JW#)w&JaemSOb z>cX|#I=Xt#Uz*@7i{*;#ysEk=Io!|H*3^(k0)|nC1u9a!v+Qusimh;`d~!-q6f>)a zz#{?kNWeT2Fv2(o`g#!Kiz>d2byP(hIAH`6mX$3*{eK<_nCyr$DF`BLVYBz=oC-+%K%miwg^Icd|9LGVgqI)CB9C8`2wWrqOBZfR3~w709BrG>LpRg&v(XSy6s4dsw|Tduj0eg^{VbxfNCU^zxw)DHiV8Cae||=42$t zM}>!m1P7sL)7wx)Q83Fj@Uh4|PKsj{b;993F)<-AF)1mT=vdaEQ=wj{ztB5zzUS)j zAqp8Qo{S5iliQjzp;0McpoHb+=H}**g`BYr#Tk%wixNX%B_QpSYWXs*!f=`Z7zMHp z)PotwP^X-Q+sWuq#n}WPL?3+qPxP+Epu;ELt#c-n{t>m+ZZG@0nQKlVzrR zTU$%z!2Ug3cWqv`eA%+a3*qTowrbD0+fNLLyLcpEa5+49T$~^=IGOtB3|m>jb`K)s zY|Da)a7Grwq#~rf2dl`jCGLF5u*RK2W?{v z$$&eGCfwaOF!-^nAU(i|M*@ai>lvUX?P?R&l;^y4b9)mM9^mfw#xD?=g3-wJkn~WZ z5%l-=#@f>Ctjx58XrvFZ5b5L;N*3XffQkQQIt1+x-`JOsK*7RnhZyPrgWAyk@c%FW z^GLuJ_TrBp+fz;M=)FnEDH2rG!JUM(3&f=ge4pRcx?*4>{_tBzYl$Aq5Y~f7(fTBw$ium=4w4R=Dl> z6JtaOLx8XxbP~?65wRZFBgL+Zp1yh|X(^YI?D=K|VKpLPUaintXk+%WS#SHj@P?i) zXjSkt(>4s3N?U7tUC_lX%XCd%X5W2$a^E52tjh9=>bgeeW)Zcvx8_-1+_-w-eto0* z@L;`TYZq^S=#!LPD5$KZl6K7{0mfIiEt)lb4UYs&G5quo-&uWrw7c1}rv|T(;e+r+ zFG?g1rz|z7za8YpY6eDFMR9IMasrP8OwWXl9F)ZOHO!A46`+7p1~FyeQu`L+$E|6^`0hg8Sb>q+;%MJ-U>Ttze zKxdMVITpGYWz^B3g!P25C%IygA6+yO#M`=pF;BL>**tEz!TeK}z4bvx0R#G~2@41zetDC1!P&m=q zp^E9FqV#9G=g-)3&%n~b#my@qG#W_ZUpuh46_Fs)KQuTX2t|TosZ1ERJlVPh|L{n_ z^sH$kq0NF0Jx$!BL82FG9niDnw0AHuYO0YfkkQ%x01E)|H`hnpDPRtVmalXgjg11w zDLMm4n;zhtiV23c7Dl0Oi|AeAcj-nw-_dDWc6o6-9j1Fbb5Evs)2R&OS$`&m0pC;> zmx`=ee*;5fDXWFpOm~Wr(vF78=H7wMoVSjZ?T%JW{r!?ILUnFtREb#F6fZdKB3_Oh|3 z8}*jTzc z-?y>2b?(RuAKO=fdB6cJDeV%s6~@|}K6~FG-0k)AOB#rAIeXMU(dOCXn53lC%v^D2 zV|JLEy@7FFh=aby-kk^3j%{Cg#n0-IUU*DwYCrqrk^1(aG%o$&**k@JPTL zHea}`t#$z+#x_nKon4}=Fi%^@AeV<4+UHO2Ke&JYFFTH2Jay#EV*_&=XCV1@H3_ob zgoVDmaqh;&)2Fr1XlZDjI;pC1^{I(9l9ZvXc6H?Wg@jq$zjgEGEglINDgb0L62mz= zH}nCDA)+QBxeg+Q5p}fYalr(1x~7JYA;GEi9&uEsxb_?;|4E;qq4Y5);pvFbH4v@S z&E!9+1QcSxGz+n$Q%J>(``VNDBuW{bq(2R0NZ=B&yZXJ3-agx_cl9CuiT{~C0r}rv zBWUays59TB9NJ=PAnfa<{ekow491baw${kDp(Q2w*`kRX=iMLZM)VVnZW7W>chxis z0yA_q?8C1voHbHm*1F@@YemR&u0^PYxJ%k%_RBUKgGWn8O?>@m;Y0=bX?qurvn{Jc z0s{nmm$*CT_-w^xN44k3&E2$lr3Q}#Jp8B8vviNj$&FrWVPywBGULv|A9w0*7`M%G z>Bt{`_;JMOVRDL#R*V}tNds9ro#NKu{ZoE2y*1%)6AsN7Hf-$J5hI5y$c>#oZ_ByI z2Bxn&B{fDfhW)T-?a06VFni|MF_Xsr_`|RXN<0#93fI7O*utQTqX-$Lz9KG&M*>br zjLl%R;7dvc$nX5}-=Bn~HNqBQGh%33>&kKyBZJ%$02G)D5#8C@{nzJ)DnU_YLkmhm zwbpjDHzXybMTSS?{D?kXJsn|XZDrXBF-e(KZIbTx##V7tR!(xTxf{Yrl2Ut{Pwn>% zceA&)wsrD~ENtzQwwH^V>oWbkUi(BuMMo#_NWeT2a66M?B>9*bk5pHZK6N$aWn^V* z<($97$B?RIfMaBcpvrRgD<$M`r2?U~vXnKFT@94T#u@9_B%o3B4yCLj=}y=P17%eO z#!+Kuer|ReiWor_;73Tl3?!DFGswb7VxL6-eWR1*gc|+`nbu7a0$=ImxTZgiM*@Cq z=99uB0n>8n5J}o{b3N{yILUrv>xZHvX!%ovjkvzLHdaSVoqoZ`-1=fPW-9OOXcUPG z^)%He{hke@9vq!Mz;bJ;uX0p7&X)Gj=jSQQ`de3Ry-|;`42LN6o?Gn`rO3E7ADNSl<|Dbvy z%zi6r&kD9SxTbdM%pJ${rVd09!hynhP-s<%YlH1vOmCi6SKqOA-Udwg<+vX^ZS$YTVA}Vn~kC6p8p$lp-CBM9> zs=a)oywaggMr4~m4rw?w0bB-aBj)9K&0W*v<(IM}ABN(L26`tBT@XJ_!W!RK*G?}| zmj4-NL*iUCbfC)VsE7&wR#Tqd{kvNg<&{CFD^tU{k#2!XQSnH?6zR_+0hjPdz%WBL zwfy$aKmY#wr@_81VO?ofbXc&TkC(fP^P6Opk*PyqVC$cM{rTr_ANqUR8*7TvfRy9s z?dj_3;t?Gi9aW3Hq5ZGFzWn~_L!YD_>2|5np@F{M9;jsN>i;%4m`4KUk$_pzjVh|V zLB$gA56MMT#S1pBQs@Xw6jS9cCYAAy9+l`p9f(e!K*~_!A=kJbTn7^5)N1Rf-U>|W zh(X5~nu1L*G^Vz`u{zV=+SJI{DYjhLKpiTO2q_q%*H^?k89cmpPV4Bty{BL22w4Uc z5+&JPp>T;H(8|n6@3yw)v3)x>ZB+HBt7Bz4N*GRPZCzQEubYL*6)RS*TD5vDj|2=L(`VoT0l;|C1lez%TvR)F zaQ|WTvzM-1y?Ga9MxMU-N+!t8tSB#QQ&VeuD-*+KXfF(mj2VnC4KFDw%17a@jFhC< zP=8Ms2Rj>UYik=@hVzRn6k~h=a))^&VErIL6KcD__<<@tWwdor6BZfonl3+j)X0$| z#;?{%L%lQ}2{vSmO`5mkkm`wZS8tU9F%Ixf3o+UVO1te+vxjFGD|WdW*swT zG3Oi*1qB9-m;)+iMG+7r=bUrSIcF9*hecY8oQyL%d-m+T&-Z=ju6`Dd^PY2E=Q{tN z-g`i)?tYdJUES4Hch!CECc&@^3JNKuKPM;A+w`ICJ$)mC$9Hetymebw_wIe7@kIyH zeb9KLx_@GPbfAZWB}$RKdSzhns(?Yc+4yWy|CA(@28;~yadWh{v*DS5ffX7Y92y=T z!dGg<>&l2-2L_Q?L$s;*I|x8od-vh}hj*+CC53{Q6=IB+NZ)_#s;Mh)=zISjU5H%{ zY+|^S4qV#bEva#^Z4&^W{nHyOEI z`-mnLup2fx&G5|*L4g*?F`gd!A4P0_kYR(cwXYXlCG)Z;jLgrtVj1HrI9>59nz^$FU+HCG9G&*mhTgt?JKRq% z4F5dpg@0Zv=H{fs??uD}-aR}lc?ikdg%a`LyJ~Co&Fh!1-F+pse~@5EKnYVW6(=9= zi_qS-d(E7AGpEYVx)?3)rT2v6M%c_|A8vT9yl3xT5?1XSN4GqaY_} zJE#AMq>bzke`zG5D9uemg1;kB*Z4gKP-6i+glouV<{HlgOnJ;;62x2OnSgmF;Cv^8 zCyyWA&^)Dm?WGO!kGvU=QUnwS2NCq9U5Kh%#aWSo0YM?C4@mH?sOXqjtRE_m!7~99 z*p_RA2O`7;!ya+l4+{~~p-Hcb9qiPB5*pdw#G!-n5_u-zYZ@v%6Yw$hD^FgTTG~0f zdICBC__wGkJ1Nw`K=01gX}N{vzdcn z0VCYpNWu6Kj34PV**RqCQ2su!WLyWr079?7HuPY=MYtAG@|YVhQBX3H*d(y3*$jm8 zqfkGan;woPsVIz!$^e?-BM^LmMvb%%0L1E>I>c>+Rby-&-^x)btEPl!0`@n5eDjK? z+8N*U)YP=}bWk9}#qf`R{pUY_eC%th&WZLkesuN1FY0IAkU|(2pU~MwK8P>>{P!PU zK8Twt^TO@*ubee z&>VdC+ounG9W{c?NavS#&#G(uqN(fP>JU))%f8Ru>O@W$Cwr!_AdTROOS`uGQ<%y%ECkcWqQgf#_8 z{&og;bhHp$du(b2xwo%BZnh}2KRh(lTbZ5WXa3@$9?t~KGXe8Vz>6*7VxZm$oEaAz zS=LnGazjIT^Rl^9C-Y3ecOO1CG%*KJuss09XygrVs>zH`&rAq(u>$r69K(*zt{?-X zycz022U=ZqS#f@PY&d8DSPMXt$T0!VGm!X!8c#8n69nScu#@uF&~@G3diLsy6Aom=P5AK12K zg~GJSlN1ibHxjgQMBdpI^Thu3RZZpHN{gq-PL!Igus6T1s+y?6*!Zp*J1c*a`$rFM zT{drujMM~~X|oI(s)?u=JPhOkN`38|<9>bL&UH&>%Sun2Fmd9P6UA67K#o9;J8H0b zOtae^m3I8I7RN+}{giy$F%4eDt3_AP9;yL0NmYK19sG84v2Pn2Hjn350| z9~X;knYQ*;uZV)K;)ln!E(KLKU@SZn@Y%~Z@9I5yuK$vZRZ`$)Vfrhc37AW);hBJW zCSY4DkCddO#H1vIn>xEc{Q2eM@Q|pXUQm#h5bEj7GXcB1^Gv`5PZluSf$RseAjrTV zd_GP#N&uk7iBPt7qys>?fajNvFo82U#TwZ}oQXmNB_mYfkV6IwhIQc{fs_H{5O6b) zn9|o+#FAMtWaJtUA())RWMknOk{RejlATkG`PpdJg{yr?(73Nm;21BFlc`14 z6wd^#rglQ**f}>JKLoSt!5R~h)7@T^o8a$kX7KFJRV_7D)srU=t6SPTy10AQHB=Uc zXLd9d#zcAZOu#U^v(w_E!oxy96CU7?3j$S&Vr<-G{v)dd#(j2LQhZEQL_~O4XlMwQ zBI|)KRKlwic;mwS?DUi*;t~gwIIUIIfiw?_RZ>i#<2+dZga(d{AvtTn!BA;G~x*c(VrjgCD5$hC3(&qzs(=lYQDOQcoU zLD-pATwIu&m7bcM5E~uEJoU^~&h(ZX2uNGNfBD!Vl9LiZT21>0Z4sD;&P`}_3I3zo zXI6U3PZVln5qV^Dv&I7R3})CumGCHDPaW7-_9qs)$G$*yLWMjNFwX?cGXe8Vz=#5J zxE>%XutBs^St9HYC=<*iEWG!(Ere08@JzrZUESi=hU%h}gy^uaumERkBg2<^*Dh#l zpE+}0muCXznSh}*bOPgO#*s^r0J`BaH$Tq=jGAN3$Qeb6uuHCI0(cB}Zax}OPkyv1S{=|WUNA~a9xpV7kMV<+G;oLcM zX3w58cizr>&&8cyex5duuW738-FI})wyo>etX{fc!Mu5M=gghE^z^OA;_h5`>*u%6 zY4A+IaA5)00(-rhn80yE&&|%xCjIBmP{!SZ4q)i5q2Lp|Tkr*N<~c2Bs$Yg8Ol*hX znSjZa*VvYsTg3s02v|ssdtL3qR=V3XmpyPOVsZvNW*u0>4!y9ov^VK(v5aE?LzY*> zjVCM={X*K?#Dfv4X1+{Lh0@q?k}w=IfQg-h;V+%MI*=^%Rm?sCVN#K7A?izf=Hu-x zZmbb^BdLh{oU8+bLIc@iaWBZ%rzJhMyhTI_M-1zT4un|k=^q?wNV4XcfW5(cOcAM)3KWdcVC``Zp*(~DqC+t1V0nbhUYPRZIKA&4-U3QVsx6 zihixs0lO-dsjbLK40dyMcXM@icJ*L6K0FgJ=`Yi9o(Y&8&akv#iLqMCwa4e*F!`V7 zKUrd23MbD5{PV%f55S_~`|yIMmgczwhmLOBy=~vFO{=!9TrqQo!rY%XY3M#hR#$7p z{iA1(9N4)1=+1Q;lr}G(H-GNzsZ-}H+o*Q)k$yW7m*>CwW%s7ld)F^rv3B{QneuaH zPn)@T%^s~=kDk52iGtg4eW>2?J-d~btWjLDaN)vv^OtVese1ADqvwWiAVu}|w)Tc( z+Z)GrY*74p!J?&Wb{tW^r2ELg)XLF~X9C8GB?ZUoMkC{up#C_n*>T?13NhqdC$d4g z4(LH=IVviXcO;OO|IWY9Y0=0gr24djEEW3aEglV<{k?($5) zw0ZGNzzIoWcqnj3u5+`!czmApxN&2~jGZuXr=6RBNCZn_r~M0CRA)!@aV5ydju|s% z+=R7n>^=N~!y}`jqUp>a4@Y+k&jgGklWx)E8ph5}j(VO6_?r|yW??W}f@cD54V{4uXn4mvoe}CJM{|f&a%aLWspl@XqB_zi- zoP6kmq@{1h!y2cZ3352!2=q!GIN*=QkZsREJ&do1jCp4I!x2Z5@l3$vKH`~xbCO+5 zp6uCo>&Xp0O~tkARxeR~^z`Pl;OMwSsBeCdm5Yb@!}Cgh{$`hsZrrkO=g-^y{cZG5 z8$?FO;zn5PWvX|}$l{@1k)Pdj)nhyMY~OZ1I@HeU%AL@NC@jDFB>Q_7DL&R-Iezx9 zwbTyn-F8ab)ZNbf_M4!P2&~7-Sa+k0R1ZtjNH05+i`v_iw(v~A8R_Zi8EBatHji{0 zRl|5D;F72WQ8&*7eEs3Y8}5OqL=`RW6s85Ix5hg$e}*x@7C& z8x$4+1HL{vBY>BZ@-+bqnU=x*7xQ)#8f=}VR_9zKB~IG@|n0?G=59bRnTa`n+Q z-BT;pty`{e;>p9Sw;eov1K=&HOz;ee^f0-$COp{m!r_fu5A2+|IU?BNp7K36PjBdd zRg9a_BRjXJ_iBP2o*g^5W8aRgI;kO+hG%t9g$Mql_QD_o`+`UZkJ3PU!xM-1@85Rn ztTnijb)MKey1_VT&J43D$n&>;9ph=Kf9m+=4V%wspL_c1$-V^A89L4rO4pHlXpLy;Y@!x!D;={zi5X)fkM9iHRW&Z74XM|Evl@ znIJza9l1TpJQFaH6_u4@{~P$<|M@c53oBkEY%a^o$cg8ffQQA6?G2GehE5*k)pdtu^TUoWTGJ7lt zL?;DYfshXjeEi%k7N@(|Ss}oVLj%V=+=Qq$4R<`N%RKb^@BRJN(e{>Dy*v{z&jj4k zTvd|n9pvR3FKB9|Y+i;hf(Mmij3TkHrl~q3!avB)?Akrc@N%Lwr<_J4H6p$%>hEbP zEGbC~4{~&Oy?0jQx=BzDh?9y-O2GsQ{qOztt+>1}Eixu0EZD*LmD!{FkM#XBGP4jL zDlEqEcfZz!*?WgXgoH&WrG|UkywbaN_Ow-Gd{Sz9W=@BwtEbN2!_~nnBsxAZA<`o@ z%IBW$lbbhQ`38dwJh`K%)F33q%f?XO)FvP?Ju})XG|~U5!IO*ouDW>#ghh3yuU~Iy ztb6tHm20aM0mEFBcS0=D8#dmrQPI3?WBYe{fw1*iXLs!W?{(WOvH= zqwbPQMpRIU2wy4hp34*f#*h7ob>M_hEs|0;KFj9k%=)fQ0{WrKE+_=x^~3*QaIhE# zE(3H$;LHq$ze5Lrm3by$TkoLA*aUHRkm^;PgPYf_-@N0nn)XG_W2zd56jv;sA-m&^ zy_;W1hr^6Dx0JVR-o9u5!NbaGT9?k9+Hq{}7Df4~vU^Od?3{1R-sf+od;6({gOj7Z zjfL^!OII|`9XhyW@av~Wa*QbgT(Eu^yK&`Z<8laHC41WDbAcW^{mO8 zy;={<9YXRdYCxk{2l6vPL582vnIoFNtY17?YTM39R2#UsL72bNw^AEwcvg z;ToVF)Ea6l%W|^|0ELmDYdAkU{OU9u&!?P!suG3sP11v@7i|Lcd?W)@BdMSJ%+!a) z&M5N)!`GXaC4jb{Qz&Ir#0EbasqTTe%GZFycoSb(>OyPLbK%Nqk@6I1irx`sv} zYK8arb+c!_D1B|AoCcVe5Qd*pw79Sbx>*?m= z?Bw83R*4Dl87ORrO{cn|v?w<#H6c1Qz}Lsi)5E>Ayn^dNG`cX1O9cfvS(&NHap55- zL*nZrC@rg?8K_a=I|1qxW+VGQIX*HxG$bgHssm7U5Tf^`#(k%&a|sptDWc*5u(V-o z(-(B4|EP)&Wg(%K07f%YoU!?mRgQ)pJQMIX`zor|2nuApm8^_}5Dy!J`&UmN+qPlT z+EuGot=XWGUWQ5xB(EvS%*&5(u{6{@ck=MA&Fe_Mdc#(m5)^U-hjLX-VUD0U#>?{Q zO>O1FyEd#@4LO*2Z{XLQgkMN?bzXs>Jju=Y{#7;A1KZZES+$ZTSg#%x7ZXGBs+83H z!dQ!^x@S))@5S^hLFBt~)rKQpC__Wbr!p=iG~C(h;gxe3uC$t@EAavLy@4W>S5(zh zhWpstnDb1)XVq2q@7%V1`;OfQjuA^J&jegjQd1=Kd9+qEk}t^c$w%>bmAKzuT(HM>TjR;2*w6 zv7OQ5l~GM9uA;Q8vdYrbBgEk7ifI#n9QFP8-~WLBzW?#3vFmsy;EPuUCE(I77Nn~# zT&y5JiD(eUjvX^@+(elLJQJ{=ueaBCKt}fy*&-P8sP*b%Qw-2=p}O0+6M?3@rcn(<7)JQFaf zA9?v0(I?Ts!U;(Hx2$m|A^~vGfn)L|FW(5$C)N`?k_pd4Gr(KGG7UzUKAD@69)JmW zCSaZk_;6l9etuqFK6am>4ja=~>f5$#S~PFQBv~10DH&N=+4(*R2}wyQX=Ivpy}o}> zb z8WBzA*ih4*v->x%m^VW~e#+!2lVm5!$;`ZBZ0qJ18XiHL>%hB$izoLkUNl!>`jjbC zCKFGY>H|Y-XHQTG^$*|^^z%%>%%+A#3;jjBk4q!xnSfD@hax0o=+kWj*&^aD+%E7J zVl2iNVhZH~D4iWGwFSvx-p;O(H7N8Y0`P?Lkiq23;ww0gTT7EeoL@h>c2O_1y^D(V zFf)h~_YCxjn~GwCT#fWD{c_>51FD;T9gnB+Ou#%7FwX?cr5i|4956FjlY}UljG~ zrPFuhM}Pm$De$-Azx1E=WF5YaAtBcomH|Kio1C5S1R|B_KaIm@|4aXQCg45mmMxwx zGhv+6q{&lPye=(9D=Ne*?JgHqw!L%VnSiIrNKYIyYP8hUIV+Co+_?A5@U@i{iZ+2M zP2_W6ZnVLu-7+mDQyMyV@*|c zC9V-vrsu=wfBydKhoRo~>Nq!(2alduQ~@N3fMx}53aCUp^v56n{QT?Np{|DFFq@YT z?%uu~)I#^S$}%9#z=iu8F zN`n65-~ai){&+vwS(_itGXdYabNjK0g^i=Dr? zaGa2#=;P~02tSsX(Az1l5|owYCx`ocd3k|jI3O@6I5?Oo21-SfL$_5}S6yD1Ni;r4 z!ik88z*j0E$gEb87+!Tict93P2%DtD#DoOo^iTo?Yjj%`HX#^ZQ3@0ek?^1;Ct-79 z+9$$jAx!`RN&Mmqpqrp_PDuu~2$*U7M?Sw6Ar>XV=K$Xn7Xlvwsz@P-%*jxO+W;sc zI^nUdzy`$i$j*YJko*ZGX7FID3_yIO)QBt;pY5wXj;)f}2@Q31@I4}Mk61k?AFyRo=9Jw?zWLIo{yjzUgM;+(v*JLmP;gF83u(0=Ms(ZMqT^Gv|6Ow2&_ z%baPnW*bvqsBhf~9JbEIGnaTKU{3vWvq$usRldd!LtF#I2385=4qYbxk|Xp0K2_3Q z)&bBTl7FNA*Z`;mF!mvIpb4r-{;mFZpt3%7K)nKj1`(_A-}S$<69vkI!bZT^tE-R$ zLGls(@9A!U{wEbRx3@JhE(Fv|A7S#v9^9R4thP-_an*hu`?7Y>AJKSRg(Ty<#Qv9+ zkM3N*cGbc;3wFHXnSg(OVDE?=RG)U___eip+eEloU)!`~{tTJ%ODy`X+z!(92P(&NUA z9z9-a%FHb{?mc~FZ2E?lLvu^BxsJv`rN#1-rBG*V^mtkM`G>CDM2FXqV>t-h^X?qq zxn|yssh~-oASFG0-m23Vuitt6!q9|{ucqdf_DHR58p^gEG*&`(LQ_=+eE1GEUV4DYkvGgJF5okOUv(yOc(Aa>_$bCpy zQeVYv3N`~JLBX`Z6JP_4$Z=4#utG_gfo)LuM-Xj0$uWNmswp5S(9we8<5-ambT@#R z1vx%*ldx4J>Kb_4-`mz$A;`+9Yz7Z_btQoyi@@#BC~Oyt`#ygDFwogrSCNw(9FSC9 zTU}F0bZkX1cADEn;(;%};{qURt`(%E1h~0{mSOk9@WOm>CAGAR`+ooZ^ZVi6_WD{u zdVG+pvy*ooBNETah61*BbbkKh^QZTNT`l!hMQQPYu1-!i4sp3KGSgC_9qppNKmYOT z`?tL<&DEvZ$uWLz0HE_sz(~S(_w>RqyBRik2O>r|S76>Jga`NlTkp;@0rO12&`${w zFjWwkOc|{e%=DM01#x%^yX|B0y>TaQrKP+OQD z9q4MSuXp*HU1=R{k$=f)OR37u2=jC>*3-M7@yoNkYDVdrpHDo=&CRW?P0i)GNg*z- zZyw&dsIIQAn^2g`E`Uhs#N#_e!us;8m;g6NlLxoYtDn+1q3M>!?w}-ZY3=N6trnyO z`Py4NyK`MzT}@3*SvQ*MNJP`;6V!Bdwv^?Edpa8#+`D;RLyczwHZnGOV{U0}YhO#r zmALQX+`@)ml$RD8hW!Ct0cdX6BPjW@79GeW#g5H00TWd)4&V_J0>>)z8IY3D?E-I$ zLHxP(FA%_Vqm!MP892Qu`JW63cD|E^(b59Hh2;E)Dv*)FOb~RbtUw2xpPZPT|D2J+ z&VL-xbb!-^hK_!&u^E_aqq-8F7@Yq!p2?uWC^&#i4o-M_2D1-}i(2TzF(b20SRCi; z84y>{#xnu)Ou)9bc6RoTPR_ODvV*4!&L}LmqJsR)?19a0}j zCrO7k$eZBbWjl08Ranvgd&lC&2-_~D_!esNn}X?|g)P{ajAC35ft4t;?8 zy}w)3Tw0u&o|cqd)!qqzFLP9O@l3!x6EJX;11!q`9cUd~=;$12$RdzvE0qOm>tQ^W ztOK3H*oc@*9*^ZzzK4a#Y4f3-2B&Q&mt{nA;L&ZADg*cYSN&(Z0iMNGUTMeG6hpgv z*vb9Z`43nUOJ`_nfxNT35Bx2dkZWus!Cr?$nr8w|?Jlto@_v5(`UN%RMfb{yV!?9h%a8&{6_ItLCNS5ZE)d;7Z8%YL3a za~9-t<}JT#-q~as9qOciQ(NW05tZWycVqZ6o(UM$7h+@M(Bcu(CPiSjDE!~JSeBQ< zIiEp=deM?4i<%4rTw!@8VBr1;Wx!F1(q0EmA8B}xw58BSh>eae^Nn@i$~nWHI>0SX zU3ey77w?|`{O`}=tcd8`k_r_36e8FlMkUX;ANs430&VSWJ-Yw-e}3<1sZWcF$tkR= zYiw>2clQkq4T-99!mZ3KEnRzu|JR@0jUsWiASb=Bwx~|n-Zj+Q*(k`%^fj|GH+SwE z{Ow9vB>Y_o2VNI=`(v zC%+^sIVK`8+Roa?)6&er&C8o-0wxdM-4$Ra>G@WmXNjH#xns+_7Hke zlw^sz%Ge z-vrrPWQ?^p>s~On`kzeTEK8H6H+1_QHnX~Lo(c9KOh)%9G|Y47ptFs2bEs{U zK-fj@5|Gw%zh>OG#Nw7%gLAKKZ0Z`@GrY_LORFoZYU>e-1mG3Sww)c#*;a>`jD1?l43Dsgg zH~8a(tOhCB%{2hQP>`8ZUMlblD*!8>Q{7rB-9>K3($VzMNWHlQ&fEY@;APN0kq97wZFeA?dO#3ax9C#++p`qbVA4DbD86oy&kMG7lQLc{mP;JB>AHzIB@sUjs5Ae4aN zjU#fVpPZsYcX=jYx)||Hz&sN$GhW&G#ZFo}A?p#LLx3(nzo4M72xlAppH4Y;62ra) zCIlF-3Lt6G=!RGXY!iOu$qWm+WgvW2(Wj zWB^(Nj{u3km9uU<6EI!IdpojEr*^Zv$gkHIa$t0Im&7C^Yu4wvzCjn&0BUZ+Tchp0 ztG%wgsdu0wE6}#Q&DN~3zaLf}C4o(WA#tp1(9OFfe@m?9NjgS8u=IkT43Ei<%4aqiifaovf{G9i3gAU0mJV zynF+KLc)mYm>KLX^@768xXAEuuKIou3a>{-MMcNN!GHh}8q(_;Ysw3AvobT&QxfB2 zDSnuckeHa1Oio``&I-&sgj$s(|ME=0@tx!kX~x^3SBz^m^YZabz#F!!o!G=P0jr#S zp#R#!4otzqj1X5#TYrbUC$-LK>^rb;-|lTk&Z!^Py#M^Qg}o~lPop5iJtX+$740kM zG&Hm{)lRCZpFXav^Vrbb)(P@9LxYgeycyK(FGZQZ-~Z|U67H?g#Ja3*qtGJcH)4iF1#I|nBhcQ4A}M~~Kq;{4ReKtDf!9}ib2M<*v2H)LS? z1qPFTlhYbZpe4EK;O|U`i2ywZ3J`>bhl9zPJZ#hf`TfWmL*{>GT1rwv9QKF!1ll1O z(=W~{++2X`Lt3pMKPQs`40tAB><>-M1B&y%tx8beGf?wdNiMkA=y^k5FTFFwL&*0D zgi>|&3(LCZq<|-jCU2O3d!QS)K^ooIKmaCDRlUG3P4A?2sLs!`M@i3Kd+c&G&jidf z0UH>bSu^b{v~j;RXYsB3S1w)Gy>s`@oqP8mJk>WeeuEu?Q(sYARdPz4ud9=@i=(x< z3C{$KI0j|=AdmqxFj`mt+rhr}s@hUP1z45f+Wg7{$~L(Ej~_q1?Gn~CloUtBrWMw) zEMCeLs;D6Ohd+M<2)k3*SYB0?80ZTJtz)Lw@Cn4aorTPR2VCp zF*!_hi6@Z;qZ>`q)?AaDlTTQ9V06AVEOy-VnVYb=u2N7)K>8M*37BUB_6q=So0m^S zN^y9ii=UOf)sPU%e=Pr1^u0}+1PjPFAQn}Z`h_bwlkOm6(qq4Ppd*)9bn~P zbXDWTiEV4BST-HC z8D1~$oIia^>%__Zo0T>$UGVd6i{#X_jI7-JBH-oP1P*6+>^pYi_;EGOGbaykU9))3 zjM-QGB4QJg(=x#Tnc;Nm(4MXPK=G@kb^73G)q~3yEtxY*0mM+{siDg2)g_HZa#GWgRl?-Vk;avwgn~3S z<~+Q8bF++;9OT>t_(kG(&9L%#LPMjw?(Ji86J-gQCYgZyMyaFJp~6t(vlsB%#Ct%6@_lRA@LihpPFIxRUt)SmUrsn3oEX<`5DQBS8m97Xr$s zrVM=4_o5DkjOmv#okCRCjM|)4f+9g>Z8bd=WpH^WV4evWWs-O%U}y(!)W|X7nSl51 z(J;+wKoyU2N|XfAD79LFpPBKChu5`KkM7;3wBfi*O${&{u&Ft5O-Y28lZoN|n_8-e z_wU%Se*N}W6txS#dZeRFCW&667ip!U+ zP+YCFX`jxGI}e|fapcuLMlbJP0aNdujo?96T)A%3mTf05T)Cz9sFaSTvhrg42lsW( zs2$t0dBfT@tChBH+kQ~<+~u419-<;1j*`-{e8+oNv`!t_vqfpcI;G9qb{$aDzHsgK z{l|3vp&S-Yo4{v06ELIgMS;6=yjC&-cqU+`{|(k!7gdfgn>}UX=$}B-OGLe+IbNM>_A}|#TFRfZVUjbD_ z$NYdd^y80XCQOuBe_ZvKi&q6a6L4V>8tMh)=H%pNXQZblC#NJQB}4`V@JzsPKMW3n zN|)OEk6kr&ULQtZIu(7wkj@KxM0@o8FSUEhA}%ZNWx~C zd{At6<@}L7yVtK>v~uB01%(;%8-u!82(qb(o<5vmdP_@r@2-6-7p++}d)DlkGiN9! zw*ladI0=2cK0kx|8V8T;-nwb=&vO;zXDG~>Ew5fp7rdHk$lr?XU%&We|NcE|R{T6$ zK|y}Hg2K$1s}i^y9m#tw9^N~>b^GRJOXn-hQkXGg=FC|$mEv>YL@5>^A$a(`!=>wc zj&EG1xN`Q~S+f*oD$JZcT`evpJGY=%FgQ3g^tR>cb=7@J%NH(MFn7+}SuFozIw2(^J12+9c_v_@-v<93 zxmhWVfJ@bbGe+1$HHKJYsir&=FujW&G$01yGUl0pLDbo4bm#VobxLdI&5@rpd6Jym zq{)*ft?-MAiBC#RCl>f2%U{lK-oI-8!UdCv>2va=$x~zwcn3zt#wR7CfK}f>!<};n zwM zK4eKUy$Ii_C>jn>$cd{P%doE(h5WbXN8 zhTgt?JKWDGS7~5%6MZ^T&Jv=OV2-_co67k@>YHRh)>zA+HeI>PjkkE!e z2~#c=Cm-&M(B8It&765Nr^?Q{7%lFlfH5l+$YvjIc&)r=?~0XkXUI>Nlbbl{sGy$; zw$T!xhYyO(&z|0?w0`kI`32KuWM!qLR(Q43@~Ez?B>8aq>q{zoH?32eH+|uJ@bsd` zyvW*$a@3s^VE(}#Z-YB0ckJ4*QgP;tNix#XGLt7u@l3$M0N}>NvUPz1kXS8U-5{B1 zD9g`GO-Y6k42WE6N-FJdSXcO+({VtcusTrrfFmy-Jj%JbIc#Y{jHF~Z^FQj+$W3-j^TaV_mE&qx1M8S)vkGsktM}vJ`_Et~Gk^8?^66to zjvYU$a?S>xE4mxgPSrKgHPkN%b$IpY-uaV94j)rKqO5J4nVp@TlbcI&5f*ntL4cM1 z!`o+096EI5$l;@B3=*LsY3UhEE^6bMfO#fh7V58i7UrA97r#X{k$aR$K=HS70(iS1}<~Ob~VTa7^HspRgA+F%89~r>QJ6CcxDt ztb*ICQG@_nu|$kS{H}(glu*Z)H!kW1v?8obbrrEwbEA8PhX*vpFjUL)K;1t8DRJD+$oh) zT31uV4CU3-L^Tz=fBo&tuf2_h@uA)p_s^VCIjN~_R!;z91Y$9LXaBFC{_$r|U3R3u zr|BK7lP6SE)bFGb9SRNzdMQKy`0}rRcULC`dAssVz{)%ma51V3XJn-FOu&G9Nc0~p zjwzl`bTl+HF1{&%Cxo^k@Cdg^^uMbmGsO9>+U|8r=Fgm|WZu%v{qJhditu=I zLwWax#dGAR&0C?{(8-M#QVyWFv#l^Q+}-HHo~`Q^&zLMFH*Lu?lrg9ICGzURlKj9Y znmacr&YLPDHF4^k^&wsCriUCu3~v_{l^46-RNJ~{>8vTz6DLlZvp}bovdzIDf$6)N z3i4Y9e6Ai*Qd~B3k`#!rZI^E8 zf$7)68U}S+duNW$fo<#N^Gv|?_2l#gV~K$Ago2Qi9l{Kx$ZB&nu?j)J&+$yaxPhTm z5-^0M;oZGMpMLxFzMp3T4!768eokBSmy0f;p5#k>5L45l1`O~}p#){%(-!~60 z{(^SZ*)J$0Bs5gqLq3RizkT}9*HI(LjC6i^_pG|cFPgdzu3iDbA)zSp)89Wd{Ndxf zK2-8d39x&4_l(*pjWbVe9bLWrgGk=jH#GeAZNIp&C^OF6{NatWr%r2LHnwzd@$~Tz z?&|940~PY{P>-;tAj#j(;Es;gFK2Zgn_58*zFv5OP-uU6XsEX`JH^lZ#X~(^o(Y&J z0$92*&jid$X5x?)g*-m5d*Q@M)g!8hR4ht`^^B3IvVxO$cKBPkn7TZD@K}4(7Nz-% z)YF7t<+xjjyOPbbqh0Kb^`Bo{`}17+Ig5@LH=>XlygXC_M%3Bp>s=HVZ37(hnXL;I zrcRr=(3D7@QECwgS&#%4xdc@txfmMVJEyXK{3%$xW-~Op%qEBqO`RBRf4kEiH|1 zQtfSFsiob`fln?SR8m|tT~=n2v>eX_%rgNK-#FzP;Hrs+6%PVCEJ|<)(EdO~;JMj! zb%wD^_AY^=8P@>M1biUA5md!rnS?vLVxHK)zN)FbTWRq$*@;q<753)WRe_47l;c_H zsF@aPTm|f63@eDcX@e{^NNloFIfFFTK*uW4ay@==yX{XJ#*-2S>DM3!=#)bxl#Di>UZDUL2 z=24Ufi$l$gu%HvY~W1=G|Ar_H*QsR2R zdUAE0OX0sNXjXqj{(b{<;@UFFSMIy0%kBpycmw@4{wQn%M^QMImA2@a8goY zQWEmnI=eso`Q_vAkf@_}o>gnugYh_^*78!vuHHcTe>;Lf2&+i6$+Zrm1^HO8{ zU7Z~4Y~EP-1_lO)gfs~o`?}x$@#|1`S8GkFAR|7)+u7OC-qzC21Lk>PAZWbB9euxj z80-=^mzCrsM}>H~IXO5u+S%B-c%cJUH+IPW54~bxb$(WAOqd_!&JHFf=GKnxKE451 z{^Ud%>}_u-$;<$9&DYz_!}ZlOV{;ov&;WQr-i9-*v%9srFf%1CGB_x}*WL7$iKPw7 zjB!lA^ni{|aZ_ntT0(SmSdfRcg|&^H1J4AUot2)3)KRX1%oO$kwjho|h*E6GN*EC{ z3xepwBo&C62?4oeG@FKb7picprojj#)8W2T7_Co)=#Z-&hfsSSiSi3H2~BeWrW)oaSk@Q|cP0uRk-ku(5Xp&2B@3 zAWx7M>FxOHsowQV+FBZ?fBEI~`I`@3nxa%7@$lkzAV>^#x7L4l_lC~-b7#(tg-J*wBz?0wxWF{!)T79O>)}sUYRw!gB^_ zIvuYiVCtntCo97>QEoG%AQ{nLlH$xICB-B>6R_0Su@h%qDQZA)5AUzIh&I3G#?~~Q zqdS%?LwI1!k7LJJa0x!t|PYnM-#k{UZ|)K8;E zji30#*6@(9@Q4VAi)^01^bWpvXx9A6VPb~zyI~?ySIbgfS1A%xz<3Y3my@I5faB0V4*&GXZn3 z9XPD1nhehbjGF}cW5Dw)ZYnQMkB^QD4fl7ld-L*%?#1(G&)u=%nSj-A>zj7*Ou*Rq z*^voDneMNME0vcNBl?q*ot>S91CvfxbijEDLz>-iaY^TyfO#h1CBMY+Ou#TL19Y!w z9o@SNS&SQ%HmqK?8g&F#tWr93=9=DGl0b|)-tgrEfm0#K%ScY(nSeQO9!hyh5V|AwKOy5__T$pZwz9wE zxFO(GQoxIlc@#HkfG6%-IoIKv33w)8S0A6&q0i#VtRix;pw=g}0hr!7K-*~qD&l;RWJvotWiCGYy-&uL3$Qt2<7MI zw$TGvLzqGg)F^s1p;`|EfS?ml?d*4kFGy{}t(fEuoC}&fa40_r1A%LZ7Bg}RL<4IW z(J!n3T(o&6U|RNQYzLtYLc#>zo}A4wfpTI=51t8_a@ho4Pp_(7d~P9r`>7ochbK4h z+xjMD6_!-i;~1-|Oma55u6#`Ep^3QjZFiD)OoF?W(KAaQOW&x3%(C2+Krc-VO_jJ#wpSyJN#&c6Elxghh z?r6vhvU?G1{nXI<)x#_2Zr?hoqI30%uD*$dH7=Z_fsKW6QIVd{Y;0aX*1M^#c|rHa zt%n8_A%NwKGoEJxW+(%O6_8vZcqZT`o(cHIGf{q`@u6KiRxMe7`o3duTuNq6a-j92 z8)_R-(`bgG&LeSkl>NE=2aoO9sYwy&jd{M=l|AlAs}-I7Xp^U*ZMah{YBD&YUEIL<-g?2iXzT#)`KR% zOK%yGBh|ILyGN9P;ynPD@Lp}d&*aLd5B@N8z%QKc1FV-&#v3LP-?!7^hrle?CVp6*%Q z^bf?HkoH>|&NBg{ln~DZjDi(ZdjiY1g9XQcS!t-JrNUM=-7_F2InM-48MYK5{D0Sf zF2>l}@;~$+=^*WyR0@eQe5qhN1sO@dNe79#p}VKs)&M3cbeG9_CSaZk81O)9SR*_W zFgf6$d2m6X5$Fw|9s1ed8xBR*gOMR{a_$AvJEX3h3y)_4uC0ST$n8BMalg;O%?p>z znkgqMJtwx60Ns`4R05Zbk1lNa;yvn4JLk`tJ6T3%(#0~AT!$5zj{=Ttyhz;ZKY63c z(Pguz$w|w|tnp7s17T!RQffvfC+}?2*13Oa)9gvo6Q!i2XFc=q4vJtrlkrS0?k+j@ z!t>rLxk=K7AX`mn{=PP=9z$56_;;xiv!WXJ>JYS0gDGyU-VFtJ(xbJ z(GE?k2zE8OT6rd5>lX%T*@zM6h()Ebey-UrZ^Eo!UR61xqrPtI$>V!&J-vQEA}KX9 zD^uJlOz}@?imO8&9d<_6m!ROG?Fiua9=&nSgmFV2aL38jS0<4#M`L$^~4k-!utbQhS>)mw|tq z|0d^@nYxQ=bedVHPcn8y$+6tnKG+wd*Tu=XF=VGf|HYzKT#>|m?WqTNCg7;(=-4E& zs5~LSCC}3=-p=^;>C+cAc_!fX8_!k z%Z!Y4#%V~JMV2UeBG{i%Z;PpKQzOhnuJk5FIGLQfh}zmjoc`lS)Pq_l{AMf=3X`zC zB?q@}Y!Gz!rbcHs`TlT4APA6r7x;@ZEp?`18^I zGe(b|Dl>7?^f9uJEN$I<17J+~ubzKXf5)UBewwsQYw3hh-+lM}sL>P0EPrTXY3JsJ z5=ar7_K-+lMf`04YO@Jzrw6EHFYVG?jhG`>O;8B-er_yEjE z8G}G5kc1Jv!5HWe!81N0r-VYD3HV-egqK%fbRxJ3Q&Tc3M8oY}b;9CuL9nGyXlUqj z^N^tM*h~R<)IgP1TU}Av`Qc-`u&1sfCDhVAIOK_qPgFu~HJCnGx<9-x&Ej8sgk8P$ zWuZJ1u$2*(9AN~|*cnIPanSUjJTT7$j7-@wS~X~JiKCIzozxSVu^bh5brsqAWTcal zli!@{0DzuEzaf`MIrV3!E&=$y%4q|jL%kKC_=Z+o4gRM87!I+RX9DJ#fT>8fq#>ft zGXaAsxKsdd``;Qxm7!e>$3vL_5@$U6z~v4Z8s+5^dKRZ^c`5r5x$`AL8Vf0<%2T2| zm6=2%;~g>gHYhl$C;le{5{#yxU6V7=ztMlB{6d-8`3^ZNq0Vqtof5128~vyGMSyXM zgw=wglDal=2W2vF`vaZ-mAin3gM_Y0E!OPRI!7do!*k?3#sM>+aIt6@Jztj<&5gH zs_i$_xg756Y_2ZKP73q)@{B_keo0|Y4x=HiYWng8g)ZL?h#ISlG8035J>A@+Aur0y zVN8J4!~gv9>!;q6dwM}1j(L`;aUmz%ST zXF&;=k|D3D@BP>BUp~DX8t87R7Gx(yhWL58ySO^X73O4RLR{DQ?$3XG`S>0mPgo_$ ziU|$!_406Wa`ewiOG)OLfO#fhiY4+)z|nTk?_SbYJHj&ouUffk)vDF&)uWJ$1}5I> zs+83H!dQ!^x@S))@7=s^?aGxa(PhICudvWiu&`HG#)X82J6i!=uX3Z$yagJsm^GvX6=X!XnHw3ITbmi`KS6u;{DlFP+T!E|I1REh z(~=URgMC~bY^*HI&CM+=xmsMs#f2E3hq|7rNeQtrk)Z+J?yjybF3!$0mZm5wggh6> zqkUby+hei?#z(t1!2L=ZC=Zrl$D53u&bPopPJ zJNNj>bMSDNR=!!Wc7x(H87ZkBzbEN`fA<}jkfj!0yiH8pAS6vwQChodfr7%cG2egx z-S?1x|HGJZ(mT)T+|b24E-5Lu-?)19!uhl0Wqv|@0yF$DYTSgGJQJ{ujWum6MMXu0 zjI|6!13>hfm=GTu9qQwWLZz-Qg5m<62^eI#)ZTq~|KS~YcLxVi*?yoGt_QducqU+; z2^cBB{k=VKR1p&(%dn-R4h@GPEft7CHQ7TTG@$o!f#zsDaYe7LA{lF-6J%j(27x7q z8%yP_84-hS5G#)E`iPQo(Q(QbuKiXnNngR|U>AKnr?C;)C0xUJ25+ZOI_C&zrux7= z$ZerUjD?W%Ou)qR3r_rj%BIIzH@JzsKtM9qSb7iBTqr+zT3Mg65i#ZWaBe`v z$XFPSs9viozm*fV23|aL0Q{b9GIh0oo1f%(1kFI(5CsB=L6jQ#@yUjzcyjiG z|9hm7nC52_(EKDPeZVsax&tY5_j9x1ujE+7&VqkG@{~wUO#=C$#}WM>?L3gvzmEF= zu8(~=kL=HwF*5$x^e>fz-Zz%v0;9AadD zkSHHT5jxsnIY=t;G=5J%KqGh_8o~_RAE2Jdlp_)fiI{c=HkvjG*66Q)$w`JN1?z)v z)B_tt`(OB=uYnG10NmqP)P$R#J^?9!+bmvs;)N;AZvYves#}qx*L5Iizy-nT4GjypNigNF_sRXg(-6?(w*=SRxfEfzLZWJJ2Y(7S$7ZpI``eiMi2^cj(c_v`k4J@;PtPa}0c_!e~+Z7khmYpzmtdyM0q=i?g z1P$^$@M?Mr^ONhVj1F)7Sz)@=*wJI8WTj>2s|6tH7Znpr^8Ccq6z^vT6z9lKnm7*l ze`zV1`G+0cJiUGV0*GR#G%nQCP;b{_dE^j}{b|$$X_?9M4qDhbxw(6Iz%1-)^fSGG z>BO2jlcmOiC*Y?E($dow95HxpY3JhRP97;y(1Yt5dsZ!w8$WvVPd|>DASJ7?=+t9< z<2Tlhu0YO+JM;7|t8Uq}Od1ph;20P;QFhkqb9W!TFgCSxz;C%-)Mjy3Wvk+RS)K_P zQMk&A(!yLY_@t&K#U~`PLzwY&kn@6P0tS~Sb#({Ck9QO7Fp>>KJI=n^*8 ziqCDoy36VC*UI#vmtUF72a^3Q+&@#TZKsWOje0uJ~0^6~B}E-%bXijRqkjEszkh`?8>yvlB^BGim(uCJ*oE6&dX1$R;+MdIUP+gNEn(QKv0*+T6<4p0ZY`Kz>uu@pT&%7cXACblK`H z+fR~{(8SEr#+G^83EAA3`a*r{&Vz@QbuON{bob$NLlbj|?HN3tXcy-V*j)>9(v#w1qNAg*M}X>+ z||I5lpcdlQ%YT=v(J6;t4=nmdi zyh={q)!}*P*17WswryFVFm3WA1)d3b>0RQ&wXw&+f;6q5Cwki2#}+EgoC570GhRw= z=9=63hObTEm{E>^ur2%U1@!|P=E_f%9yez6=H8=w=|pUXdF~p zERQP!I*gZ}q-Ih0S>o_FW?&NcI9Oa)Ey1S#q1^H!a{c>T^}aP>j^T0k<_ z9;vl$!;-o3QzpsC$jw~3=IFW0xAj2fYY6RucH&b8sczc1a>=jtszo(ULRNcxYIP{0~CAW2>ia)U;AIt79e zF@+N(hbm(Mq+N)bWHdJgbs(r9#YZ`!&%epRBUlHbK;SEoeaQB)k@1pGLEjqcstJ8T zWTNy0nwXL%C}PQYd||jVFe0M@mB>j@{321;z}x=bw#EuU zR!(Izc)-E>OmfitH458NxZvaG4+CKGs>n$W4oIr5t*!wpG~~FcG`ESw17Cjs6)6VI zwSu&i05`YLG6bSAyf7aW(k<=czTbcU{C>E%y}nkE9v|fD?Bt!th{SWUv9en`IzRvM z`P2Ksu9o_$qO|xxS0^VMhqzoAnQ5t5`|YB>KmYOT`?tL<&DEvZ$uWLzj&`7!J5Qyt9vgm5r@d$_w3)dwyDw0c@tDgJh>jM}Oa@O;F9>N_+zI4CGE zFo^NYQkqmd-6^s1a6QjRNsNz;VHQ9HEf?CMfcnAGtb=xx!A8hP#SI~z41k6PP-rqE z2G@f|A_xE(crgqBTp|)dC<4QPb>L$DL}6P&s#lnoodFXeIf;l=vFvHtHqxCIWa1=; zl>tC83p@rv0VaeQX#ZIGc6@4AucwC6aLdur;^l-aEK-uKeVQJQMH_KaCoT7{bAu z5At>Axd03C1X=mTJJoe`9~-{0f}vDX(^R3mecAMRlV!$D zoH#*xn!@aryOcFA-$9JPtd8V0)%lON&7VDI#`GETGv~}-ym8-g&5JkgJ$&}cnB>S( zs;EqUc6bZ2pVqG5vis;s^|P0*>*_sx`qGd*Zs2aWhj90)DJv<= zO-qal0|?OB88sDb9o;;A20=ma{{4`+v8GH=SeTQV5*HB~G% z!}F`qBR?lSB|a`XEYQyn0f5l(h<ojt6F z2nCCTJ(#w?B|um8*wOvFHmzR0Y~|YBx)IGi#OX*ECK3yb&!0GO@W}pMJ9lnft*E$S z`Pvhfxx!{vhza~d8PCsapFDO%`Iz#7ojca8T)t?*{CSJE-}cBP7hrd9(JNfKPphgZ zA6GfN9m%dMmn@t=cmBdf%XdD^D9G(@_pvj3bnUFRy7EcY!@K@(_TDlus$^Rm9>Fan zfxtlU!QEX3_W;2O?veljf)jUlcXxNUj&~>NxOB%8WSALd=EyzYz4v`q?QUSsdEfuv zk6q_Xq*qn#&|Rx))spAgv|`1Q1wYQ3F>~h3x$}4?;Kwg%QSeN_w4k{}0LlY`M*>Wu zcfeEXIvi*l?4C}&`C*s!1czDr+5){yfO+jL+LX z=Xd~QR2?Ae!GMh5YnRKvF!}zq!*QYW)0S(}pfMM0( z|H!(-`uVpev!+;X0?!0Y`iC;w-t5yF=BJiUQ&B{U?COZj-2D8UtnA!^LQdW#F*7x} zv2Lo;s1c(^kC|=~9vPRIoRplJPDFoLblt+|w!se@ceHGKU;B2WPs$1?%5 z%PUk84k*q=hGczV1x)z-{QLqS=+WsfSeHIslr01M)f(&LipOwXZSp1 zrB_zs`am^2-2IsPFb@}D^;1e-b+vpLX5oG$wO}_1lGoJ8Hw8>W)fHbP@}GDn;I<~jf^nC^rHY8kIOrm& z*Y;fpW{y!%QqgW}YHn==Q3B5d%rgNqt${Vn9dh`MAq~Pq4j&vfxmMvj+h!1Tf^L#} z{)e1(ZJ}E{a(-Dl@n6Sd10m&RPCl9dOaNczhc!+f2)f5`N%7>uM{eS9+S}O9hHDU+ z^0d*>F2(Ib^>rkt4&(>HKZ1;R*f4mlok11WGdcC46nP33aD60g0v2#+_$+68EZa@d zN7_+$pJM`KLus`98i6+p9e5_-mzlY_d4)yN)~eJ{SKH@rl6|e7s;eE`et7?eRrlO@ zCSWsD3wPh((6;ub;t(g?NZhS2>v>q;P~W+F)%pw9E}zykv32ndguF2&(An55$o0|5 zYjID7HRnG+`;nLBz0 zgpgd?lJ4QnGXWFkm#o3c;F*9?af#Gz?27HhaXy}g#;>ex3R2B3KRvQiU;AEMS!Ee; z)hjB2Vk3?>d~*7dtEYvjqrIku@hy%0FFY;v{Bm;g3W|!67nmPyar&&LO{kN}^GnBG zKe~1HfKR-|vqw=03CZc%lD68+5GU)Gx;a5MI>&Zw*?r{TrX^Rr%^p4pjf&=(fL+?! zTdU%{twN){Y%jlZ)H}0(>l&U380A5!DLfM}SvwS*gz3@IQYEPE>Ju5QRSs@2cnS1V z0%L|TL{e#|L@0c1S<{dd@NDk*HM2DPI&m9hwvrg>4bt|iT7h4h_A%?wt8=Ce9XoC1 zq3c20auhI@mRGb(I-?FvS6q1D z+>FsP*RES~Y_ZbBA2+EjU#*vxmWdqz@N(DZs|;0{u=?PFMXOY1FCRN-@c4_GlSj_? zi-?F#PV1=m-aBr{yfI_-H;)}UR7vFqUUa6TcZeW8#c8o7OFwvux=PBZqCgqWMhE*b1hpr0%xz*5l*89k*rY zzQae=)lZy0w{PW+o7yjUCScSh!6cB|@G#;zq-2)h!%#SQCgAS+Q@dP4ovh8xL988~ z-_+C5QrcKAO80g%@eBiuOq>DF1k7%fbSs3fQrbx=J$-%efBE@CYpfj<6e>y=OZvvH zpHhSrsKf8S_9fYx;@S!(NxFJ|=^&*J10O%WE8>}enLCdv+!5m>=K(9FM`bzg|8P+9 zOu+DKy#KW(%-%aB0>^1mYPh$Jp7yN^C#@pmlTy<&bK2TFx@!DATpheZqT>@2B0XZG zd>(2&yL(5^H#jmrF}ba)R5v8W%SQi|iA_LadS%)J>f|0RXE#q@)7|P3?nVZd?mQDP(s1N{Zk`F45_Nbc z;L1u$&XNcNtQ-t(o<4eX^NLxEkJ%OjNd%?`0ew`ExGvG<)thHG&Yal0f9JyKlVI|>f_b;71aqj4`-5b`fTkzwY?H0+YX&G6$`9-a5k`{r(g{`{| z9aU31e&+nK{hL2v2!?mel#chTJW(`O#G z^$v=RO^|d3so%W1cf+dH8@BF0u5slIy6s!Cc-~~Ct)}*Fej#lRlULk3ym7;p9lL=* zb^P45izl`o+PQJbqzOtpjIHdP?@Zg}Z>GgF0pnw&L!P*Xab(riZ%-se0 zBnBZ=g5<z2kkEWcTuI;ptZ^JFz(YWWV_ps;AgmxhTmlfHl5T`fk>7$T2m}Xm za80r%s%dDh7gq_21eMi7dMZ*RnVb{XN_u;{+8RVf8FA66r6SBgP>I<{mLYjne>Fi`- zZee9Z@)k+&hxY?r(#D$d?D()C9}g7yx;Wb#8Jn6}io`q3u$*yS6*`YWZQsjR%%{_&%$=Z_!Sv0=^170cId+Pr1&nTyx&5)-KaM@eZ} zzT?9i=T02hv2pF1RckkF{%Ox~jmx(*A3en}Bmk8zPMg3N7fv17y>0WBEnBzmIdnqf z>ManNK7&|B^()A9e|+)C9`KADJ$vcO)th$#Gr}_gqt*ySSsu+ruswJQMKHk&|!g=^G-* zQC_04oM!^g$&Y=#f7z@l)27c}wt4^2<0sEvxO(H(T}s17Dq%i~f^u>ay-l8IJ$z-L z`}D!RyZ1D;v>rSn8eeoEl5Ws=1KmF{K046D!P3-FUr$e0SC8ZIW#hAf+m%Y#UonwE zK5mZob~Zc{Fmi?Z`w7JbU#SsZ3ouzg57*yCer1Uizk>jjHJ%B0@tkR@s*`?DRh=?r zSptqdG`xB5x-FhOJh^GhhJ_1esZLd$JbB8LsZ-X*vkV0R-qg1r9IoBop|)<}lBLsT zOr5GaMRm%wACAYRAO*QtKyUoJrsucScdcDCd+v`jrq7rVPO$&s5sV3NKOleX96Z54TNB1-{|ckj|WgRK9i9$ zu1<==u#SxAkL2_+HLfEI<4^}~0&aX4kUFTbzT8tNY6GVadyNx*)r0cZX@>ST8c%GZ zpBtM3bt{}0&nK>AbO0N&tRMS3cqU+016qPNr@zHY|K*9TTQ<(0Jw;{QI3-2papT6# z3(a6r!oD2jnRf1U}LG60bSC~a>pO%8E3(z$g-JG8Z<9Tf{y+ugv4yZU-0 z^+mBkt_IrI&R)Lm0CZELMMgms<)278`Ukq(YqLYV9SyZEs;eKr;)$#iCT}3QwD;Zn zx9#Q0K2DY|Z=X4)cH;cq^e$`-DP7RGwmnLNIB;^^Tc=Pp_`awQaX zG`#!mz^|X`b3&XfjI=MQtEnAUJNqQ3frERXorGrs=9z%8T!>4Exz2zEUPurkIayiR z@~nJp2}tWf-47ue6GSxy*z$98n4A^VqAmy?HiG|wSB+}O(Wj_@(k|$r=aMfz|3GCx ztTc#ZXiWUokNBN?GWQzK1k5u5^Gv|t=**4|^7Hla_9PAwFK=Hzf7H)0pdsXxH`G-_ z?FurIVi`|ZR9I+uc!W%k00u-{V?qM@&&x^!Lts*JVr*PoJgFI1Fwf~f@qqD6z&Kp7 zC!xv^Mat|1=b3Je`9DZm|YrD^$bt~tqswgTc zj8d31ZPAYNH??$L8yH(zwZclHqt0&2^7+#zsZ3B-o;qjQo(n`G@W#N@+NK#D7*bdG z<=s1X?pQf{;hJ3+?myOf@ftK|RyMYTAcWdmkfGHT3rdTUd|e%poq+rYCnrZDN+iTJ zmbg^k*ic&o?5@I$1jYdp76O)#z#ul5@}5xFTg#vYKqSh+jKzUxG>Y_}Wj!ISiOzq@ zd*hjaLCn#BcLRaND%1{>fsfYS)F3M1nSlMxpWeN3=J!64Rvl_K6~nd8%hXyCSX671K8OG7$%${g0iywWS$8a5W%=9AaIXZJt-C$ z`E&#Lj0^ybVCq0})cvr=_2Hlc2!VhQPMDlKpfL-k!4$wcL}?yDppaZxg>iBPCZ`#~ z3ea$$hnPYTe@3u8tHJ*D^v^8>afF*L+1w0cl z&jidf0n5?B2|pAKV5Pa)a{VS8aim5wx7wkdA;d$LT+90WFk%O)w(Vp-RN$ zU#UNO5QGVQkLW-X5LBrg+k@$U8^bsu{U<*JBsf`bD6{ivRSWwv!Q${`TuYXGgQBRFDxL;q6RBUY2&C+42tz1dX?(t>?F&`#U5JWhFVuQ6XM# zP7V%^b~bh{Ug&_;4IGu;pSvZELfqbB!u%k2b}%+J2hEp{Zvd7*f+*7d?pAS0W=29} zkgvC!hpXNTLvtG-^?G|j-hwkq+Sx2D%uI=k3=RtLbvMy7wzRQ#a&`A4reAtMn^aO? znwORk9UT_rVQpb;W9Q)LER*w0zyKvA-Vd;cU<;yS8)?xfv7udpuxe!uHaB&kG9Ol= zLt=qUhF~KXu>yE45tbskj2Ri6ob7?slg+^Dgt%RbjVBePeUyO7WE}{koST7|YUNDe z9P}Yw8{h_Eerr)IFgpGzr;9i0`5GKJkScLI1IcyuY z(bDBcE-x;UJszT_raDn+L0(Y>D1MM(O!Fhk0y!oiaeZZ0T9~(kjhROdT)C9w%;e~R z2RGvUC`^wIbhUk@ef^eQY0Y2dL@r*Hn-S*eV5qHq`SjTrd6a>S5KcbLk1Cqx`iAn{ zq!1TZ(F zuMADht*UG4W7IbsR@<|3`uNdGqel)K!ZQJzh&iwkqJVB9CfMn^$k(4cU*k<(Yk6<&-SfqamKI+ z0ny$2;nQy)`no|5SYItFDa=lZ4v)>}nSiaWtsLBZ`uiIH`=7WzNLy<}Vqr;sVq~PB zqqC#6g@vW1jU6%l_V7%=$YAdT+B>RNT3{+Rz<)-E6Qc@(2aBcULPlBdfC0uy3Wx$q z7NGJ&@~|OY4GlgvN-V{B3ce!RG}sZ%dB9|H;?9(v(zIKU5zA12S)Ku>_=tBBmN@YT zu@jy7?O6hl>7W1s#Awu_(>h31>FJJ9~nP){C0LU@pQ0J+TsJ>V_G z_EBFC36S=}2=oP?N|P(ODEJEJ$xcqZUeAU0Jy!ZQIUBqVaH7F55q zUMQ{wyR#S^4ygA_16U`|1Wfx@C#%q+eL$w%fZn8ZX1r+>737(Kc_!fFhj;J#X~Wia zD;F(XIB(9pdGi)7{Ym3C&jie!b#$zs%-Dk0 zI{b~3R~w3jK8rC4n}O;F+ItdT_;`Cu>O>OQB%h5SbYS*QM@P5C!ysRuru5kIrgkbg z;;I!mbEm7fe?XjM%`*Xe^Gv|VaFkiREWrl3hS&t)sfUGMQCdRV0RVw9xJTUS}KYhEUrmL1RZ7d0akM zYfxH5Q>^YqBO9BVy4DOY^T1Le97MJC*w~1%MIvo$$hOkIeUE1XrW+~Y2e(ugCI-6N z=-oW0ary2O9i9o8X9DJ#fO#fhH_G9s|KN_|nShybNrxfKQ#z)|8%TExW?;~3ut8(`GT7(m}1~` z(f+|R0Yj^>WwsPQTsD5J;+V1OuPj}C0|J6VBI4-gL^=mKqF?pNk2g%6xbdmEi??4u zaCl5&DjjAdMhB{@d^zE150!qLkc5*51m@*M{zI;>+np#loLZ)ZHV&G`Ty6( zGXe8Vz>jq;99%qn`~!oqKS{_R(NX_$)x24g$0?wVUGm^1Kzv+0ym%&HraRmQLwgYX zIxJrfsc2kg&sVy|%xsAUq=)gX72&}qm-nyR zv}fCt4H3Z>4-Y?d^Yn&oT@~YIpkwFu{Glk=;l-i7TX${UbTu`^QvbqLCsz;nk6H_Z zbnOcw9Xv_{?e&lD-@SYDi3`@?O1}Ec-q8)trH0Hfn}R%lYoi!X%U37VHmuojUgP3( zy=Tu&tsHnJV737;{YU0HQh){dIhi~Y@Le4^AZY_*_dD39JQMJD-wd8}aQEb4!zL(> z8TZ3*B^^s!H{Sq>q}zY_th=wajvF*&+`@AUMi2e&yYGh%8#8>-6Jt=9dP$@$5gU|k zUo9E&?a*m+Ck+}p_`C1E8#3yLne!L!(6@4QlSmt1EF8XR>!feKQMj{c(6`@yH)!b4 z;ffbGDXR?GZE58I2b%TFVc(BZ{bBSL=Y>Nc|8DU2!>4H<7(IIU0%J3)w)U2^J9EC< zqP=?5M$-jDAs;e)&}hZEi$@JrIi_!H)+T8R*fsHcgInXi8Mk-Jpg|)?4jDRl?C6oI zv({@odTC(NCarosWze@jtr+^vx6`MN9HBDuyKe`LQyM*D?FBIX>YKJn%XlVW?)(P^ zAdY`j&cFl8G7yO`{2NLSAR_^00A=@br#>pfs8)uo1-!`F7<74oiNJNh1%^;KIQi#G z3~YDC)JEkxU*t3#ISO#GU;=#Jl%l}Nsj`7167J}I*Wc4xRb48m5R>l|*^)Fp$|Bg) zdp~~s}{_{V6gO6C+SXW+El^Ez5nv&1RUqAp;Rtj<7 z|NQHBuvj%Ww>LJFDiUUz# zHlXm^+B*OISW_V=CFfIDadfBezgD~z_cq{RU*IoAQcP@V}GibxqI z&=OHqX$i};Z{l>2NuYq#AV#S`c`5sq3UZi79j~JVfa(YkUqU5nMH#)qD%?qN(Re0c zGo42|ulzGu5U8*iI}*6=J(NR6yFWE{3SfKA z3NG}!aPa7DR8CUT5$trzL!%DVtD&VXAtv_8G4=Bp$wt9RNT&b^sZXP{IXm0s&WV%k zH;ktPLN2Bb&DE8{XszQ%Nsdv}0YPQf*o3XMjg9$F)Q`~L&1R(@oSa@j2}whBh3%0; z^w&|JFOB^pTicrJMa5BS+Ze>(R~^_TnEp}C^)=~{KdmCgq7gI?9g%cBh6YmkOHJmj zO|R*9x-7Hr+%HVeplH?QSy_3U{<5!}lvEpy4%n)4K>_LVmp=3x#5f=+lNahOV6S}j zg{d&BWoPDo38&xOFdB)5x^Egy$7gvddx_lnG9hLrDHYpO4G=|`(14N+OBrtzS0jW? zqOp!Ew}so1GFa)u#oH@8|50{ZFS8FI=eSr|=wD`ae|7%T{K#tUXm1n>ib`r)Bye$l zeg2aJxlP)V5n%rE+L2Rd?%1Z*wE{SWGe2;`)2iT^fPE5k3JUVF(v#z(yp5kdKXc^V z`Xy5)PPkxfy7Sy)bBB<;3J^ge@w>82P>|tgaQ?uVv#aNgSD5`s-@-8@Cb0l?ooKYM zuw3u`pA1-tqT|~kZvpXkmY@DGqV!<5%KjHL&KG6yw zHxlq{YN`|8+*CWaXuN{b-Zmy=`i~_<5j4c%YOAB(oLApESwUd|efrd(|Bxe-5fh-! zy2?xM+UdE<3O}Ggi^-XR$TI=cR>185wW!jos4SPgIkdlWp8(Ti+5Uh+ced%tJJAk> zs&!I7Yybcq5>>HHi*26>k<$LvNHyn(PKpfi^Ky4_b&e|p&IiOb zb?^W2=kFgsyzP-TRtd6VLcyi$0V-a9!iz?YcU{9TfB*dtaPjuEi$%p5ks$%zo^H+# z_U=Fdi4%!x>zjW6^Y`C>dDq+3Qd?D!5*Z4rUROs)2bV}7Q3-h_;JU^ZWKDNSo5jMS z%($>XfP8s+IlpweAkC&&1JD3=` z9)z2TYjLTd0IMc7IW9aTFu>o}M^IW;L8(gA08>^bCOf&vK;2bKUe zmh!}Xr>isD2#O%4n>Oy+^o2+UsB<~%K>Gwn6XphTRv2X`eWU4!;G1xONKUWNGRI6@ z<9cu%Fgt5_6L5k7ub`HJ4AK;A0{IwWb!}z3kGa8XUAyQ~aSc7AjLM+p^6E0437BUB zKC)vSc#xMY<(YsZ0*H^m+nY{u(o-;pu=5I-HgiA6HRYR#|0f;t`^IaPh=3gNJ^PvH0)%!9zx@3IUExX=zziiROJ< zkEiQqC=VMn_4cJ?J@ z6_t7FTNW=}F;#iYkU?<&f5*vrCg2kqS8m(}k8dFw7*vjxp*m@@>hx)oPGMgs%{iO!lA^p z+xNE4?C9=2%U1q4Rdw?C@k%O(OZ$;>&dPvja~x>5xOjT|`gIHEtIk#(r>v}`xXibm ze8lk6(w6!*&EWd6-J8~KoUJ-B3Z4m=-2F1`Cf_^H1Wf9S z#xnu$S~6qiOy%*)AZaC->082IQZ z@eHm!F8q!@biA?#lbDWFbYKB_HVZ;;1<7IF&aRQ7Zu+bcd!jQIu@iLXnSgr*p$>XF4=)`%u>a8E z1BW#XGqba^aR5?;w;hXHTo8cjTFvuE_w74yVE@7Mx{1(`wDb%nZ*Qro$n`OPqIKh} z+MYf84jepkK{qNck<^IflE&)tycoyVn%6I=AK0;T@BRbFF6#${qDN8^$vf&x3)6ya zUf#NR>gc|ocI?@+|M(RXFaO~1=(q%YI?|5j;_O6kyVqCG96P*k$M#)&51hDZ>52|v zQ85%d?2z(Iz+8DE&jj4w(Z%UM7;T+BU9ujO?;zKIo(Y&|0-nV)0oNjN2q8LTf)(Z= z5deXHO8a34B|Lse4Pw9~6x2s5Kv5yM!Ui#AlBp(_f(4v$0upC|J7JYf*$8TZE+}*b zq9h{LfhMBdBza>tIjeFe$YTop5!0vqfCg9)b!cy8GfUCs*vQ<<&IuKYKE6B?FgtE3Lz<;JQ<5jD2Y4pnMoN=L5d|?1e_p9bSOvq zYlgoL2_AWn4qgL-0bPG^~VBwpH_|DUBU7ddwJ=qs0|KYsk-qytTDG z*gU4e?f#KnYi3OvuQ+BTkaX5YAgG?umFvf6i`_KS6IM| zb|T5+QiQ=2`cL{rK0~Jn$1IdU4nH=*|87A-?+nT@=NVJ_I)L$S4xwr@m2o+P!IOO}@^>(+^RS2?j zDjT5vV0|VzSefb?TP2d7kDq?-lQ!2>x4B2IHY z`Ox3dR9jV)79Z&91VG{P`che|*a`0oT`*7iK1egX!DD-QC^8)62`tn-*6SD#f{rYjssgUPf{psJ=tN z;ujPc7{qvHsZ0u?JuC}hRcT=^n}DY zMl1$}CL(_HALN(-{FYz}qT4%4I3VXbuo7OReNYtu6bF}gKz$@9C)L!VVxNlqFgunl z?%YgXjPjFgigltepr!^r>YYDNB1t7k953ab;n6W?Ow>Oq92+ zk%9JYgz-Q$tbX{CiG_`=gHugyT}51Cr64mV)Wg{1h1T_pXHFhJaq9HR+b;|)Z0sFz z*A$Bdd4jY^Z%4i7+PAN1oI8E;?Aeo-?mm8FVrB1yMOzCtWIb>2nvY-T8Jk;JLC%PLYf9r>t@NKi zeysiQ-mTl34|Sfu2F8fFrCiQ40b}jsfF{Ej|KaM2GaTnH6*zO-6JhG00}gne37Fmi zVq(Pk%^48&&EoLeo7T*oIc@gJyRo&kFfZ8szKGnec;A|HAMRZ+W77CB3L{30nR=s0 zESIz4bhk4{|Ow{Yr2*<`TC3N zc_v_Tsxx!0Fh46P!r#-?$==S^*3Qn}(aE_Q8iVo{G%DH02bP%}7l|@Ke?MPeUmqWz zYR1XQ3Sh8es3Ipni)R8x{G8Hv$lb_1|19H}JZ+!Dg9r>DzK1=DrB6+1=CI|8}6iSJF~bnqOGi*v?h1 zw~)yOaZm5S&mZ6R_ja~7lon^ErzK@qwMqdO58E9~pga@s|NQf}_kFm0z!I%2&Q6UF zqY_t3E1n5BGczMo3jBYT=h#hbPXv;MTL7Ft*eWQ+Fq?P?x_BmFPH(^HKf$=LyEqd- zvt_DH+WlF*c_!eLtWK#I=?{6i8PT|rIy+nFzkHy5<($T;lP6D}I(5oEp|e@k-BwkU zljP&+>*VZYru$My`_{#?Cr=zde(adKZg59OYiC<^Zd$CqnVY?*o2Bs^o%=T~pHV+@ z?AXyG$927Fd+e^uN{x5%@C7u1xrMIwt*f{LsHq(}a_p?JbEl-BM^amq9_{bq>h5W8 z{`$%N8+ba;1PrV>1Yme3V5H-M%lEU|lJ*kt5C<&O+6=|!nSk$Jg2EhDJFSJf7!!rT%Ou)cs0?8-9xd;N2^KPR6S#}tby$qBwaw3u& zULNb?nSgmF;D7zkCrMUBbZ$w7um*THxMWGY``-QBTa^@OYj5k(`S<_*qphhnEh;9b zu&So6p-Ix&GcYjFUX>GWWoBvV+Wq#w|Iu03E)fcH(hIALY8qQR2D+tng1k&$Gb?j* z=brxG{@GJj-HF>#ji{!!6Vl%IgisepTVqStuKsuLf9dP)?dh+sY$&TNtrH6J zgqhjF0p9Me7RC;qQg9k~y=&`hZxmG4R~F)i937LK9OvWW>1kGN*N#k6hvd|lATK8u@95x&FdtU~<2Mg)o<4o%`V*seX;)o! zPD*A*frn#&xt*oAv)OCMSGsWIU$}hv-fQ4|cSwa5VYw0hMz#SC`lc3EcW&N)sCE9- z#cNmYyfm>w3RXvFn>a7X?sc&BbA4;QCpRu?-aB^W>dhNkuZ%6Mks(FZVReOZQIVc6 zY;264YTwm3b6M-oy(hX9AwX!3Qcrm%U>4pYtsnz|^YJ4JU&@?rEIXHq5V4bx*9~rC zG3*RV;-v&$ihojiE>$sdWst0cOpX+N>Olc=l-(hS4!4|KUM-R(0MpD=Ai#h`>93F{ z6nxQ7&XhsE%GZCv=kYS-{dxif<7?$|<{w})&=X|(-lH`MwjYlVPKEkyK*QMj!r+RwQhSHASE{NrC1oE)SHQL-0ybRoZ)Wg%dDB4JaD$p7N{h1&XWGVeY*xpS{>25^}w0r*D+roioN$}zpTX4#xw zIliY&xFi;LAj71?dpqsb+2xmJ57~m0(K*qV)_qn8_xs`RiMT+o)A8G zCg7$I<=otJW zV*e7Z01$r#jV4oFF6Y94t(gD-AUlKP%@kzh?$=iGCDF@Y2uHsrSyD& zT_bfx%+21>(OcyHVDHws)0Gs*&Q5Ft6gDy)5m**t%cY0&Ou((JO`&_HP98gY)M$m> zFU=f1K{FZ>7Ku-jJ^~m{JQFY}AFXTJ92gY<@|WnfFO8%mCe;?Bl;?{aI?L&`%sl2M zsQs+}Oplp@Q{)W|lh6v6zy9Sr&jidf0rO12ECC$uO4M+CRi%m}l9jmPf{Xo~(6E`_ zaHlf%F;Xz(l~7(d&jgHnDs~$A#zGzfb_vFtg&%k(V4ew>X97-4N+u2u24@9!6{Pp0 zEQHGYGcwZC(=*UAIgSr9RZ#&zsi`~@aA&u}y@yX3J90xkd;4%nZxNQ(b#}LvgqjuA z8@{RS>ZIEP%p@w(!;Pw@+StCjE;S?D>C}-m&wHd8O`cG3E!&lvnkqtaEnfyl8sFWo zwtnj?4?l4OFbD_%o&1``aXy}g#;>ex3R2B3KRvQiU;AEMStY0vz|jEzv^d`I$>~e3 zo))H#_L>&Pw>0*@0MD&o4rm68irXd4`Oy}q&uZF)I+;AbbnNw`TW1gW#9KUj6qS&W zoSrRdtIZ5?vVN(X6J(=vY{!<}M-FaUa>d*1;gis)=;+uaNqc!hfJ>gIS-hR0=E;*+ z&aByT{OJ1IPp;f?4-AWlj0RI@T5x)Eyrbi5t@HbI+zqb$q^`bti`uzswjRDgVG;Pp z*WzkZ5bpH)FJZ#EL}W&0z+_WwWI}<6$U%J-m>wg&MmDIi&w2$ zqYEzGqLSQnAoM51M1YlFq`dgRd4G zo2tq)0goOrV*K=p3d4pfOi&m+|2&w24a~9s=a`=yarDN-ZwAkpIAhJGb&KXKTl&Mu zVH>Y#KGQR{f^tdfZYys+KJMFbTXya{d{kZi#OZVUR_?f|{X*B!%$lihp^f{!>GSSA zx^eBc*8K~I>`1vb+LsLsz2hz^gmiCsaO1I{V9KB&RlMSlQMxbaNDJQFayr~pju_~osnu2mdqpzq{Sj>I688Ox#?@Gb+@ zwxj#gK-=5qBBy&+j&Q?*CX3*kWWMBXpdxqo|I*j`wm!hx%Bs1Q0_^PeMr9aqvqIk2 z_wf_bKGR+7tZ?|@nPg_c&B-$XlNTL#T{^HE8fq#9g?NAQ4zpV?UT4+ek@DH*xyYx&~jLws&++o^3{P+Cb$c03cX%#F_Z z&?&sdGXYbkkSqd5`xc9J?1snGf-tBJ_P>2zj^9c=xJs@%7&egQex5S8Z|((tK?t1HWLvkRCzoAn?w zoPuyH0)Xdp&N}+5?bIN5*rzo|qX=tt&R|$#)mDNJR5GpP% z$|pHR(uKlWNpEjgTZ5=5BQ83%RK#lVft6BL&WKX08j)hs-y>-d3V`PA8<0UDJ0MNX zCMH%G6D_}e`sF>Sb{m96*-2skUY>EN!Y?Vz$>B!V|Ni@Lzx)iCo;r}lCWe5f)jb+= zK&=o%qEPtu@4x@bGXb|WRp+O~gn($+#o5u((Z<2m)df7o4b5+V`-nHUv$e6RAT=61 z#_rC}PCzOmCz%NH){c*$N$%-v5trvBMFisl?Ba|b769?EA$g0W_rv>vE@@*;d3JnQ zkdKF(v$Kn{y^*o0nWab!m?Vj`?`o!??O4Bd)$(O4R&KVhg1i6}$k<-8 zG7>^OY;+&pJb7sI8W46ZTef1&k#rEwf0d=1Ce{7r#$+(gej3Lxv6m&dsoSMk4S%m4NA(ghHV>cF)4Os*@B(4jDWQ zY`cKbxm6(~c`*~$LkS>jEMNVK|@E5o^tr~`Rljw5taZH z@7l8Evs5Qf7(09rxRM7C9zJ@E;%YVZvsZ57BPaF4;X+EvxO8C0_SGxrE}cC^Rdw>DbwQnU)rG4C zM85-XGfeKCJG}F!T}$V#ST=3yv?)_2FG+4ef;Y$(cqU-f5A^kR5ffmjyR%PJY)p7q zcx+;7MpjN#)C`^Lh_DR46Av!KG;V%ID-VX5CY}kHyqGM)$jSu7SnRagxhhr4 zh9f*Z8K53{CvlvE29=Qjah^_csmgkgSCo!>$T5O87Bp6}K^^F4PL59pPhg{14 zBiPS0KT7P-h)|pxCTS-z&Ct%~hk}BzwWk|hW%IH>Q$9cAisjU_r<I@l(qwIUZ&WzolvR- z6R{WxX)MnKeCFt(!$;JP-wXt#JQ{^)JGwvifA}Pfb2Ha_di~^~1BcWO9=T{EKr1N` zur0l>V}NG@Hhq3m2DvwUY;k$ipMC&e)NaZKJDwD2F$Rft1i?5coZ{00=EBDJ}p)e|A3{DpA3l9-%8hWdHGh2h_@TpU2M?cp85t8BhxJGD?)UHCb=7ACIawP$ zym<8Bp@Rp3|KlHoRUA$=0v(-i`=zbo6dzmTmv=SP_U%7-=;*me?%sX@$W&+YE@?|$ z3C{$~GXZnwzwAuMP2^7)j9`3z z{`B`he*Jl%yA^h}@nfB57FDRqfB{|s^RBbAYv6Bx`}?O~-wkw#i^FW*Jbs|58Pr5& z-j!wWHFS3O{s#H)ANu>HwHYpkI*)E&ykt;GIbfwFkOM*JkH7u>pCAJ3Yb{OmG<&Lb zN8^lECJ})Z6-cFBJ^gS0_{Ts01uBA`#=K~EbDev)PM^3MS(ulblar0(8$gIAb}*8U;L>HIUgCkKe3XL zoS4En1;umdu7yiJb)Y|i$-xxP^cT|3&bs2_^b|o;JMKz^Wr*>_B+kjDojFDq_HJ9V zRpYrwMOz~$$G?<7AkjM7L!MsJx_tDQ`T_NQM=VMk=?~zTen~EE^S5v@ae4musmA(^ zYiG?pmBupxJ5!yiFDUL>TH3?kyt=1(W#!^EODBz096efL{M5C&Hug>~u5Ogq+S-== zF=!KKuDSX6rJ=dCy)z}n;_loWb#Bv!rE@@ZsyJ40 z(%j{z?mu}AK5tul%9$mOz4{=vRcn^an>TO4!sQ#c9MgRCT+i6d(#DoSHPM=_#FPM;8=4x-ub$q!cHX4%3M0{Bl+vVG`)=Gt2P4R#oQzdl2^3|vFjqs`(gVfir zTRMOK!bOW$ZP{|*!u7ikw4c3v^@fqe(&^Wj&NBhinNB2srR@AiMR8{OVB*hZ4g9#Z zBu57#*@btBHMy9|8aQNNj%1f-0_K^31A{|C>Kp5NI^X^6*MZKCW>KjiBR<00+1b(F z*3u5vgMVNklvmQ$^V`q;9g>E!lAPqI5HB|;2M0$x8#@;e4F)0)1#Y9>pSvZELU828 zg!w`4>|ktcZtdvq;~N00m1>atyIaL2nHdR@LB8H@9$n2Eelu0{`NlS=AK^U@Ncqr-wctSzi<>>N;p&&eAYkUpTo zc_v_1T+2ANsldM5QP+p{jWl_z;j-K|vn8M_`IiNyd$xYqa)xCSV5Ni%6?% ze<&_$0>L^q2y6{4jm2@ko&j+MEnvFAXT>uC#KU!(@NEAs789HLD;`qIHAHOm(wS*r>EDpYY zOnu3?iOQpKxd03CXr)Q>ww=1F^;F-~s+v}BeTCMRg+I(3uQ+ndn9*Y=s!m(_)8RAM z@9VraG^-)GNSOb0^Q>vpC;u>c(v<15=B?YMcIL{RhfiMU8IoKGbeziM7yCD^T)K4S z>W$kE9y@gbcs<%rp1;v&M7}8ULM>H6VyL&HrNJv5?FSFFpFDl>M$gE^tOl7Dq&_?o zFuC}svA`XbE0eRYu~8P#llcH;J>clWwGys8!pVl~kDP4sz@MxO&jegpi4a3YPtS+{ z`o|w1`+GX^7Piz@!s(w99qQ}p;o|C-P+BJF>Hqt`{(;~@Z#ODgn`+eLGNVTZ6xG*Cl($CukMz({EO=M>O$3On-AHRJV=x(TM z#7ZvC$xcs>^!GrKu08DFfW*Gv{`Fsf|Mae}qp+m5vaYtI09aIE0d5Xpy0$X6_K)fR z_`m=CuTP*st`P%AN>p5ui!xm=N6c+wWohdh(m&A0GXWzOXBEjCuu31(1B#8O zbNRor3<65JqWMXNAYF%~rLMZBu^q|%vTy~->mWt$ahIe{kd=&eZ)f9?B$6;-2{vAg z03OEH7pEo0MFe`78^3t|;+1n|6QgRNA{-3wl2oI(JSHa8&)vb!=*=_jyPCSeC4`d# zql?7#<;Cgo(NUq{{!VtLZ=Pvgxpd*;eVgb)Sd=9loswp;uqY)VIxH+Kz}ebB|Bd#o z%NiQz&tKBA&n+tG>;&>fbzy39d`Lu8fU~WE!K;TiuAD!2=JdIx8cQ-` ze4Opg%naXv2JGgIYnRSnxNr$5fBI%t^!awwpp+>@ZOy}H*Vg(cmK&N z12ZdIZh5p-Wkh&8+FF^J7`}P=`~~0vjLd-H=j`T5>j$f^y}emnDag-CON@;Og_FVG zFCYN^kI-HnHlMbBSb{SuoUUUBxHcW21KD@2PONr zG}MX8ON#RIa$(^!6QA*rVh$=P@a*m* zTi>Lt!jj5bkj+*91S)+A{D0Nuv^Ksnw>%VSwm z^o83SSeT4DP$o|?d!704v9qvM&&ja|u`nDt`0+aaM^ZW!W#cKxh91~`*v>*1A#71R z6EN=EJQMJpf?{^IVvhjwgQyL$Vv{bw#-xqk1G`cKOiFPt`U`ns!+ z+N5b#H@E+EOkM53z8$*`oKQP@c=xsyE0@fkraEoT-s_KB+gp8~Tt0K|%*8$X4sPDQ zdDlC(cCGMrcawVW!{P%=kDpec#ZuY+E*K@t+r$P+W9M%%%44b z_RLue)@)P1qN(#z-xN|HMYptylWp%D+PY@RoFC^dSh4lMscTv~x+YeRJQFZE5tl=%PQR9S@NGyWl4RUPphQi4MrQ;c{eh zDR|FG4)YCUz3n|MRj%jGJJDE94%8T)2^c3sSKmN?&-*HAnXY&aPn^B zu&2YA^o?{1jb{SJVj{{{YS=pdpZZTdX!rOx{f9G{X96BEYV?@tCgG8BiOEUHsp%Qm z-LUAoh0kq+A1)rNFmm{ak)stgeEktmpet^Yq>bcVHEy<7)Mk!FhVJkYqsMHsb3+GK z#LkWHkhVput%ZEV@ZrNpj$UbM??E6uQBl#fdy?_k+4Oq(^dFQ)0cCIG=;iv>F5Usu z0dh3RB@*$g`BSEj9W`p?s4-jKSU7u95q$)heo2lwcqU-B?%Bd-Hwk3Bp>Z#M`AUZ_ zS@i6p@E>xLl9;QavE^%ClPr zy+61&kYSD!q3%A<1Y8P^gLX-MPK^Dv>)KZS_NF?wuRgwa?bI>XFl&9D37BUB=9z#S zh<^nQWgN`O(B2GNQPR_zx+h*rrVNk|Z$lBk4so(Y&|0zP--0#b}E>|EN~ z8#6*&Ep7cB9vnM&{`9UryLN5geBk1#{bwG%G_tUF#p0YJlT87F#sYmRqNh_U9an>TOWxu>bA_2ALHt9M=*Q;rbHTifajcqU-j z&~&at38A4dpmT76hknxKj~ZSkzZ#|Akq1N~)Zos4=J{YuOk@|ZUeu*8Qi}m$b18t( zeop_TZDLaMo|eR);yXC|g6s*o{_{-0b5#b7m@sbi*b(3JOu&=_Xxb(%+dt~NZ>Dcj z{(cb81dL*a%8HVL+^mfBG_=&zlyA5YC2eEmBVbBPVMzEUxr$)CC_95$A6W;a-w+B1 z>XIPmj%YfiVc(NhQsM9yISq$S)Afj=smPU-%c-D{8{X0TuD_?Xs=8EAA!fc&Za9kl z*wcGIe*EQKM`Mk+q&O-zt+0l|s5BU*8zlevAHN|9TiRGxUR9MC=oy-l4+_vSVlyf$ zg}Cp3{`Gr*x1_n5X96Zy8_xvXF6jbnboaZyI{n)eq5qtvLvIhACsGMAza{OBmDi7w zRuI`Sooi%aa)%AY%iEhO3=f+zIjwv)kPSvR`rgtY%FW3qpBr*?J~yT&JQFb6A4rAq z?qGY6G&NL}Bzp&W`Nj+Co2iZhnT=he4PR!~^Lu6!iNp$H2QJh(_ks}omw>0LWLS6Sf)+zn|s8l9Rj zADbXYHG0ky&AS^E6_g=oYY!c8Z{(SP=~B($E&w|K5;~yZVdtW^j~W^As9GoU8)8u4 ziK>`g&voD|{6@mOU}=Cr%Mew4i$r9?i>F3E(U0^E!3|&u*nSiB@Rf4RTP;lvbxHvibX92|l?^j*JFMt32k6%9w z^t6jb#Tk(y0p6Z&&JOnOiHQkuB2jI9)9-)&{`)WQdb?U`s|r%Ul;e#WVn+v;$mqxj zVRc=7%b$OK`t`%R9x14JbCM&2{V+Y4d>wrP0|JCKfZ6%;_g_E0!yT%rMvxvK8tCig zhQ~YFyLfrHV|YXJZx}w%E9IGh1zB-`00|3nw=gj=H8nM}up%Bxa1As23rqkN1=$%X zaUotVj&`;-*49>5#6*pc5NeHm4;92!rNy~v@sYtGn|5(VC3hKH{i0gJs;Nine?@6g zZdPhSbZCIDkC&&1J4KQ32xtdPW?YL)L2sRfEakZH5WtZ5`UpzPWK`MEJW*AdpcwSm zS?MXs@sZ)7Awhuzp+M*$JQJ{!vHqjG=hXM_-nwS>>MeQ|gxOMD$aq9~CSZFT^B0;o z&Zuo)ziRQ~#Y>hh1rzbkS2i|wSbd_Z2q#;!*N<;s&`{g5cKPCk3l}e0wru5wpPrbS zS^!<6N|<7AYi028&Si}Q+tw{xv|z!aB}K=7kOXPrRh4+#nCL&&yng<`*44|F zVEp2xE7xt(c<}i7YsO7om272VpsjiN?2#?2mM&R@`IoORx$dD1LIu3UBC1n+rdFophFI_QJdCZVO-=T5xp(7{X z)YCV_`BPq^v3&9Tng55qvyP7{%l7?DH*Qe^gaE;v#%Y|!J%IqhgFAr)*SHHIPTbwy zDoI5u?o!c;8`=%scY0<%_rCXA`&59Lx$lqn|JgGg=&XHCRac$0_u9+8D&t3wW^$S{ zWY~yt6SW>aen!S$soCNctCvialaZnO;}swuK1ycxh1)t0m|@A2fXUPrp#Cr=IX*Vh z-_732%*a4rUr$dzCpQlxS4eCi^zBMcN{EYz3J>sdb$D%uMl3ckvU6wx+IrC~GE%8P zDKTdnCg^~LC@-6 zs2~xJ0we=>QUNm?#PfulBJ_yGQ6W9aq0P`|DX5baAKE{t|Kwa?>Q0bO>7b>7=Fkcu zl@E&8eHe+8O`sPWydDdT^gUoQ_bM!&B55U#C`SVQeg@^GQ#L_s3%x!qOJ4`QS&&CN zIWgW}7ZxW>kS<^NclJI|ZbAQzB-$0@5!}0)|pCW*>F+EGimQ4zPBa`jHczu?c{(pk|Wa%h>~u@)soWGe;l! z>Dc2=lep{Q4dw0t{bqFlv=`GLwS>meGmQz{^@!xxKM1T(I_-=*ZNx+Tu94UC9JM8qvt;SiWkh z>XeDNxg1_WL4Ll2cR+AhR1DV5-T})K=hpHhU{W5ouks{dwxr3`vH;#NYju z%m_BP-;pWsP5-6Szn61DZUKP)v-|yvoGlLAxb(NL^j?PD&N)+g?oi-~Y##zyG77JR!i-MgQ7yo&>z- zfaaCQ`leQP4leFU9YFa>nHhgsX?717yKnVb3?6rfl>l;!GJY#sK#R#Mo3L*o1yuCc#-90coy?uPiUZ*KQ zdDqtx2|On)Av!8DG9n@(BserQOsYr7If6aNk-)PlvI|&vd~{4qEU6jm1SBPaBccvg zI6Qf2scFfW#FU83-Ngfi5Fi6&P_|fBCSU={Oiqe|6F>AHXO0s1WZ`$E0KG6vK+7lX z-TyJ@KMWU>_Wg&9{epYv--~H2{%$gN{outU6j4(H|8waE2AoPEHV$13!GbR9KH$in(P&+$j)ia1qvna1P-{t0!4a*lyRgxVyPEmE% z#Y#FPf*^ta?)JJIVMDLi)qQK0ES#b!0}z(N6yJz$R~MZp*y5YvW3u1k96w3&4^}%C05N=1IWG$tkI+I6mNv z|Ht3|`Op9Q*xgi~8R>5H@ap-KnrB@jBco$t&Qd>z~Jv878qLl!ZsIX!gi-vek{GV)L9RE5z0*`04fIo=_@5UDUnH~*K?RXx&u4g zXA|m+y*S++r%j#&ylTbL*{ZWP>+>XFo&@|5AYnZNVBQ#K5 zLAJ87eMJfNwNSu%aSc*ziwpD8Vu%Fj?uG;#8kBL5J77vpHBxK~3i7g2;-bO<`3njR z@b~qhdIq`!m^C#>$_M%(KQ}WqDIQrmv^<0`Ijt~A@TFAVk^*Gm(+a_IeImnI#SOOv zR3ldyMZ7FMJ}+BH2`>rxvwcKk%@(%t?j*Ud%BuxOGFu&rS&!VDogP-YG|6y-4DJ+uK@SDN0H5b9D_aqIS}FR)lzP8k!~DzyJRE{Xkc9btRJU16-UO zJ+liSgp&t7-5OikKL6L}Pw)HM8>-9lQeypG935@#V^HdiI&VB$&8^*k{(&6-u7>*Z zLP27bk1G<;t=*Cm65QLl}<@)T=NzD~g9fKY}_=NCWmSG9VhXH^zeUxxO&P~9P7A`!> z*e@Xi04&fnmgCH9DvD5iHt&u83;Es0|3t@83_5@@gOw? z^+MbL%#`Q@c!L~dmnQ)ik^W0T#Pk(5)K(PcWapKL>+wNRtrO`tC_h&Rll!;NX&ya( z=!7e9RA^UE@&>d{YAhF}1bDx;e0t~lY0V=?jvUmEM3c_QNP2&wiuSgKB4Mbzlc64< zM8}UDIi!9-%hk)rFCeJ8rn)99v!l5pE6&%+T<__ftEY}=XdFJgSJMg}@HZY+HKnUORP*?; z>rai~V|S>c2UwIXN(uLL(0`(P{nF`E$B&&ndFEy1gm^pKApMpn0T&aC8*T)O9dOGN zRv?%e)v!S*b&0l5*eW6>#?l7jJDY%G3<{EfIpNF@CTADoDWxS4rAmY*<_ZCEf41S$ zCQ8q_xSoptVWOazkm(oX4N?n@**FNAQDzJ#U<&$~h(y>wnE}VnShxp8)i9w9l_zWi zr&X8scW$r=WCmgX$3h7+iLPfdTBFf4z6lkobe7}n1D*t2Q&(;@ZjGMPt0VK}6-N&r zIeh3))DMgpHAMNDDNh1+bgE=1VeHEc?g6$(dSXm?kiW05kGHqCmzP&1V(u8UvqEL4 zb{0PB_-IsT&=Dap0Q)n^Y2c|Y7D1(O{AU&fn?n?Esk985!PplVijTMmT3G)%PfWs&j#{%wT!_eSjim-%X z5X0z@#_PB`Vs0wUz`8&kg?SQidq+>t`_I3<@9pZ8G}TpB6yyq$B15BvRaosPB8z@A zpZ@&vX`rXGqgB!fA5xAWKG@&W-7k(O0o&R+x`8n8=ih#PhbB$cvvrk1)L2gz` zd_+i~r>m2bBktkg>h47dg7@$HCAAerqTJlfw1||2@6^7^7gIzZ57VEE|f zrPC)gkKzcSaqObbb2CH$#b`|s73A$?XJu&i;@-7O7tWkGaa{Ani3@k07+D|!$diD% zjT;IFYnNHTFa_8#UW7j7NVWMYheC34SOd()F663zSneJ71DXF)Z3T*_sR;yaX~_@6 zJ)qctCA~qJnPNA00+LMSI;7q#*{Y4JGUC#7coH!9Luot-_|df!8ryg5-?nkXs^!ZT z%$kK-Kh^2e7aY6wNYat@#^%{=o&-#rCA`Jeu-D6r5lBP|UzR{1fd0}J3hm-a!1xfb zn$g*VVxn}I=Sjd`js2e`r5SnnJP?PVr5g#W9o@bC?^?5yz3rSmJ9!c?PXfl_p4uB? zje+7&i6SCgXm3NYK(@M&X2D3HqjTq&YNm|l$JFTYf7gF15cp61$33~hws$1f|NrQJ_2Bxz)i4hl2n=;rSKE@1 z0nQEv#|Xh+*gA&PedgP&%RFW*V6_Ku-bn{d;7Py_c@i+ZhaTR3fx%SeioJ{KxnZBz zR21b3QsR-kMPLc?+CxMR{N<~ex##`5{W2=hW?$D-9cqK zSwcxOdEROJMd>~w#h5_l-w1C}06IUP70Pl>dA2=6wP@o+xPVyUqI`O&Nv+5%MhuRw zr&mBgf(x>7%8M7lJy3_lsr8AD4WtsLCnWKiP;33vw>b#yuPSQ-#{n0tl> z`o6ZYa4JW8ltwbc@Vrok8sxoQuhM!egsHL4)<%XQbqIO`7*8z-+4V?r7nlh9+slO! zuIA4l+2j-zQ+yLG9*anh%iCN4MCxY@skq0{=~CcYj0Zhqs-TXCr zW(YL8@!evRBTpKvU;2Jp@%@8A&6>~<>K6|LwL_*csVrESx=k zKq8KcPo~X`dxluirnojI$b+hz!XsnilhZOX+3}W5A%8d9ArXo!KrCM<%s~e|+Wql= zY?8Fg(mqQ$CseMB(=!I`-ONH@k2QNRVJna&KymrfQi_OS4(=eGz%1N4TKbWFR9QKA z8ScXURl3b{a-IbIZ~EUwy+iPA(C#Wmv!jMVIjynn?M;#0(g`GQ!3x63nN)(-VK>j- zYGFmsIHri3VKI@Mq&x}u?EYEf87C{d_Jz5!X{VO7@FZZC`GtUJdE9HgoA+HDO^t2Mb&Q_f zxOnW5^DFaEfE9QWa0H&6>V(%imPuYV9+^I`jZYofvwh>y)246iEO-(yPXgviz%AA3 zL5?=h^fCi%A0FPeX{Y-B4NER~n(I6WjzAfIf~2)L&d)j9-8|OL==QN=7f!6+bmS0E z0_I7;Ny*8{Nl8h}Ly4mc!saZs9W{a?6cD3)Bt1PnEiDzNN3I1Zd7{|P%}i!E3%Q(r zW;&Bs1OKoiIDV3gqp6kT^>z3iGrh@%MhXz!i*|pW1dMcU5jOZ%Nk_y1Rk?-xPE8*> zW6jS?4lkCUIBSF2vQ_#iDe0NnITDHR@_eP?N)uM?U$AJU((GljLxzsmx;=UHe4nte z=){!vI?vsT!{&{XHP|RSe7L;QUob|`Un@UyxjUM0ghaNrSk4>vgYhcWGvj{z>t9uP z67c9B{yIcae(b0IO+|T1LE|v13G3R9s?Lo#qagU`HDZ3oAR!NdsCsf zt|Hab)zlq+sPGs=oy0H?5C6#c#AI}JNGoX_Xl}0(=NF3tt-OMRgP&Oh1%yVYi%{Z0 zVVm-j(zXvDo5h_~B}u_nZvulJ+j>RBWtD^UiDq$4RK{E{`L$Er-c?-`Y;ED;7ZRBv z%r8fF5G82R;7P#n_wgiP1QIEXSVT_^27K}uYjKKutwM$V?(PqK7Qb~ zd3c2-<%h;Q`&hrWzH;E9v$M;tvpfly`OxVh;(X-fDsO477K?=sG}KuDoqo?fpo23g zWwx^9mHGkpXmh`n{-od&rt>6VmPNps^<=zL%0J6PKvxUolh$(Oww!~UCjoERaX{nv zsZ+;xAJf>qXzqN~89WJ?CjoC+^^_f#=?4oS(k9BXY_P#qmKJ3Ra=vBPNF#_;sDrX| z)Q2KRr+{j7u&{A=VcRPAf(C2cX zyRE)FPmmDe>){@Q6s&^W%uMd;x-VaT`}6^AdTPt_(&K}GX?+t3Ia;l-Je~4^zkT`j z6HvS@b){LLw0gU_I=jRcksRF!$}7ab|MBJ54{!UsTB?gu!%)!X;p*hKt+8{`$$ zU4Q@m%cpn!y&VnZB0)lUkdMb3XBVfKT=e;XxT^NupMQM$_#SVMCjrNX1bDfj$k*BF zwXunrxm86C+9XNZfMUa!Us;?T7vkrM4qb0toXzx%OiV2*tI&rBVVIuoj;6Zu!pyi3 zaFE^HT;JF}e_?2BVva*qt+)vj+S?jy@a4vY_@l{}yNA=u=lXO#T2@roH{fL0+S*uI zB1(%6Ly5niuZy9ckuh3@S>fTStU(6}tQgVW5DfRTKN7S>=OQ8Ti9>P&#&&|}V0 zrkJ*|8QcUaMx=ojXDlV6vKmVt9Zk3*MiI?tqq4d*)yu;0g`QnxVNDfHD57qsU&Td{ zcF*o!I(=l{_HD;aGiu=JU~PIh{VcB(`IsBMcyRrc#{TUa*Q{1^uBc!+lzB}3%PR`P zJa`iD6)lZj8`mrY<`@58wt9yd6rs4JtfDm3izfl+`#%L~c;}Xln>KCUy6eEv(-*G+ zWcnC>UP{%)5T(Dlucf|g*N#1hPM*7P@#;-&-3O1Jeiak4GcC;3!qCvd#@yifV~nTI zUg+fyPRPq48fbb-LR@5^my5lvwWWoHg{2kl%Vy-_dg|+$oDdfs6&~#8`NqY?+1bg7 zy)w0v%Y_`RL3t7|PXhiCW9YC^D|r&|g{va!W|uEY)tEg`MOjg1)QFLzMvWXjdYs&> z?fVa(IDZAIL&}q%cwy1>NlLO~F~*FQkq2pDw;JgLlTUb=Dbp(Dr8to+Kgn~+1Z8Ob*@Gv3qm zfws_PAfDRR=B$pNz)WfY;D1Bdiz-)7EaDh0NxX{ zA4>1UZ~M_D9zSWYIfNz#9*=?_5|E4u0LsR@5AQ#`leTn0Qx^m(5!d`Ge*Y1`jpCYa zw2WczqMK6RB&}#7L$~jLSLJ+Z&$jiysNEI3{n(FJp!;)jOy^0!8avi3nmuO*}F5$dH?y!skd8uBsdq6U>%9(Wo(iuYlwjwY9{#WkV{b zXBKy0a*%xMiIYnMKu?|o%svI41gxN>B&Q}~xTgX?ul$V1X(q zDk{zkPGe<2kn<#9fOZiYg<4Tz7E1KM+RCSmIWIpC%xFfIrbHR3-jQN|ZDaW_q+U*Q zQUAkH0eBKHBMEl_R?IaU;*yQ92vWijws$OsSh+em5;(&hOJ|Un+y7~0!}`aTI83Fl z0aw9aLnXO3-Yk}i``(q?Xs%neXvNkm$vu6 zta*#3PMkPVX}rqfUFYvUGqG`Sb@T9{&$p+$v%AGxbKShTGiNMVd+^fjdr$PKgx}4} zHvn=x9r#i^8?pmk9lgTB&;-!MjS6@}!Xl!g*>izM3{OjYhoq&krbtNLu@e(1MJ_oh z8P5l{UV!1->FcI8T2*M&4Mv_29ObO6OxjmDxIBH|JP8=_3NC2K=@(A|W}ipT+jj%4 z#fe^yR?n`VIIMQ`%+1tJwq1Y}iuI~};O+Zxl;`}=I|t7F8s=qfII8b z0vv6Ob+iucKd^uQfm08B1Ay@Z2oUPoG0@l6T$AMW%H-M2(`tM6?muwo)V((-vy~PJ zc67Ei)fPm$T7YqIV*jo^`}S*`zW>@0jdc9{+4bm%P>~hpZ1ninrQ`c{@7c5e$eBmz z;SbWE52<&DL|jvn7Vh#w_xgq72lnjVv;XM%r^cAzVxY5H$jFXj-o4MEC)!h>$5cIInsWIC922l_vpvdV0fEga$kmL~2U+Ffg>RwRL=LVr*fJ>_U9a@go-4MJ_gh|!4(Jxs&$1{(;M<7zS8pd4L*MQ9{O z96z`!kXuCg&CS$@tiC}^XbGea!n_CPmq1YB23Y@t?K_dUOAWwy60n%-F^X>J)C3Zv zy*233Iqma@4r}bw*rRS)C`Jz!d>5rsd0UIGrL(E?llzZOuU)@p=3LDb@mD#0we5)( zf=FjOqvy{qte7)hS#_>jel1nxuQw6QrhMlV^$TO(DsJ9rgZ?FYR8lWbPz+ zIYn88d6qFz0DZ?njy)opCjr+cztG&UW%u5L7cZQ-boaqC0}~4?+gGoN;YQ)Q+M2qG z^w`w&IDcnr9*oH3*`s0Aq2DP-}L zm6lf$i-3A4kpd$4NprXf1llYkc~K#L$H_4T>u4LC>SA#%n1tnJ2*r?mP~6#3lb@fO zkXPT_RL5Xn=$BqtFeq>9$TB*zam|vYJ1@R2YNi%#xE@)EQhB??_wvF0TX+(%g6x>F zV`OBMcoHyA0!GGQ>QIisM^$+Q*@NCt{_P;fPb3mi!Jc#sZmNs|NH|g&gF^a~O`sbP z=8`gi|4mLFNPq%C>4T4w5zPm$m%a=710B{VCm3r3y93>qiYBNo0x&@oP|)JRkpgJ~ zlam;dT4D)eDW~O0iYm;f%bA#)LKCRNnw0b>Vx(UZp`#d20_I7;x#@ACAbq>NdGp52 zoel!@^fbUoM~NIBoyxL;?6kxfpuU4a@eA~G)Uz1Un+$f z(%)zTx4;Z85cnn2aMEM8fUpU$oTU>;JgEP)MloD5X*VX&62#<8!P(-$y$8~NOn^Ke z_y4BH9trGQI9KOor$mQfeE=%} z10GQK3aVVe1Tsl+Fwe^oq{K#qh6G`8@bmTa^P|oUjK)obTROP|(FxaGR74oDK!bzu zD6$#&L3u@mh}-1~1t1L)OB_xHdREy4+L@tPi2h*)+yf~|32`yeQ6y&r#W1BP8e{-z zt{eb2iI0N})i=19ZHlO@L3eRfBOtwjIUy2Rw_WUgtn?5YG^SxT-U&|vrbg^}4KO>f zKwxob66eQwyZgoDG&SOUK_(2Ampn18UNAlyYeKJYSUq>fwAm|eMpswkyI|-0JVGMC zCTYyl*}Y)8^7wHwqehLJdL^%BP|hx|ZA`hifAjo>O0wfd4n>Z-%-K$r2cCQ_H60lf$vj6G?z=@um zPSjhZk=zCB=ue0EK`#OM0WgIeqLh}voCD-Rr78b13ysL&OrYPxiz}VMW(~R<7;=OY zBO{Py_K_SnVXuKiX(%TAyUYj7at&GP9_4bf0eM|5NvVrJ^89-wO;v@$+)^=uRy08> z@96I7|L}33ucxE6zA!&MH6=k%*4zevFSUEF;z_{0|MU0X-t_{-3tOr*UyvLd;^FLI zXJu_;V{Py1)z>Tj@4tV3*W1=yRZ&x3AdC+W_i=D?u(7nXvf@d=nF4s15uxbnMu-9F zt*uSiYAB9Cn0lZ>gxT<5(GyHEN`GZE5pgZ74KB?mpFC^~Oo0il5@a#sm_oG$um@n_ z7Kk_x82Q#9r}6`KM5aIiXNV)Ok#iDr-GHFA6i|?w1(*v>pb6gwDK?4e#0&+dn=&u= z*NGIG=Sjc?I7yHj65p<*t~ft6HZmd@&Bg4@UOv{oaPF+u9b29R%#(nVlSwOCdn4$K zxSr0hIB5_GR0x=mK#+lbh&ChWA9RkqTkNO-{}`-K81h24Q!(v?T7I2?2albQc@i-4 zx3R-_boHVEhe()P*-E8S_&{4E_yemc3KTl9!`CHP-n(=}ZTD_X9nZqHPBx}Wx*$$E*y+(-KQ zZS{ri)KKI*{ilL1H1#ezGRM;BW!-}fw}PuWTX97L$8;cJLsNyX*4l--1~1cZ-aEE^ zw_aLtVNq#CHF6}#HPh6PX{NP$>6{%8UsMMBJ=ni|-iEvGap^))aXGcLtIPA&ySRSt zG?isHp0*0(jrRPqdFlLB$L=`<#w4X@Ci>g(Bw$OP1dNrFyn(dkU|FCaES-<}5@}6< zqYt|nK*{h>e6`XsiDmN9nt_cJ<7)~ZJ^UEoCXmPCe<6WNC*XRf2HaKjlU^BTbLlN` zqbAy_2jy6vz+-JqL-QVpIDcm=UVxKBif$=AeZ8g+p^{KG<_~SdYDaQtC^)UH-A!dK zr_MOiwVa&1{3PZ{!2NyQ@48z`V%#kZ?%%wwZ5S9Cmza_P7gZKMpUz&iV0!z$yIvR@ z;Ar|%=i1GeKA}+wm>>{jBK^9XVMOJ#7UadSt}(>fV`2cX6}8F2~L5)j#qldr2m|pnk|u>q;2iu zwuUl~lLrjjItJzBl>I91Nes$JcfGRv7qjLz_WBe9k&?i1bw_8RnX$3@WMFy|X2r@It?q$!Q^e0O7FsR8tLU0(&{Qdfn(*6fe zKKZAyz9Zp|>*GIo4?6u(ry+Ry(QS;D|8jcTrGuH-tV*Vq)bl^&Y#M@A5Z0sPN7cVy zk6nk;JUIF21}qI=@aEVnNiU(9cyE;SN*;FPCXQtK$W{lcMq~fumJ7C0)YX!lg8H1> ziL=644h>HNMg*d!1`AxXFg7UI{*|7!tuVsy`hAT}MvosRRUq0!tfSQ6 ze4v}>WA{wsz~*h6HlB+NwzIx+Cpe5J0YmHB&?Tk5wxSrt{pncP<71dZ19QN10{_w-l zA4g8p-8XjZ$OR_m)-A0~DL3Z)ut|5-nDu50hC@DVpu5{SI#2jc=za0~Q zG`yzx7scIEh71`!df4!xvSUZ9%v^i=-ZMkfmbS7NQ-=KYm*vC%@>kWVqem(6Bw%z1 zLZ4GmfN8+n#q4+F53&p-cs7Bb%0Tr923Cl_kZDh$WzI(cml+)q;Jc#Z0o5JhY`_A~ z>>L2U8aDyQ2Pp{@^1(v}3lea8^Szud$D4qw1}}o|o1H<}^;9p+UEbdFwy(Rntg=v4 z0;&>K`m)=j*pIz>&&Q9S-nNUYY6|irqEm9KkO)cd4W)o3B>(W|Z}1VfiEE3?%HsXq zgOh}XMMXu3a3aPDaqs{9^Gjcsq_MG8Twj#UlYm>=KMhD~n`^=i4IJHy%d2YZP~}Sg z6&9Z9MABr}=l+&~#yrPc)(&v;P$L`2m997JmnuhcSt0u&UV)L%5h^n0Tf6fUnczd82!Kh-qTYa`Pzye4)&1J1o%SXZ)d*t zcb`7IZ;rOJpoa^GZ#o^*3(-JG0ps15Uwac?nX!ixuRzcF;Dk1s(Est{+dOMSdbXv< zR{U4cp#Msq1PleIA^@HQ+|v5)*Q$`$o|QP>m4pUuHc?30i@1&=9GuuuMUH%?YT+;PZ1^zo&>z(z#%oYBPY%r-n(J>Jk`n5uK0vSBg&l)0%V%w(LLLE5-?N{ z$xR6Cpa)a@%d2XqcoHyA0tQK!nsLC;hQVC|-Y4t6LG3xfUqiFd9ay+<3sgBV(l3MYLR3}XSXWaf$`h4VmeZ>O*20s3 z@p;wOfBM^(-+u*&xV5GtKP^1S&(q!2$^P{lbfS!@Kw@CSmp{II`SiA@v#Gi)Cn-D_ zEL;}{2Ycu6$ndc8%G$c7KmPdq>-)FeZA~@BnTg?nJ|1q)4j5kketzXub&!Ag_2XNd zp&F`0sj`x5pbNCr3v|TN^uj#|p@s+dl%63{-DTadtvjAew9dap~k}X<=z? zOY$a3&--`%oo(W(VgXMA&dbV325s8U+sgypiVC52q*mxgf`DE{c_HxD8R^N1F`+?d zL*nfvDlC$svT8lLm_NNNaJ@##x(w z5M+QR)8U%6`61FT>$ydpyYM#X54s&oFeVj-oPNWTfP>s@_3m9gc3|V`wJVk`UAla= zdMbc3LPiQNNY56AIa?WMYaQPE%es{$U$%OKZ2>yH(wbV4n<>hV^00bx^Yp>JzpP%q z4DzMR*WAF@oPaN+ygWNcRGi>ybnogBpnO*@U-}c>V3lS>OjH!f%aW3XxzUzSw9g(o zxE;6uiCDi&SMT#c8yb3iN@LIy+{yX@PXgYzZT*_nE7z>s_{**%r_W!zeeV$|0bsn? zZFmweHQz%wA#_AR#yQkKKfj=$0_{L)NOyNy1MpXA8^C-~0k!)iSK@ahaIypjClq3F zU=50}|5sC&z^WR=LU9Xf1Bh2BAUPXV^+i6nH4g4S{?J#1^oI&`(M6Y@ukxB4z0E2z zBS)ag&A6r7NwpOfrPSrC0IL&SUK_hdMR|f8y4a6EpPLc$j4QxU$4jnRd*f5zZ0Ff6I9O8jA(_5zwZvSP+ zPjit$iD(?2tyLWu|zO5V9&YLq`MR~G{>NI7|a;(W%X(4|r zd2RgS$|B$1^;@jY6u)KJt|8zZDQU<562`Jj( zmIvIQSjkv(q{|onosnC+Ag7Qc{+%sO_(4eUK3bhH1L#La`fU0t?tj-pB+zyglCbxL z!dzV5C|&yc-j=^QwQ23rH9N1T^u7go63Zk?1)50S+uJ+P6MlBf_EocHPgR~U<8n+# zF9(i5I23OV;#%`VJ9jNzF$>x?e!RTW!NNXV+(_s)S{(aZEwzqsUHkLG`6{zj6crTY z<(7K4B8ijSxb)@+QVcI2-nn7T`q?V;=0dKhs4yoQCJvp<=>DDV&u$;uvJD4|sZ*5X z<>Yu0ux~&}cvNhBQgRCSb+gR`J+>O^DhhK1X=y+tWeAYro0Y|$1)c;<+C&>Zg<&a@ z%e0fiF0@%wkvfyp9MWCsj21+0NH-xTb(U&0&7lc&Kb{0kt3&TVjq$;4+ZX>deX{Z- z1%+{n`$av}u&oYh79{R#wK#ih%bHd5W-HH{BquK~E3??688V!cO4;S9#+TH$uU)xj z#-!OZ<>VDGV@7x-oGduukZINFsdwk_=3iF-v}DR;MLAh;y2i`w%?8XiJ6i~Oe~YcD zzUIdDYv<0G3=TLjU-I(uGri*C5)zV9==yf!dpa7+S1nbYG7&eI!z(Dr&v)<+2o8&i zq4jCN^2E8dJC;nJF+*Xzf+D6UD9UWMb@cEL4vQp;PGw|r2{Y-*-4%`hG)&xU4{m;a|6IHGzcM;d>1CEE^Z zHvSK&AzkCjn(fz_n#CzlSOy zT1lT-J_eQtS|RYS-{d5td^+w=b`#n_zvTx}k_b2OBJAa81N|yzLhiooW1!`Px)!om zWS8O>xUL-Sq4Ac(wMv{;t+)L6E0|k+znG z#*qu|xIMdnJ>HU}jVA#YXGb}_xPAGo#=dRackkVI_`E?ta70XeLIT?j>I!pH0&SmN z)6zV&=a+4}cI`cK!4#n1(8!oL`uKPfFnd(%s;j8ECm>*`uFl91q=V#^LghEepre#j zv=O5gfml`$JL5^ftPc3A{$nwzD@u>@b8!wSA={jccj`ktsQ;Z^?KOEx!45BPT+sGw zY-e)neJ<7ifq~w(n%v|trKYe4;Q%EUDQ@4^|6iZ~_`9Sa+S|qU>7}Cw)zlBDpD`B*VBltEl6v)g zdjI)jLsppUD~tQ*j~qCtw*TPCXW>!NF>&!sKfB(&d&`r6-E3a!+_-u6@F9&;Hy%9G zH?_2V?dbA`ir;8AlN1HMHa9kWbouPL8_x`lOw6rpUps(wN-0Zt#;~V}@pTmnA_IK9 zy*%BCA>!fb?c+i1w*F#RX1B0q;G0mCl9M?*!(OoMq6Fi!%WsU!!E!tfC?6I2%;xOhY7 zse!SzbxRAhr@7T@*Uu~Ft0>9I%Z!mxp0;S)nXB3lUl^KLTf@c$O0&7iZqu^)s>(_e z6cnb;S-R`&4V?#1UmBX(*fwGU>yRCKe&_b>+g8k8xO&IgJNF+xeW7n;YHj-pyEAqI z801a0`J%$S1aB7yCzk)<=;+|+?BYrV@02?wu0wn8Dm3TKO^ag;kdPoyLi_{R#W)Y3 zGN0}LAb|)ok`qA@jf(+iG=lUW);xAHPX8(IohJb!5e+2`6j&^y!vr_bDXW4f0rMna z?OS(lKQghjb#QU_^z?=^iYEb+Q-UgMkRD8Vc8oG0LVgd98qgh_*~yGyh6W{hBCu2i z)0D#c73G}#+YGKoh+tWqoNON&*pP7-W~gv?7?evba8|{^CeS-!S;&+&P{t1UoC4FC zABTK0yXh^kw1QIRabWU}j#`jFlSB=zaGjIwjO)w3%G)|JjnD4hvU>CBCvGJzJPDX5 z0qc{I{)#!%@Wj>fBw+LeB8?`36(u&2`ez9S^&3VCwYMbKB%^@=|Opw8C*#+AHj=eNFD|-@Res3?(_4v2qiq>eT@0jUK^qjV_0qyQO!S&##Y6lf7u?|Mgl$Lg8NF@IDx$fm_c7;{{Di@>mY*|)Xh`YU!uI~BcC!c0h1~O8Fg~Ull6-{GZeQ{Pokh6=~ zgIgCgH8r*4a2AI-m8TE*4H8Bu<&4kq_+pVK^g{Ll&4lmyfO#gV+Bv8}DK zT$B>v{o3;Bo$IGHj~qF2P&+a_A~G_P-k+$Vy{(~080zk1sHb!D-0>qv4yhl|a`p1@ z3kc##z_6Qn5-@fAWXC83A(324en@)03q&NKQ$rm~OQ5J0Cg5O?#~*SLld}t1i~9OH zDnF(afA0B*ocsZR-BLON+*{N^PU-t7a=GjTwRJhhZpy7(I5_ z{D;qtOf9S{tLmaO)*V#awL*3LSoyJ|Q6w>J_$Xv4?Y?>cxv`lQzVDivz{`geJ@hLo9Zen3UUQWk)hGTDy)h%D9S{?nNNRy`83ee+0iO# ztST?c5yS`kd%F9@<(5|A!R+pS|BpX^|A-FtaHBR=mjeHq6dCO8?&j>`6IWOy>hAm7 zKmYvlzOSbXk?6*%()@g3Qdpojs(2k8?ESNIdOrTwKmPvfySIHESiCAL3iET*(!zZ_ zogMA%?QLzt)B8UD{*OO@d*9zxUn|CwoS!L3O$_&SLy_)l8%rC%_}<_C`H#PSe%srg zTToqETV0SNNQw>dbF~NQ+S&UB|M~BKeg+1as*qFH_dZ78ql=|!ex7tx+57KjQqIDd%po0XL* z;7Py;K}aJ2_>&7IV3k4WmP`Xi0;d!VN?_#1;1mk0*45FqkWvOD_XGdV!VwgEg;78$ zFbD#5bhb;HYAdV6tsNa4=NqwCGCe3l1gWG}l#z(%-pw z9j$?)f*sx9x6lMO*c9abK#dW!Amv3B0;M^3AYXd0^Z-qzi1gb~$7Do$;Yq*;cJA1= zVb!uFi|5Upt~zz9E?YQf`jn}VtIk+-*P^Y?Dl*vd`OVYnyY{K8?cR#Z z7tWrpsyYp~pRq;fnWT*;0b@TRumwVEFbHuhUmkQx8leZErggJfq^? zSQ|dI^0M-dh)XZZO7b^1)Vr`>K&FmACqXIk}ojoH1!$Q1V3{76@Ts?mL z#N`LTu65Q{W+tVl<+wTcS=d>5I+?$4c&>NxoYvX%=Wo40pYQgz@{*9OFkj@c=d|*a}!G&E=6Bb zn;R1m?*7!)*7%X`&C@5&Yu~u_Ko5C<_AEsoM?{_kjPo=G`RK_B4;MU70zPI82Mk;U zaNbD=`3_Kxfp9>5L(zKqM?NIyIu6hb=33xMz#6|SUA%DGMAe@!-fL+~vA(+Xm%|!r z`}S&6|wtX;a{r^Ps6 zOrNv%xb_p|=QoDk+kbZ7uAevU-?DP`nsxJL%$z=L!UWZYKOedI@HthHHVgGnZe6=< z`>F+tS1g)4MOk&)#3}QZZ##7hnfN#)!Y5rFtgE(d>zet?m&~6%d-jZ(3s!H@xN!U7 zGXpb7DUH9mCh^sc1DjVbnKNtdg5{g{XJ;7U3C^bZ1Dm096VMvoso(?^95 zPXea2V4eiblYq&C+6l}qPXeZX3_J;#s}|sn6?C*i`TouTYecyD$BuFaSpa=hh~k1k96w!(zy9%)Gjy;v+sTl>dG=NdivYrjC?>0qtt=3>Nopm9ak_Kn-f9H}jCE2W60(#8jvgXi zp?bQ{wKgrDt=rhr&csV)63q@$qhh7juG;n0s zw@SKv$Ny}yA4azVPXd;g-+k+ext)Wfi@R?yZ44BzrEn3tG9*9VGHc4(ThGjF?Hye~ zYQg&ObprX88^oeeuRuRt&c@i*B0*;J{N2FF5VD0Q?@!;GVA7ArJ`+r`)W6PXPzP`54kLiU+M#D7B_uxsu z4b`ID^qBC_P_Fwv>a#+_BO<_RN3IJ`0>EY6a<^+)fv64?(D!3 zgYtv9i(9KIO2AZ-6o;&-X78$@p(H5F>RDj8$<4iLYd1f4^Qoyv zE;4mMry9Tf7%z7tljkoAn(QNSuO`;$!SQo0?v`c_uWwtL zTsyt@sk@cFPbTZLi@ZQ#q~-CGw{3$RO`n`Q{NmoVll#14Eg#>Dh>J^16-amzFi!%8 zZ6ikhWoaS6Iq4}$32`yhAP9{@;^X1H#Gq##7AJMj7YQ@d(}?lOlYmM8kzhpY3Qq!7 z`Qa}^=j`7(dBlhba^n;yjg)_A^~%-TPlB_>vY9uZZ&n;KOmX3<1!IT*@WYS8M~oY} z=z)oqovVkWtto7s!mH;?M*Vg8w7JSdh7bMWhaZNGnKWbm!fgidpiA1sPZy5duvz&p zf04PdXb4EYLxv9@DW|nTL5U{;TfMR;?QCvpZ7NGlit%=FbaHmEu`n?*HnXs{b8vET z^CUSEhf)1fAwu?bOmt+3KR`%s?w&rregT2O>~Pc6K>A-?Bn;0BGE-l6>IJe!AS5%B;xcqn!#p&s%m~c&IlKSRbyQB0 za|q`oauv}8npNLa8y6M*;IPIS3a>LcJO!9RLYgCPYZM5aZyY_w{*4*2Ea{txX#!6I zuBrsUYkpoZYtB~7#N?E;j4WXuQp}n}_GdTqBw(B}q3iI8RFo%~ z-#B(<{dDCt1tF9<}!;^q{5-ZQ?RfMpQ6Zx^6)6`cl7Wo&;Q1 z*C1)@9q8?Bsjn=~jtlYgbbI6a#>Lr8&&b5o0&&<{G2E~{01elbqgXQppkg;S*EhD$ zUlm@-J-(foh|rLbfH#(=reJngx zl4629oE_|5+1l7xTNh&jV9r=pC`T8oN@0FhN^E$bx4Wye6DqljIL>c1^{T+4gVq&1 z3AnJP3TKxRpg2WRk|#dnqDZ@EcQ2hjvTysga2 z;^z&g@7{m%qL>mT$`Y+j4RvpyKdHWHKhn= zL|a#PqsPyneTAiFi&v~(GEq)OW+<-x;fKHefH1)* znb{X^lm0>bQqtZil(-?mzFM@sWN$j?8qTtB@Z1sa_l&s1Pn9C#aWcklYjvLW#iq4 z_aEN%Gaxatxx0`$hV-#M;)Bw7|FONIs<@{6{d-IS->w@pAtaargbNmLU;n!*=SzFG zt^Y;suHfy*e#p_dKq|-df!??)`wnhevUI_$>5FeC^}Jc*#Pd|+68Nh6cv@p%0CBm5dHyOPUzKn5^zsX zYv8$^E0!-=ymH&Aho-j9J|U5DNoj)2OesFPv%Rw+JNS*0S44DFXh>*ud~#YwW_ET? zj#SQi^hlZ;>MD!TeK{vLH;=kpVr?NgA*{PP5ljOZ4$b6XX46)O0T&Y`{uAx6yBnZi zI4LPJ1g1D~8d*Qy{HOXLuc%FEB2n68< zL;ho4Lls+l{!M6Z0hC!`tW0ovt!T zNl9tEf}G-Pjr#^RPVPPdL99bwPha-^gR|x>nu_H?X}rqfUFYvUGqG`Sb@T9{&$p+$ zv%AGxbKShTGiNMVd+^fjdr$PuZ0ud!py!ZdbH)OI{Xfvv(JL%0(9_k$&BGf>{$UYO z(QNyq1p;Yc9oW8WiiGLONr{Pxlp=>bIc%NSn$bRrI=|8WUt3jHln>6P5FBN+>0?_b z+XES0lD2k|g45QR9|?vDnLJPDW?2wzFR6eOmC3=9N|c@nUg*>H8W zwB~jV^#A&~E;Go{(pdMLhML+zwUZAr>rp|3b`7+WbbomJ`H!j;a+BRUap=H7b+sc` z{n4M6e2e%z+q*vYz5iSu<7%P*=<=}x`wpn>SJ$#d4`n(V(@NFe+uq+J3bxmOsB`Y{ zzP$$y?mKweC|w{BWM*ZNycOzEljCRo{K4%rhxY8*w{P$MGkPeHNlr;kWAfIfs*)@( zlLy*YPO9zNwP)Xc^|N~5{E`}xTq1_+ohJdq=bWDr?vLi_fk3c?QI=j*H06lX=1WZ_ ztBC}fD@Y;CCkEy4CLrB`4h00Q=1IWJR-qMw%N3!5M%W|-JPDX50pGlK=HQN9`_)h1dh`-0%nmMYpe*6ZX|GF-@N?AHxpw`W`kn*oC$8Rm zVqk_Y<|J<>s(X8JFi!%8u6-N$V2DCW+V}03-~Ww?o4yxQw*7aLx$6fnVX62~V_NLE z-~Ef6X*hE%&#QmX|V zd&y401l)@zkQ{pbbui~7^P&>goRfbw{5i9qOdWOyyV#nRcTvo`DJAkn?7q6{oEGHu{PYjMAGOY7XOjq4Yy zOdPMMvMaW>ysZ4IysbUz@oVF&Ck}31GjF2&IGOP(+l5tSJPFv;%$#Zj#7%;`=QVe& zo~}GWcJ#;*BgV)mO<8|K=ZU_NsTqt(@TKc5E*{^#W}fnRnbDXqMqYX5o+~#o!5DIw zgW~4wJ8D~&&p-~(xUpkpWGBs7dhEjWJC9x%n9%kG$Xs*ysg0}WPghn_l#^4KvS9gs zt;@G{A3uL-K>HgKY{lXLjkQ1jG=KiWMT=K%+O+TN<(qePA3uBkl2P7h_YQn%kDl%4Yd`8IoWw7;(ExXB?ctN`a|Nn(u|Z4PkUQ)H=YE1MC0H& zQ%l=d_KsE6wIwm}rK0qxU^f%fr`ngbP8>UORP*?;>rag=ZC^V8vs+Um$`+-BdphVp z(Y=1@^r_>=PM$n=?&kfMrq-_=iNi~<;`rb$i0tK7n;% zZecYj$9G;VOpOWgcD6AyGB7YSGBLBTvU&Boa&Q7pdsNhw5*^})E*-8e7_Mm6Q2}`+ zUV+?IR1}gUNQp%mP*6aCzn`z4pC5IKs-&9`;T97_c|t)-LTpq-SXgLCaBvVjc5DWI z5FWP>ez{zsAT=o=HX6M?!oncuCXlxcbuT~&Vg_7yTr;33lCyzuaA^rPUMft<&B>NL>jv6&mW_M~O;}GWvNnR%w-8*`5&%&t_ z6_jO13?DgStjwAKp&&giEj5MYwY9d7Z=cz-KzV|~c-fI7(S=8Tl%ig8Tx?8C48447 z!E*~coxLj-O_GrrHGKH6;lsy_8?qrZC?qs24B|Z7XD>Yib@oi1Iez3=kbWT_F>2U+ zTPIf!Z{M0ao&-$l!;^rys2s)SSx|u|0VBbWNT56k_<#QX+q>TO25jWDrTK#7*bpjl zwX#NutG%mNU$6MT|Ni-1Z(DO!MNN5uFg`ro$HB?L#?sQt%GS=&4TOO||Mu&s@){HcV>RP|GVxz4fI-7yR&;&)v6`m*Hj}eFB0ZwLFw8xA`X;w$m^;Q7Awd}OO1~V4+-$|@p47{-`78=hZNrTb>aGz&=BTi0E-Jy zTmb>W0O5*=?1lnYC(tPa`ZMwT=0hhLduw6>koY=~qlh8`o(ULnLgtMIf(rpIbAaU1 zdI}E$r~__vWn3TY2rVJVw}jh>&Nck(*MeW1X99N0EzDOa0KQROkd~4V78xDn z=3r*_%<#&^Gp9}*KXvlt)6{H5TP@Nb@^Uj{LPJB`+^kKX-o1D6)M>qA$Byag={Y4T znyNZmD+_ay{k;8M-CQkAo*Lb|cJAb{qeqVD=;)e+wzsz^TB~!@<4i3*oxDA5UcWH9 zbLGMb-9tJ$hYuYw@dd$ZXJ=h@T7tWmKcES$tWEA+(?4|tiHe5~>70D+rjX}%$!iKT zVglVg-g-M(J%4!T3a+kuNSkK@#_|A|WNKJCkn)MJxTG3o{6#_--xR(<7BZfAG>YON zd05K1&`@4pgy>I>Kp@Db2sxWFNG_%fWzv9w&a6)u@>a zbAH{%EX}yCwx_h^*U$$(~{FyVRO`kq}&fIw?J!ZQIr@T{L_C)Tz^mhj-?zxzA%18E-Q}p5HY%zk4^&1iW$Wx(%yWtg* zc@s{jkDq^Fa44n=O;BX5z#K#dPPr&AGX+4Mv2j4^1y(Q1n<4xp3dAwTGtUIf?nQq< z;cI1pGVza$2j+{wQb%juH#w(33#D=Z;P6Ynmw!{BZv*$cIV-o4GcKqiks7`#S`iyS z8G(pE=>z9NCdVqIZ2jLiAhBBN@2#_mCplItr2?y7U~6_uv%EJ_*V3QKu`+TBw3xG1 z6d${-H{yk>Pb*U7zlzx>pk)%PrmQRJiJz}8EK|7xMMd1_WC|olmriTL5P!eMjJUE! z85N-P-w6_SbochvCfj-V@l3!x6EM#Ntem`Tc5{{#oBu5-f{L&6is_IB&jgHHz%v1( z2Ef_FC!h(YF9IlPYei9BR$2;9|Ewh? zG5l2S=!-oCadd!p78mAI_8mO5nVAHV$~D^9s4f>)0O5NU6~e~P%L9rk8XbvYptJZj zDUkhMQe1>^0DxKPEX^8~?$&dqVa&is2)&2|{VsfR;`bjBkbZ_Jd`U40DFK3lR4fSI ze}v$ffa`O3Cg3MBVUqctEt{6iTY2ohOK5y*R!&N=ozaaWs~1ciJ8_Y|kz5?@bZ+PF z1KZYXt=y`!_r$r2mv5ctnSkS{#U~^rFdhz^5I`$KbFUUh0OD}VL41ae_!*Sq$u%Ud z13i?oU}%Y;`Hv!FA=R~!4js@jL9s$$f^q>StPw>;@Ex!ff?hDwvBa35Hw%%vi-3GzxP>x-Sd9+v4M8wgKPIK)5 z<6&ZQcoCKm$!W8u`Nn1)v>3z=j45)uatfLLEYOhp=nS5pK_YHv?LJ75Bb;Y z>-syg`OQsqOl}}P6NC_a63W$yGhvjL3Ntxq!NGq_&MjihdL|XN7C{K(gl7Uq0x|pp z_?yn1zLqWbum&)aA@Z@_MVwU*{jJO1BY`1bDZO>;EW)l_FCwKg==A=8mPKj=%{ zJ8hibon1P6rn-vC=qqA?P*J)F2tk?q0bxeL@zYj%i>HoNLyGLm$gEr;yvRHgFwX?c zP1*ieHgmbf2ag|RFMX9@(lRL-5p)z_N0FMZ^Z(mAK#?~ztWaRWr6SyaYrwW)8k7{L zlz+7!*b$s_;n4^r|JM^Z7AOoym4uvF$<)hPAKWWljvS^`yVTF*OA@w{bz-@s5m=pI z6EymJ(i+>|-W=1VRGW{_tBru9dzC_eDq+i;1-Xw{LUMyY7%*g1Rp1Oy|(9VLf{ zvR&3*ZuaWz{+VOcR7Z{&rK-B-xg}xj1R)4c>ksxgS!37%ty$wWR7Q;$rJ}m{fr+)V zJ3x4XLm&si2NuElrz_^pm^fMmO?43i@o@);PcXh8@|vR6JXtz-_IP!aP^hgjc>D_K zeI7nO{v=@rs7%%vd0>|2SPeB*^^G@;&F!3B-QW5IK#W)sduW0!FGk zYGi5q5YuewZ!OG8K~4ZX{m4$K{!Weq3e8JQejE{46)&9wswG3YTT810c6qwfoas-C z1yd$D9WclbLSF_oHU)VmV4ew>X99jl0dtu&Ul?s~JQFZkBHT(vzsaifrL@3E=?^rLa9#*R3p0OeDS=5=StANazo%mt zu0MO~VAZM14_p=_859f&5{X>aE;Zk}&ibk0{2^m33}=r~RhhVL_6VEeG89k%Bwr?1 zL?6&pTd?ocw2{-bRxi?72p7S6?WHSUrl)6t3tTQ2UY<90@Yr!H_s{=n#n@R(RR;_l zbME%U;qw9_BjZxi+v|OIj~+C4l&a|l)xm?+$NmS}@Of+0hc5FD2@87{)7EM|chG;n zTB&(v)DM69)1*0L2MilGdZg;GA7;FE^Pv$Y7Om zDg)=8xqA1pnH9F39IIo)4qqAn9|Nb2pSEiK>YrvWS^VShA?q&Qe)RISE%qXL-F1yk zM@Ii?^v3Ob4j$IkJ$n4qp5@!F-g|;1Av?%hnhWgT-qM_V>;9EX*A4F6y>rL#{)5NQ zOwHf$Ou*twcCerm9GwK|6oIolg((Oni0XwoA7Xp=hu*H1%IXqPIarmb(zn0fn1xun zzkK=mLwkdywzw!dF1wyeNkT&Rn5lumTG zz~Nh#O0ur4e@^NYeP=(pKVX)k)z?**WoPF>)ZaKM#rDw1|5jNh%7-_ddAd0z`U_%~ z2f#A{(+*F`S#oiZt+UzHL^+K{PFkzJbGJQFaolK>`wR42+aDh8wv*cQtx z5Vn}h&20g>haY=C-#@Rkk)1XzI73ZNli2*4Ex zAunfKs1Qk{P4%^vqC!zcwV19^R8+|1+^{urkZ-k0s|qvYW70~h7{(sZ5=zTh_sWJ( zUq1GB$)#dZc2c;1P$mnp=i~FUKzwEMufP5L5mdWUaiJjjU7(M5JgV@E3vzO}-u1u# z{_D@5KlI4zKo*-62AbBlF_44lmtz{`nSej_b+fL@ijM3fHz;Ix$Z zh%msA`1^@UO3S$kf-MukcVGrYdNKfl6QUx*!$N`yAq10_8h}4IT2-2pY!U;rgp~_| z9ux>8>=i#ql_|i-RfOJbD<>|7`1KLy&^93(bG*V8~3Y z;fUBsfs-pgeRWMmhM$$$a}&oHo(Xu#k|oQuZs2Q9E~n)&FJDxa>}h`g>Ji;t8&-e| z7xJYm^`cRWM)JzkG+{xU^<#sxhYxOFyJGp`#fzb`YM&2aXlQw@hz|>oaI<}Qa^d#fk6ubj}{x@N_~g$ox!zD#TTGkbeSbg!z6bak+N z{^0uA)7l%gmM#R7@1iA3mapCN@XZ@*T)$GBilTlqL!JpZGt$$_%*@Kp()8IQv?ouW zn}CjxQhl+P$cqZVzhmM}Uc;$K_fpP)3D>p|N z_k8b?8Iz}K&RViz@8KiIPM+1ja_uJMnArtX(w~!)II7yPn|Yp%B0DYCQtqG zNPH?%kc&jUK+*Zo`1rc+4y~VN&6zn(bJ~>2Q>ILuZxNo5nJX+TA_Se@kHrS(w3jYj zFlUCQrsmYi6Q``a=NS^0mYtI)Wb)qkg)dJnUZyo~`gGtI%vpcd#K|{2E+s=C5O8vy z37D9Kc_v_vwiHG4?HyEYftVuR0ofyN4wYaqK4eDQsGw>L3OPcqq*Cstg`k(I5OX8s z6utc_?)%tUR#7Bw@9TpKksmW6!=^5diMjWK*x}U1HA}R1UQh4&0Om=cr%-(s$x#~e zzB}sd=ItwI&YCh|-1N)wiXP%tWrWS>4so3&&jidf0mGY!Cyp|4DZPZN_3Nk6=IVdg zBMSw})g=u{H9cvpaX=IZrE?>YBa+hi!9dCyOUqzPlkj2*2$T0?E}6>|sA07B8D8~ic1^R>6=YO zf+SzZ=lUmf4({2ub;s^~N3Ys=_ymT&i;ks7uf44_J=({{_@bW9p?y2HY}>U@&%oB@ ztzU3hL=2b1*^n0GY5VB%sbfd>@7TO;&!MwVtQ|cee;0*0LnD;=JHB~*_4HA_L%VkD z*?;`bYaon*h#W;wc<5=h7ucKJyK?sUaUu$Z*d7R@ft17#InqGUgsvu!j33@OarE@H z7xu0m-V7xqA`%maE&*G!0n?dh0%mIj#Y$PiMF0ArTpZ{=(%m8}<=^D&9w%*4F}*+zhC6IU>p zEQANk0rCP2n7+UK<*&c}@}aN2w&GL{=gle@?@KY{UERHZ{N=BI|K&qZPfJOXx23VcjngL#vN))ywziJ0-uHj}?eG8m z0{3M@Ud&r7qg&UGAJva4z!IM$Xlw5TByH&_D-&-b|)jSrB(3o+C|+>^JdSP zqB3ICuwg3Wrfz%o%G$xj-Gegdn;Yxxu3yyMux!p`^${v4#Z#Lw>*%BBP;m8NiSZ3h zdG~d9ubee&s+!6uwDHqdU48J>+{(_$jdJ7R@@k4cwSMj5*%QYCs6=hToTYkq9zK6% zW$WMsG5lr7$_>$8v1-xWxpU_)Sh{Yb&h7h;c_v_@`l5OR77D%@MHgW#l*A|vw>O(XNNVp-7?wvk; zVAiC`V==pjj!@B_DfDoKu-z=(#Jq1A8mQc{5@!7As>k5Ka-NVO4y%J;F*9KAp_3@oSc-L zjBpdr1WZ1uwlZKt2sUy6cISkdqh- zC4C6x2=pOKi{T;1_?(#OFa?x7U<@S_IQlE)w3O09TLb=)emTzsTvJ<98=0eMsme_Z zbh9*ha_1^oz;tzV_UhRT~m-6vzz1{0Ac&YMiJ)6JaB40zDzY_fnvQTOW!F z8|lLlW@$rnLs7iHcThaf1UzuikYNKRJhi|9-L<-2q(63rf%)~7voywz7&2hMz(E5B z4jw*o&^)7O<`!1A)sp&H-L(g`cP-Z(Gg5uz@F9Z+3>rL4Rc*}fn-8A7dSe4WO>J%H zWgXo`qsMEE#Nh%g#3R)w%-yV~Z(wZt#sgm*yl#l~Q>{%9Fk8Q2n%g z<+`o=b@a{xujk&w$1hAleGQUq5WAOG(zL1hcl5m=OgX9Di) z;hBJWCSU~kxwIYR4Z>y2GXWRlBtfPM2m<8wWkneYG11`>fv%2kUOY0mc>e6UJN7XJ zh}0CfE96bJ;=#EFY{9-CWX{qATe&WiPObF#EFe_?#@=G7~g&YwAZ_B>GjOf7A( z{43h)a-+OlY~Q?oZSwe$;jJ4tu3Wu->(0YxW|p=Nw7Ik^S}QXneO(-EEiKGnJbnD+ zx!J2%mca0H^Ymsr2Ia;!)mDgv+387fk>Q~sL4g55LBXM@ou>T;+X{9?n3DL$%SkCY z2^2x`L=?_50rO12nNQE3);X~6;DLj?Hg8(7_@_BDXH1{7@wQhM+`%G6XW>gjo(cHe z@xy!fZeO=%&9cRd=FOQkW5$e`v*&F)ck7W{-jQj3@A|1D2Y2q+vUbzzZrbsX99k0C;#}dImPtG!?*F-`J(cwT4^K7vUn!ohmUzCU;^qYQEu<3 z1fU{9?9$)wzHEPI`#&~#C}55;4zF+9_jmh0+;})AVf+5S_J1lD_-^}F?*DA&ex1Fj zD*XSm|JU@}2h8>N@AJPkIh$hwNBsbI8PXtxf66M8@=U->Z}3dO|M0rO12$ls(QUyz4k8DWi%160XJ zCjxMfp|Sd5rIdbrQ(&b@P2Y*QUzG~zPuZW}$!XlSj{YGqP&2OZJrf{u@nxA^1eHP| z1$+V-P9xl)SiHd1+TGh@VPqnM3{D$}+pyY^98m)Bw92}gD?Lt~aizYToV@%bMvS1d zt1mA$A;Km&K0-h%9dueit=2#hi1v0uBIzwPw|srq#>M+>HrQg@jEA_}Uuf#d|s(GJC3o842#T=4S8ek=K9<1j1Eh{kyu~3tkKR z+Qq_XPs?Yy9FR;CYbUNFw$1d*AoIvpO@<%l6qrORun4^iRdVg5;e9#M0N>0|1vMQM`}6 z?c30pgya`MKFyU{Mw37ZenUK;)~yCr~Q%;PGi`M0p4z zzyyj}R)(hpARIgsFq`K}CqG5^`r*;>K3DyZ2LUs$UxUd>>wnCDWq~je6H*wFb^DhW zP61v2&-qUp)cfD&KU}#jSsYXnlQ&V2ksF$<8#sD96b>e@X#R6@G6hIZ(zf=7w#G`I zlLyQ|3#62jE%H^|of4Lv<#}cImNzYJ?D`y-3*@Xf&jkE@spgOBBZdweHhkn#Q#*Ix zpwNh@=oq@cbc5yc+Gq17Pf;B)V)%$r8(&zvc~c2OB#QJ&j`gyor7?Whl!>4s9jUVO zsiliIXhyjreo`R(oTla?!zE)>)kdl6KC|)g4+7I3&jd`hy5AbjI2M>k?3!l+=9z%w z6S&F8GXZ0lqO)aVgDAo;G{`?NDD+)SLQ-lPXSs4mM?$?pMp|uE8Sv!=*<`-tz_wMY z&_acsfzG$m+G?t^qcUt}{xT60WBw~@`a$vM8iepnyHS72EnMpe=bbJ@MYxE7a zw@AwBJ9}EQgB{A69W3j*yNR9^UH~R-lh@XH2GqCYXD2#eIlt4q6ZF?$3Z#@^7%*+} zrY3Q$$j2cm!~Wqpy&d~s`i4lG$$-Zq3wet$AuQb4!Nk^H7;Sd_f$m20M@Fetz+|W7 zF`z1m6P-+MKJak0cx7*CX#Vtu{xM^B2g?Y+Z~}x|Ca=$lb-Hx8#`4{lx3 z)A4v`XZkcNH#e`Ku&t#sE!@N5@rx9HTVq}A{aX+2UAy9zr`>atbO8+T9J#C{F2Frsu=AtgN%3q)0PKytdD&EsSHkGj}Bd$3k()B4p%^=|vTi-}K8!!utKpi{yF3#xZSp)5FwX=$ zXz)PQk;5m=SabURQ!|U!w#w&|2mEQvvcdoHC(S9thm9TnpMM%KT7Bd&t+SK^_@=e3 zbnl4&{146b8b9z%z+{u6at0pI%#3ujw6xU!0EPn^Iqu-sV@D+BJYcmlPzRGQaqaf+ z-2DnTz%Y~zChr&1VF`)zRMhjmoVw$LMeaddh-U(BE@`N*%JB8H@P-2{D&EX6CDO+y zI3_72Ej=wYvt0JRrCricR3-|w@e2Xotv@~=`%2UH_ z-iC%fviFNl%oPLJmHA}x1xe+dV1-Q-iaFyX>~2+A7#U%8kmsH zc_v`Lq@4WxyzGpWglON_j~<^mbZX6_$vhLV#wc|PE`BvY=@%*;#RgYfZhBm2<_Hz4 z0sU8;TksvD)k=idwk{tsl%Cq}6_{6`oBWe$_z-!9$1(~Vw zVLt9Ijt=&AcDA;}L|p|10-BIYYpN|DyIsH@ z^NpGD6+NB3TQ;s-zHG(17Zs2f7O?TF3(^yUgRD*NUpb|{ZH?B7rAwAA-{4dUc|Ooc zVCQ6KCWd+0o7}&8?7)UqYnBs{@2W!?rJy<|c~x;%o-oqg#?;^(h6hzI5&5oKZ(j`R zR{}(-D##HP#roJhzIpoK-Yu(^ErpzC0^Xs2&WQ~*KXf8ru`3aSOCQeeuD9LZr-qQNs#_d`# zXVL_f;e!Sa0o(83q3YMl#U$sMfJ@6O@^m*YT)b?G#;8F9Km^P)0gs=k`O{{e2^e{q zI6d_uwVK%8(70b19VbU?1_e3fOoS|NG^Pp-@NgpS6HUh6<=^VB$m`KgRC`nJpARS*z5e&(YJ{fmC0f30O=_zjCKn&rj~$xosKG1YBQT2CEC% z|AmBdiK8q%S@ghkcEC@-^7HCoW&=fz8qg-GI)I+quC5N0hRBJA4~@}y5;|BVd!p&- z$3soeBc+a!3j%R45J^;3k%%?$%i@WI0-|J;iwKKfS|_RQ7xy#$qQHbok)HKG9 zRnr#1!h&fFd2h3=>C>Z|Hm;jDYx3C9qt(?kMvopnH$0P-0a5=pvpcsBuh3dHU30?d zF{3p!MvoaYdSO6xY(jEc1|F^MKAV&0*X~?0W7f>k#Pm64^q8?~yL^MA;!tQq6wE!f zch2ozw`k_%DHF$!8xNH+8q@B(_=O|MHko~Cz4@1p?Btn%*^Hp&fu=vt1dQ7umyt1m z5<4_nM!01|E@MjsoBuTTvHr0oj_d}ut{~cg6^2&BR>;Zlz*9=CtCNO6$F>f(%(b?l zBU{4p!ZQI+pNQG7s-mW@u0F#rF)=wgH61svxBb<9L)~R7muOBN51L;!HFXUQ^?5G- zA>om+@wlly@2yXqU&Av2)3(Wq$HC#t9Zzwnr~Q-LL-Azdd87~+wq-8-jw9^%OujV# zp};c%qiUGW4ydU3#uQ2!e)J}@3veSjJEgga1sBO~qBqbF1-AlG9qBn+T<5P`*ysi7g$M>?b6hBv+r`J#D zXdgXuGXvHj;fP9+`ouE<*OwHehuS~Ac24i`9uWEN+I!@p1&DrmCg7xG%8J9|Opj`P zjYM2tT3nQ$hvJUxY(Z99T6#LB2sGG{pdK)T%ym`_2FQG29{j`E0!rLNqay{~b(HKAP31G~>lZy+Ag~5+bY+ki!`Z!S8jniBi)=qdwRD%ffphZ+zR^)N>$oggT zr;Jq{HEOixO#Nys*I11uSohlN^M#E)epmNtEm|;nw8}^oRgH<8qN-6siZeXAwp%YT z`)7H5MP7u{v+L(hpE!BZJv{tfWJDw|e$amY{kNY#cGr~`rTD*jc=06KRkwhUu(0rO zIYI37e*E?4&t0umqO2&l7kAI<9Y1-(z}dqm2te5Y`RVTNd;j^%$F3H!Fg3{W#oaSU zjvhbr*ullaCoqKMU0r?eKYZwx*A-^P`&vD`arWr36PL|xoZY?s0z(;mQ!ipX9Sv3a z$$^e0cl1x4JgaYPVGFshe;_+7r#qz3Rzz~Hz67a&VqD8NA?8({=;IFeTa zj5@UpTcDCCaL@{I)L@5cX0Q{q54KDKAdpB1q8CF|0fmaQ>B%4`4cgR@p$oDsP7b@C zHEfs|g;QW7^vjheaHZJ~1^Og_0AzA;CAu-&AJ-uhABTKWppQV~iz~i~6^go|qKs5g zqYP&y4nq+C)P8xJBIniF-J4f!I{nzIyp?AHo@*T+i|L)nzP9GLsM7j;_Z!C#uH~74 z^)H^ebob#?)7Mru_6|-&@kCE#U2T0;RzgNrVz9fdl@)OeySRD0_41}T9BCk)S1l2j z76~)rB0vMcS`eC2jscJ|Us;ahdrn4jd~8fibaYe{s6I)KhN2r}wF4LgX)DC@nUa(c z4=};lSi}LD6ohq@_72x#F*3)5Ia%pxDM^V5N~TYIJSE2+^F2~-S*`#osVVsEG_k+R zaS4pT&h~H+@JztU^`TuJc=_P|%{&t@&jhT0;pA!ktH=&~^z4O+DQMn^=nZp7S}jP< z&Pxq(wK6v~F(n>k8#{Xkl&gTp7r;IZwbj*8qd<*!Z zV8c*`2Az+gz`D^AOKU9Pk!UHWoI+%8up~knm5DhOQlO<&$@C{G?3YuLHjoKnDiMkk zu7Q69E!ON)Nr9dXKWrPq41oS=08V5Thz4P1L5|DTH#Es)?L8m5JDcmuMcFwO(puyd zR!|OOAt63Bw8-UMUw-@C)7B&@&q)alN*06Qj%YxNh>1|zER*;A{>LvLdSudSQF>~S zr)PL+HJ5@cM7?HXi@fWPKYsi4zO$vKT9lCx;^F4%n^%lfmfRcxAkUjx+kX4YZ$E$P zZEvipEJQZ4hpVf-b397DQRj`lt3}rJw?F^#=|g9uR9qrRi4E{X0=lhNYBJ9R?Cpau zTgrg+c_v^+vWw}zuJ1dJSFo(Y&|0+!%7jD{Vdw74)YJ&yMOx1Jtoo;V<&_68>d zIuBQgsVF30ke+}tpfH{Zm}df}{U6(Z9c+@OT*KY-r%f0$N@duvQB$rI)}lm-*$2%2 zsB22s-@j?z0)z*K4jeXYsLJk)YPS013z_}VAi954f6sy`<25Fz4jDXj$Ve5f5TPI| z6Id=JudB0vboy*FV6(*;Na-!Re59u&C)UD^MhfK2!$+XU4%Z;l6Xs)fM(QB) z+_8v!KN12L*@qmQ6~0){UYN#Np+uI``z z_WRH8yE_y>{DTiEUyu|Y?CTwrSWp3||MIS`PyhJaA76k_k6YMWQ&CiypBfYH@9pL8 z5s+9?D(dR}>p%bY`={RSPQ<#KBo%moQX@nCy*yl8U7UmS^1HwMQB51=2*S zt4fLrGBcwBeBDt$>TGWxmDT&@kAM8_*H3+&(z*sbhDAAojFhNAFE0@p zqYz6)UM>!B#6y4t`7R{aQxah_)dHgwn0)k12UCls|5A|m0y z$&^y(5Y1*d#yE4bRfnB`IS-gpPUQ#ehzxHfRtu_=MRpPAB&Hcljqv#?_l6pDmFYU9 z#5GbOF`bz4jMK!YG^YN%9{f&`0;~>Pv;YDOL2(3%x^s=Zorj`m*3RdNO_!Q{2bFG1h>BCx+i276h(e)9OqGqg9LU|_OLx*)QnOQ5^v*fL{h3S!@u8waVt;`?X zx^?l`aXnp~!-sYBZa=eVS5!APiG}eIfgUanuT9O2Z(cfmLhtC2Bf7fB^bL6?V9XeL zoH6$>=g7OojvBPjV++sE7qVxa%}k2E^($aT9@1D}; zzcxbM;pQRo5}TVbZ@9@t;W?(tB6heeH|0?}g$0ThA6jXsa)-;_<)&dvGfMUFHnMh= z)0Td)3Cm_-OA}kS6ts3i<*Syw4Zq`)H#gE{6Vjt zx@ghDpOzoC$!(C*nXnQpHkmvVu<^AMy4!c`-?m}>%4JLE&zw1Z`ZUdH)8-$$Wh__Z zzO{RLn`Z*1xrv>-2KG8U3CIJ^&B+xA1oRwodnnM0>hZ`?@SS#lc6=->V9z=|t(Xin z!MPQJe0k*KE-PiL2a{tmbI&Z#1l-bqLRCdbOijX)AwN}(ce>y3Ef>eC8Jfv@8J5vYDZ)|K?c z&(~L8S0#r{^3@2!C1`y_mriTL5P!eMjJUE!85K2PNY+T$(cRlun{3B30sHbyz|A}p zFgGzUN6@(3TS|=JnSgKcOu(>wnPJ*oU62&)VgK^#snZv3J~ZN)fGf&)Cg5YQ1Z@8X ze#3W52q=Dl+9qu*U8imwK=O9(T!G)%3PU)O$SP>4)}C?W^}p}`WQmdIL|We@TzB9B zXh%T{+HW4vMQDwp;y|g~WsdR7mu=FL_HH`=D0g>Ug1oD6pp+h&ZEgOd{^9yt;gSv+ zdQcVu);AJ1Hdh6nTeIMv>5HtJ_m6GgZIW45Qd&_}1LqLAW||vw-ke*tWcCiD=hfjs z5BD#dyZ)|sVwO-;Ca#e-Hq{sUo9M5bGj$Tr1nh}>$}<6z*-FnQ=W3&e5jHUM8L%Q; zn4Wm>=shxP(V$2QbmZWffU!RCOu#%7Fu_y+k%IF;;iOHeWaO;F(T+9%IEql~05Llo zl6RX9{P5?|bfP(hM!;80&fLo+M`M!jUf9tR$4{hwF@^6$TxXiejN^{{>+osMFn=Mt zIQPOu9BA#p|FM;QCsO{(U4z^iv<-ffvwsL<0_WByo(Wixn_qx68?mDfx#b&+E32od zj~F&$0##%OCnK5XdFp~FWmf8*p8Kt#X9^h*yg zp6^DU37DH4^96DLqe#Cp-xo}L z{10mh+q@|#QAcvv`bZFBTRGc>*+H0M;5g?pIq5L`K{8LcGeWC~1stSb<;*Z)hX^`G zwAbCiITZu2uGG8V#Z5*E&^^mcIy!5iWqPu7&F5 z0t+0n(ox!*P1&Cr8JTEVB&UEC85LAS2y}X+22gep*U0*y`OESLD0nF5ic63^AVd;U z4jCrQYyg-7_mgZ0G??Es|Cw1r0vrWsm0*qi$@#gIW=p>vLWzV>;hBI-D+qqRyaMsO z+6425$Ip9sTfcE}x^4aX+UdPdylr0cOu%RLT|K<;bhi|Qm^kG}IeV1^JDDEdyL0D; zqi5}KhxBIRFrqXO;!e1U!~!0_K^3<6{9Z0QXExY;3GDpOyB1hC)y(5@u(l zr>3MNa}5s1{{0`QQ`r7_Cg7Ze_=K2rh6P_(BtrhgZ~y(Nwy3gJT3g@LDsQYR79>Oj zy2htxW(#ui0F$Wr^A}0ED8Ec1ZE9(46t^@>65~@N!lM$BQlTU3XbCHBD$a_JPRuB8 zYEv}VG|KBTvy*~exkksvCZ=@O>+SFeceS&!vT^i`5H@zT^Gv`!z-0d8kM3@9jFSz_ zQy9Q_Na<8a9&m*M3G+QYAAkP*sU^`S zNTKh`mk))uW@G?!{pkEijkXe0W^{Lc=&3WkP8KVdL(g@E6j@wI%vZ=|4HcIUlfm19 zE3?iRf{vo(a|Nz!W4ZZ3OD3oA80*M7(}zK8mR9BF2+5YlGxoL7KZiwmarl)~hzek5 zH#Rma#bk|>0nL1T^>ttcD`s*o_)B&?v&-S(W8QnJJAxTbEMsJnbFM)aon{_Do(UKn zQ26>gfB7IUD@c!uO??;YZ2r>H=)Tdjz|5>{L9Vc%2!4%^zewIW`G!S?y^Bdsi}1C7 zdGFfUW42L*f}fSsDr@hM1bTTm`-B13J~7HGF51t~;L*(+Fa1NY1f;Zfl$eC2`q-O3 zv#<|J%E*fG2~P@qZ1U*hj;o%&LGPj!87o(snj2ibeC67$`{v%s1(^}A{e9h@>mA!= z;O6P=|7NFdjQKlp@7zert8r?+t%+mpnLq(sbjm3>F)k%&OFWO2OWGv zqT&+eiV)qa`n%VzSh;r7-Xo_ko`Bk(MGNOnRNwT*$ul6V)p_EwTL;&z-MDS%?!5<( zoVs-G=%xeP*DabbPJP>JTSvDWQ+EVf8r*(t?donTlv@GV^(xt-#VL?y z4)dEZaoGAmmBE#NLM0)pf?1>A=vVqdmBu_1a6wKEqgk!2|NZx0fBp=Zo;q=1R#F&f zTHnS1NS$W_=9z#&^jnKP9EK#%1Wc8MXrLls3C2{Vj5;`a2ZXl_hJerx@D6DZ4wwi` zoFr#Vpe!W#4)C%Rzf3`QW>;TDy}Z&nSk$KJ)#Rd zY;fTcmGDZv==j)JutJC{Q`3Y6an_Fw&K^Fv9Yn*67cYj&s(n81!o!J3x*{F{DL31P zSI(ik)>4u#Ub1x6jyK-k-eu*LRTU9_PWDz$ZeKZ}y>-osg$oxhf_$0Q_Gk9?j_6)h z8R_a^`TW84v!}H;YAs#3V8OygOO`BOyXE1VH`cg*r8w2e!Pd<1#)Z@SHm_dt)BO2A zEn2*E)!Kazo|(QXN2@IMwYM;RaQpI^eVbM;S%m%z7cXDE{`B1kkDoK5-^vtQ3$uH- zFPuEIamC_AKVkf(T5EVFV0U*nH*C~kGz4G(w(C4VgJuF@G%+zDHZmx{A0=kKw3Bo4 ze72uMA(gy?NipG}!NEa+n0csGrJ7+($g;As3IK(agG86+W)TjZBxi^LAk-!BJ<#^T zi-?SKDncwOF0QI1kP)UrQ;}$Qsr*n$^eZaHJ0!)Auk)X00{&sZfWgClN+|_X3uZ@U zRpcE@=UY2h&r=;bXwZNk{&T<&KMWW&T>ZI7kex|iN0qygyY~ZKt!ZjtDIWNrKMX+s zfkQ?dOixaTFGucwrHzGGn92Ty<3|k~`~&*pzaIt;8nz+~STiLhrIp3E?>KlFub!qc zWWc~5z~oCh0|yPA$7;y*E!L4$@(GIDlwD(0Dhi-=K2 zm}9Yh#>8=>HPlCs;hBIL-5%!@?F9r1BjE#M9Q&ncL=%J(SiMPf*Ul(87g9j~0Al&O;^Ph6DJOey4&YK;HBE5PLb@!k8ju3s~E_OwY8CQj0v zIzbQXI#q)6Ou)T(M7tSAkG!RkX99-D5782y2^b3$#YP#=C@o-pA6v^Rip1@GeOSPV zZWwYvuXJ$~qrD%*4yQJ*S)#S`dV0@?E|Oyg!(j?J!cFhHqt0&LzH;WQDHFy`zZ|dV zAY;#?;jk(`KK{`rPrYYgR9qH)+-+ zF!8FZE%BF;j~HHF`e^Ud%`WTgT(7lm)}*;}h^cq9#%!Jmn2Hc$;_+S4*9E$ehMZt` zXYYup@IXJmz>s%Qu?W$or4z?2D60EwbLy)~@&%ch8595#U)F-3`ulc77ExVrs~#euw52zH&5<6#zP=Bw~%^C;`PI z!!rS+Y>vAfJQFY$kDgwc)!Ad4wN}oZHDTtDpy^drS?JTkibyIdNd7+K)ult**R0T* z{^P6};OT|NbP$D=5uzhjm);IvlRG+_wyavbX!69-YO1PgW5%fL%|ni7UY-#0zE*pS zmwFr4t(n6!0XNl_3bWFXA(N7thL)Pvg#{c8(}0np?SNpkB$cH|qNM~`N~ERrgRLO6 z-t$br2sBU%1Z%h-$O`6S4+tQVh!P>#Po_c}s8UW^lsv&2rTdct&jgIejc#Y>`@Ua( ztIr8@wSIN)ysozPLG66edh3!8StRATey<-ITsf%?BF=sL z51loMMwua|5%rfhRF~z&x;(#q`K<1~ZQFP6-KTTGG$b4v$;s^cJQJ{-KqYIc;W{f2 z0G>A)w=N|SS^hi|Ff9#eY;oY$6_(D0z$VWGj2sQjpSHd~|Ni%XD8$Jjz8*XiFwX=G zjKExWh)KgbLScP0DsLo*5C8(%$pqPAxj94?#z`qji7|n*ydn7a;lC#~@mv9RWiE}F zn}*+BX#%1%CMIp^|4U{A}1ksD(+mMRKVrGF@d)$ z+FGR2rdCB42LwQ;cT_eh?TR*eWvcfh{o}`vU*ws9HPlAWx{{ub-8(A_ceSHHm{L<| zws-aHNk6I#8!}WyT~&R?ksw68 zsj51D=01~GHjeI|Z{^7Fm4!UGetg@KnHnR83>h?V@JJQ)Npp@GKQn)0=i)(difwuK zF6*vavp^LT2H+SNK1zMc(sOr>o|{|PIOAJxku_VNJ+yw&40WCfn8*UkONa&_Jq-b% z#3VMq874Q){~Dl(Ql5XQh?NRvq;l&6OH!oyU&nw+DB=f7g2IA4LDm4wJF>@FBM%48 zK*%KokT?t6iOImE=}1}7@L-VJijs^u1sVwR`fGy*^DWlbGvY)RTKsDGb7nvJz1awq z1Bt*Oa+vzBj?p*9I6mJ|gS?og7G6RXh_g&jf66 z>&|WC*Vgtf9^Ssb{@7Fiz|+&$*V~-sV`^q)Z|~~#`jwR}mM<)D{sD9ng+^CTC-x9g zX{j(JBGAXj2OPse!6Bicp=@HHB7hvaO$`#HSZ5_Cup*qu$Vimp5NI}rz{8990wMT= zEEN12DalC`iI0zKCIxEfjy)RBe0d4jLP64&ik6a0Af8N&-oTzCS`qLG0R~u*1uEy% z6yz3Bj$aE!i=>SWw8f*&7vXuX7&rr%Z@!!D4w%fgJ$ z8(cW7qq|Rc&mrrQ2BIP+q+}(wL2ID3yM_DX2gaw@tkaq?M=!nMtDL^t_7p2YjJu=x zv!@rA&z?3xbB=aV9TrHgKbEaJf8WCR7<;Rym(Hx8HEG=V$+Ilzs|M5}zC4fw7P^O& zC%c=P8J;_|a>n>E8fp`c7L%h{R778{tS!kSO(FDt^-}-9+SwCEtBqFESQjsXftHsG zGYrXP;qOEp1re6UI%}3_j#XC~t){-vOOTO)O`hzumgaY9B?@Wqqf5KB7R~ulU2U|g z2G0ci^0g(XehC(l0JG}q(w^(B-;6wLo(Y)PgwW`aAYdyKv&VsajTMG`pezRunLBit z_$G%bM(NjdFG&HZKO|Rf{%iymSENb=C{&`fgt!Q>e{ zwSKX(uz)5O&LAY`6qLQ$4~kiXwu~6fz7Fuc9K0Dk6EM#N>YY?&ITw( zjR04`jAT`u9Y_IwJyZbzYD{SXeC@~}>ZSGUTun|2D3Ap~HPNU!W@ZMC21shf*#IR& zvqKm~XvA`v`+oERU0Q{Wtj$gM}XRa8U=8tTn8HUhaeF~12@ zO;kwznGAJYKsv)Ar>>Uy7M1fKHI+0z4nEBdMe+XLLGk&`O*miR>cquX*nlA+uWzc2 zxW0bXoas|%Ex#F8Q-kG$o$q-jU`c(f?%IReyOwK?8L2*U_>e&Z1`QsD7{czG51zex zV}tLzwl?&#j_#t-<26PO8#)3k#3R)w%-yV~Z(wZt#sgm*yl#l~Q>{ z%9Fk8Q2n%g<+`o=b@a|&x^8gq;o}#k*`h*DURc|TctBzfADqp_hlJ`Iu+<_aZHa;<+LP$OdI;ysESQe%!-(&>*NLJk~t zNH`Go4x$qT;K6tD`Z|(=tF`xiUvIa(SyCb_sAxdYiar78I!F$lz`oC4-h&KRCM_w- z%1BQZRJOFi-;1>aOrVPH?oYq{`l$zGBF*sn6&DCnVn4b|9=;7vqqCp!Q zJ9|5lfB5wK&mR=BMoDE!evu$GGQ^)w2oClR_BbN=_Vx9B`t?g+dt*&iX>mbrdQ$Ye zP+w0sH()9_xOjT|_4f6>|MaO3d}|1c738F)#z%&S1o-)QxdMaG*FUI-6yEoB;ra-R z6$o=OQWN51-USB)AOH{^5!np|R1u)krBl|@(m*`F`OrZX9US}#iHRMQgh~JBnSg2H zqfuy{X9DJ#fO#fhEDuS_JqueR0rkTCr3j!95kLyxAPX5g9vY?%)dnzh4#bs+2~?Pu zBM=C(u@BK^1O@Du*pHO>K0FgJ&jdVo-pTkhMX^(e@6+qoFC00jeQ?{hbxRk`o_sBpYJa5*t$rC3{nWj1OvTJ)~U_zR^;SK#=d$bQ7+_!b(ilqx?Pn$di za?R;K-L-0~w}}aNeRlKopm*TJg{YPtKO?q@}PC=!lPTD9}bit^W zRpvz4TH4rnbiV(uzbR0uAQt6h6jT>V8d}=>I@{_*d0GCJwpLbdUA@2ly{oiZfzy$s zN>W3&wzfLIFgr2a-NoUxjYmiChmSw^bar?3R#!+%D@y9bqC9byAT-GLt%vn%XYV#} z8h3nX?U6NzD(Wi=ii(P2VpCG${oK91ZC*RQ^=(tY0{{5AyG1N)F3S-XXQ#wQCdD|~ z`FYz|IwJ(YGXe8Vz{*ARf6V`u6hTFQ0)f&HBU=g!Od~OY*9*_R$z$?w?gcCmC_Mnx zW`ne`QhU;icS^Z(0UH(2W1PvkSN8Uj zx|U2ItKbrGMP+qOJ!T)^DuH|3Dizq8Ugw#Bo#|Fm#Tm~8%m72b9U*uo;QAb%3HXUj zm}I_Z%cdpsRvx?W5*nYHm6H-|XLRGp>IGBBPF$pKBo{|Jo!hzlz_#^TE4S+GJ#p^h zVCs0y)%y2a+tO{XZr!4ztG#c}wjKM9Y9BthbMvz0i)KxoGlZJ?0b|iEfXmu)+B%jn5-xSOzG z)`Z{F-nLb1-m*pWX3d&4ea8G%n{_YVHhOCM22!B6H@DQLINUg}Y1N|HGv~}-wrQW< zB?BW93tJbS37EDN@}sb^!Q?U-F2Knl?LfJAZ;yo$$hi?}p#HM9*2V^sV*>|ItE{WJ(&N+_SL(~j$;(e- z#0WaO`to8EB5Z==BLoaKp2{HzG#PT92^e1V51+cE!h{f4ix-C1ZoUYJh)qsO&lU)B zaC^F#m;L9@vSLAIn3JXPojc~fVKIp*sp(nS*>bs}vxi*lpT2xli1X8Y?4CckYwi^i zlR$O)8Ia?fBOiMo(2zQ-Gb6oFt8D2N28xB$)U+%C$vfeN@9FDBqJLMDDBj23_HAfP z0(ej}f%Fc!g1q#&2L9_(v^AFHgKvvK;PVR#ibTloR^SsT6?*Xa04fM6stDn-hNlD| z9-Rc%LJb3erx`}3eFK6*BVv=%Xg44+6i_pY^z!^LpYZT^ zkpK`%O3Td7L3$uH$We|+8t~JJ%Zm#M1|&bfpb&c={hxL@lEeK(IVZIehpys1eXEAnwj%CLWUPH)Hlm%brVh8Tx+9`Y7p zLRh%7gNd!ZFxu?;1Ko}0kBm~Qh;vZYzJ=;L1H%8a>AKbd6r{nR?&h%+kZf;&dVOvXOTDXV9;}41^H;YT^ z6rHWb;g*H<<}YeG6y!a?V#sxtNvdBvRoCIj=&E;U)#I)uqxE~$${OQc5GU&pB83g$}<561cy>khYk(W+N$E*4B&$$#zulFoaG-z z;F!W-^q_$H{;G-+RQ_kBrzR)HgDD&dM&Jr>W>F#9+rKh_<^b!T5Dr`Cloj>FB9? zrKJ%nHG5jy)k*6E=0A0AQ^>{Q=Qa{)a?qnWV^+<$-J`&1kb2kE(ubB+)`$Yq@9Efu z>(8D#Sas_11DC}}21OaOL?V~9OU<{gvwmtgf5;dM!`Wk0RVHqmJ;J8A3f>jw*Iv5vWqNuRh(_gd;pKT_2ag@Ma{v6FR*apsRCU0> zG3Rbi96m1~GBPeDy}jOd_vk@$N2!`_P#rv2oo50b&NBh?Ou$Hlgn3PsNaeJ^eg508 z@DaB))Rk3MCIx$krwTy|VdRn7dUZdU(>bsF$10lq8l9Evb z4RN@d>gX3|bT>^@QJK$IbE3=&W#isA}A`J37BeuK+DH70fQ-$GBL#B8hLkjN2|1|Ff%?Tt)z<8 z-~;Hiw2b5OZ9s}iZ{?&6lil zG}z@l6YvJ7N?@($1C0cCB|9@Q%*)>7{?%g#Hmq8+97Mg#RvpR!;S4c>imQsV@`RD@ zHl_yWboOomRWA|wu3B$joR^bLfGAZ3IijLiADhQFPaoX7W!18!kb{Z$Mpjx%GBN#% z^YTSy$)4ss6Y!yJtHFc3Xz_|Q>o(|IxN_^B5h!cP8C2x-;J*HuBL}vvUA27KQmyqH zHts%g?($8;hd{&wpj}C+(8ch|siXV0tT8W zvwDYiZr-qQ+OSchj#7SvFGr~^B486-UQ5u@sqD& z!0zW~MtWMAnOWIcnm&7k_T=ev6GqcZ-3#;ca|yXWIWZ>G&%@c?*4oO-%G!qU_~Hr$ z=#R%CGc7qWE;cHhN~k<|Cg7rCEH|`)i)-^uHce6)I%M$RL8F!!q}EkcfzBA;8;EvE zT$ixt|6}hh!>h`&cG1&CK|vuAv>>>I2$DcZAh;zENU%VNySux)yJyE;c6`T$ zKtUB%-BsOv&Uel`=Gr^Z{q=qBbN}4)XU%SsRK{F$?d-kAm}3rkU%8pnWq@Ko_Pf!e z$1XFh2H#V0QL&gwyguge%B6BMrN)mLJr-=c-;I;KQ(i@K0bN{Go1?gW%R0H4vXG4# zJsLE+W2A0@h`hMCklnuC>av>rX;jTm0>}+V)cf7!w^bx(`d#=~Q{&3<-3z8p95Z?p zuIHJ6!GtWeSPcq9aFQjd@>@5qTO=npW8A1w-;Da^FCaS}H(q+5>h*i-cn^z<%j~wT zU%zU~dGzRU6DP@R zKCN&`?KVC_0Teh*VevA#nNx@c;rs8$jUPWrX3>EYXO*tqBJBnuz930$)%+i(Nl(O> z0HT6v3wIwmO*$?pB;DkhfQgWj4Wh{;;zh)Q5f|?497JigSi~~{uUWTZ(frj9llzBp z7@bm2VU1I(T^XS1ejU`$G8c5WV({|^qnD|~x-?S`$( z7c2mN!O~qSx^`Y6F-d7zSy`OCzrQ2s>Y6X<44jtM=WYo)>Fa(5y70S#flKYWV)Yn7nC4=c3=`0ltN#vvt zP$|+Ts2i2ih*!gZQrHzb1$|;9Ks1B){E->xejqunCw(Qug25_v=mpWwoE$F^cVO$X z8CuTucJQMI>?$c9?maUot^Du3y-0H*E9=$TMa&UF`^riQ=zptmS-RI(t zWlI+>Sh4-ojfYQOyfv}1cX0>fH{^Ia@S$S=4{~+%4i5(ipo_bwkAF~TIEc>JbAd+; zt)X4rqIRAMn6fCsMoRx=gY0Gj70|kR|nh&`6jLSPEJLuM=Nd zBus#)4s5Q;PYU&Na*3$!CEcVy2F*s|+oH;_r6eiX$x!=_nr28_7xf7tl(c$bmc5_P>B{xH;35aVuZ{Pf28GpFP)t6Da(3Pzp@nBKmw z7C}~mm+kB8N@q_U-T%|UBgfC*ws7(E3kr>lruVn2votl*)8d)h#k2Cq5B|9S@bQc4 zmS7182o8(lYB-xx{9P@d-@JU`+=+wx_8*m3d1-Fz3VCP*J#JW`%*WQ`#ck#D7v&M5 zJE5py1cXuFz~Hb5JZF%LI5EEF4cV-2%^26Z(b)nGpd1PY(*FHp7U>sOT8Hekz{gnSkko;s)L< zazj{8<%oS?!msq7CQva384mUl(g#7TkX=2T{*xbrGUE}u($Usfni1{q;v8Dev9{xK z9B?H1-_zSwUzi-?@aCSHx_=AO%7m*3H%P4i@7@h`))%CNJH5HBd{xaxC}OvVoSr{X z_s^f+4~VLfJ9K(=1NyJ(LiB7{pY;fdM8lsy|2ouKk`&=@tEGBg{`}?J$s+QyaKc?i^$J)-|S6R`7zJ!_WEm7Vzg_fk9) z@amJ-?>&C0XJ~1OGaf4bI=l~W+qhhAnvAT}1gV*GSM9%YTV4CLzLBLRd|Xgzwzb;s zS-*VV%xTjnPoA@6-C-3X5qP6-Vr5MbLaapa4i1iv4vx+)uGH|(3YQw2gmtxGwkF3k?nS z_XpP~yBG~RC~N{tFF^}{NHir0Amj0|;2DjiU6NHjq3#JyJgn}WX97kY8P5b<0J8V& ztj?}p0BQd9AOHNHfBP`lS(C>z0rO12JQHve`PS4#AZGgInSe_h^PTT0p4zc;{`9Fb zvNF?Gy!BxxSauL=3wKEi_BA_y}J6+{^*@kPUD*c+V(57joV z-m-S4w9LebQd8$_)dkZYHaJ#6-=3v)M@?bx@+C{>NKKdo7i9X}{W^x`HV)1%*gRWW zn;Wg}swwQ=uynTU1Sz;EGBX#SfBqU199^j7zNM)p_ld%h&5IY$m64i+F=N4&+fQE^ zm|5955fTgzS}l>6ckNiaWY)B)GSV_Lm#)94q4gSk-ZpmBt4aX*je(~(ZCSHy*|HTY z*YDhOmV$&l6EOJ3iR6l<0-zDZ^v4+Pim9-PX9E7h6x`MBq47ZV>fzlxSIfT<#T1FCrz9*Y1$bWI-4cAYqO%pd^It7*#pwj6o#GR+nYf?&;*=FKmd@N1vv-^0l(eR z(KRsK-`mNr6mxE&gr+5lYe@$=6Eoh`NHJQFa_1e~3Y>g|NMn3(A3 z`g%}kGAD)*2=QE2g5MHMK^bYu2m{1I&P|}4z7UnI)gThW{*CSyKz$@7CDzu}*EgUo ziXbaEXi_E@6oN-6BPBVh21pgPn1OUNa{%yMQthfh06>wf%(PVW+j0Y}8$1&*6-I#e zm1hE$KdI{K?aMO(+mZV4Ou!70lS~W+A$cZXo(cH-Z)d(T#sS^2rm^Vyv`y*;cQ-Gd zJZ-|*Z_yv|?Pyf!k6Es*V_)PyqZ3 zL17;HiINiI@u(759ivOeOCW-3+ynpU^qHBKJccX5p*Rr!(V&nH!5u^ADaB3sdJNX- z&Hcn?AnAbwJkJEoGXW0`{MX-qeLnyyUU;Gvf~=IdP)}zETMJ7oD@%Kx2^gqKeJE1| zauOAe5{N+q5`9omP<`g)WM`qM4LsGoeT0yNqPAA}@SoBwHtp@4;S``s4xTPArcoE zTs?F6$nirz?%TI({hBqaS8Y6Fk<%mujcHXSSZvZ?T~$7N^7yHfrw;GiyJ_vJrHd9W zSi0w-dqxF}e0Oi*+sF5>UQm!fbz1(|9yGgxh({MWxq98! zqgU=|K7T_cmhItBFCRa!Z|~kcJ9h5gwq?uK&6{@}y?Fiplb5w~t>y5IrdngO`Ge zQtGP9i*nPG5V(tu0a7opdRc`q)c{c21CSY;@0hgucYH;aebsvHFr=`gXEne>)C^{pZY{A7jN&Dq0gd<%tB&!XlkK?W2%|$8yI@uk(=UU z>+IF@&;R-?$_$UnDJ}=#XHzRFMDGC41nlVMC*Yv!VNkt(+$2wzrQNWObkW+srdGiW#CHZ;7kBJ#iC;B(2Ng8aOkoL0^| zXAU^zbY>PdQlOt;s^H_JK9PN9qyR8gtS%iqL-5Zl=nznZ`b0R_+(5P%4m^4Ab=6f^ zg3bW&$1#ESEBN7X%>lzk9zFsNG@+!Jymf*(WL`T3xErXL5KLzP$H2=h5wFa=cg$h0 zZ-j>%I~e4w#bIOur6Q<(NL*yYuIFapfIuriy}1P_I5EQ1EqRSN8~p#K|2z}0k(KEE z`_?4Ads=R>nfXQK)kr6y?E-bFMV>EipHq8fE*k#S78T<2{QeUgpTx|9;)*(KW0e(& zPWpFGoxH4NBo87B5gDbazR zj?P|DLE)j^F8W4q9^Y0}RJy5U*wNY3P?MdUk)H4F;BRJY;pJrd+CfM6`c+kxYu6sU zHiaYBSydjI6Yghd<8QBLVs3fww#H-iD;HI7sNH*Ij2d0sp}jsg(DrqZ)eAkVw_3MU zA3iuMfBp6?bsZyfD;zjU0~-orBO^RsT3Z`F)4Z>&bWQ!<11()6b8DUnn9lL!osi+- z%7IWF2iw3i0rO12>+kVQz!b2;KWIr!PK1-e^JlN#8W@?Pe9@H}i9@K%6?+#bVNqrX zvX0V%tki_q7&c<#;^GK~o=s?Jp?$X=M}QIm@Vc_7ek(OK4faVqSnQ5+Xu$6T1SP_I z@FMc4uZ`x=-U$W8-ps;t2+pCt46%NWpO9km6nJBYPTF}{^bQ_`fcyr$JaU_vrqk9; z3SNy4vNA*fX=_F^EV>-1(}AiCxEs*k4F8~HEWJ9Kf`u^Pr@)%B{hV#*JQFazDLfPK z&|u&DzV`B14>P@|_wTCf2Z6{pH8VRaD+ljS4>A4nOu!7Nf&;%0`}w4Z2+ZRILE$OD zr2v|S3)EA4DhvvAo%D@q9i1}>%LB>>u3_4=8)=ATBi|rK?5mAM9R9nUG#8%;%#BZ5iLkxq_0w8aPh5DPeI-jnrv}#i#N?b2qhp460?nXlVi154`++Ik z4DkXy6EK+%qQ~ejbbEAU@6vg)GSZ6^+5v@)CSS_z;={+M4hv&pAF8r`$s$=PsVTRr zfc;4@#b7d^>mlyXQdBm(xOVO|nTb-;o5M45^767Xc_v_<3HU3$#g9yLxgZ0a5vpF2 z$jO0VQgR~bC_v5%YMUhbFZqt9MDC;j3q@Hz$?v~>hcCotFrr^h{uRpo5eeM+LNm5eMb6&c8 z0b&an6g(3!ZF>ywiJB=Waa|PV9pvxh2aq6ulqIKdjxUR#K~-1=y}r5(RX7OnWo2b& zlXFaKPy1LcZ&-BKLZ95O{Te8Q`&syh# zZbVcJ)JNcHtocCSTuZai*Y=gd$-Vpc?7kWmVrzLzBP2W$4`^MY-DC4)Z!6DiUpvFg z=Z+rOeO}qv&DQLpNnmg|^s6GqO+P)w-NHD+)7D5$dH2?x%9pRbHszUs(|IOfo(Z_4 zEZ*Na*TXc<*5KiV3u;PR_MAJj{jQeUJ+}ZLQALS5n^J?)TH+iWUaMa@s_mw)_M?Kr z<~^q`->`A_2?WZOsIx99I5R)Y@%4pWyUk6XtMA;tZ|hpc3pXsB-GQTpjjc7+zqBC8 z{`H=nx3%x6pI^Oc(<-?$&$VtpwD<7whkdAs_Xv(~H@dSSEXerUv2D8!@0-0NJjk49 z0(NwE^F(lw9E+BEL0(EkfUmEgx4R4QK^&>W+M8zr=9z$bCg9Y2OTO8oxp~4)lNI0n zj;e*84t)l^$wEQpLrEvQB8f?gZX1RNCn+}b-bKBo%6 zt}G^t4@fBbrKhQ@x2`nA(#+F8G%7PsP=)Fs1diE2oe9qb+yi*v-r<1;y}R`Ik5uX4 z4Wbx2;xQ-;6?HUK+&n{pGa@^NqXZ!yVnhNcCW{)@=5m8mrcBPl1au({nnvGSh1EIP zc@#|lQpCd!`J^T6sLnG1^Gv`dhZMry4D~JCeEt3Xd_29wlLcW3&c2p*mbXr7J3G4^ zR?MwLP^dCb_+ zCQt7j+y4>5guB7~1IW8rQ9avV0KENg`#-G@%*GiP>hSh)JEjet6l2_+YdgCuT9aMpSA z{Fc)BBgYP`oHuj9A^oI+-2B2KVge`MqbA++wZ_#8=P#c*duYejZ7UWn`N=#fB{e-W zC$A6{WUWQ^Dtix}JahW=Ii)LSkL}v9Y~HN7JQMKF`La9{@HBePokLn_oiC4QLNHSd|T)U?d50vhv9j;3kk8PDwbH0T80f7d1MB5=9j_+zh5(P%zwr zs(jJsSHWcv*c|3Nkb6y~1I*TggpOwd=9z$N8ib$z`0e*!J`DAB)K?4ABZB?CJY1dZ z?c5RoBU4>n*Vz2qUw`}U({O)JYh7i2azu!qm%EFDgS~S^R77}HO#_&I|N8lt55s+( zt@UNuNfAN5p6<>-vUTtd@aLI;tE%gS&FHS^=xC`aFG`OI4-WA2_jA$LH83>1El%V(7MFnDIujJJv0=dh@F=~DN=n64+1N6xD}h4*`fGImC&fjCg#-r% z5UvnB0UBjAxbGlOC9EMp1wu?GZS1x51rzBXnulou=2@v>eX_%rgQ1 zsAXbejt8c)D%sA)QvdP2Ys$y>ZCkf$#fnvH)~?^Ok@Jwtl%DvQ0WjrE>Bym{sL z-p%OXTe*7m+KtV%@UUlGb5 zs?kJ+BIyx1&jfs8^^8fQzZ*4b)VKIIYV?@zHw6Pn28W=^;)fbG?$5T(pFH;4(csY~ zF~EApEw=IX_AM?ctISb3u6X0%_Bk@+N&FW!Wz3lG<+SZ>?TSmwD{>X~tX{ie&g4mB zz6A>}QSpu%HRikVvu?lDGr;~+R;;{!_3{OBQ^${Ia+>q)n6Z;)s6KoC3TLpA3X|0v zx2%~VBPB)a;|`GXOu!Ews;fVGl3$3C&se}&+dxV}TvUL&y@iQ^-rKjjx^MGQ2380r zVssT`5fONDA`$=*f!?kTcDB~m)>cF?P7`t&qzHC);t+_7i3;(CbKvUYjQyUikdKDM z?ChMZbWBKs1&EIbgoW_({ALjSk3_ae70eh!$cKt$I`bfoiNnv}AkpkGJ~c7d9x(%o zXJ8uSJQMK1yMg#y$4~9ynSkfdn?GmvoH?^r7>C5ABfuhHOuRf3a6ggNQhI?BVO$)I zU;}Dvm{QRf$iY@f2?Xltp@Tj~Lj&>LvLTkE<&jokaw_8|o?IHlrHSjol?T-&20RKB zpjfwuKr+}MK2OM@)0B&&PF$V|m}dgU<2BH!ukrBArmZ{^FvwXExugDkfdIQ|VWFUq zNTZ2-8atju@2E$g>#&1o&4{TPi7lsB&`b;j@X%uiFSnFLY*70T)Q_Tkhz1}HPEcHx z6%sMm|3jKdL3EDEm%9D_WqmRp;uXj~P#Hh%#WYx$l5W7_^gI(V&jg&GmzSHH2Yid6 zc5CCe7kBU6zI4H?Dd<6xl982_UFaPjpO~1O3i)7{;giP-8#b?-H+zQcWLX&**~yb- zmpk|bhJ;7QV(%GvXRdU0`@uDU9-2IL@)S&&JVk1+wWDW1NO%-Ee?yHLDu;HgUNB2; zCNYJ}PMIt-`<8)?t8YkHIPF~n@AK8p9$2%ymIE~(c{ODow%Zl2AP!9H0lvRk5X%G zd5*V{minzrrw<=K3N{TD-N@L4q@<)|l8c&Z%5tL}UO&94qHuiwfg{I`pS`9R7=jsz zi6rl8EGbA0vVL_(_2QYMAo4wY?3|i0huZ%t^E|wgLTiwByrx6QuAEM$BlsT* z(gO|{F}}ilau~=cu<8isR0NVu#Y7}@TV0rm7@(S0z5CEkRDZMd6fBXE` zzl(}vd|a$w-Z&39!ISb=Oi8^lNWJ<$efa#bIVarJ#_Z{}b0<%oK5^>Os|W^j#q_iH z{rlmb#`Hi(E5paCXHJ|vaRT^1eu3b#4x=7{uI_h(oo)5W-ZnGnbzL zMGHihK}_D$+1gMXCg$?dlda)t9G7xV+Z9tEPDJ=#ir*&R=_JhzU;aUUXpPnSiNsfSoEBwyz)` zk-YS@^pwQJB&I}M?Jns*QiO#4!AQPuOhSJkollXJlq!ZDU99m`J3-d2X!Eh)c_e4{)|LGb4^+2PYSxePgdj5&8_W_jY1P!;B!r7<&%r$W={jp_qYjClV@*ui26Na6H`D+H8-_p zJ-T-B@Rs>Ar%R6?H+JjN)m>@fI;n7?7 zF~JaWC|^@suEyzo8y3u(4w_`Zoc*w1-37I~8qZ$q8A1D+8->kn5tnywSw4T}v?($& zlV`8ka6viIn$KV9yx~}0ag(Mr zo(Y&M#v}A^BT;WI8e=Vm7B!ZYICBzEMj!ER=S3%$EfuDrIZXv!NJr`s{Q3j2N1$3vZ7?F!~ zu=SakY%JiB$X3$90j0_m2@&A_oR}@aRv=r5Dn!U(CM9wz(&m|fi}F+BLp+=uY%I;K zLxF}Lg$=6tegDsYe11RB+ge{C$W4j%b8)n{wKg&L2?z)Z4o0_FU-$6eei`cSYN;+M zN{cJ z0yZ)+v#_$^nSiNziDUhvy%on8o(Z^xX9DJ#fNgAT?HnAPYS5R5 zmK6+C4&v0Emyr}3fi^)uUmqVIZ*T7!2FA%6VBm35M-I;f%(8#LB8OoS3R&iX<^7Rg z!2iD_6p@X zXKQRADMHAD?}i5ZMXj|Zc?A_s0Al}24xYfFpFh4E?C-+GJKY#x?*w>9jN^4z(pfEof#5^AE&Mv<3C8b4ugMa+<&)+@__V*$g z-cnnE2Pio_$Om1#jt=$#x%vGc|Mri+|MGr#up4C}HPt18g7owVUoU6$kJ?*XM`R3s z{QVz){`z64SJ=>m$54=+m6jCY=kDa-U}t4+<)1L{>p%bT$LHaJu7cvaiiW!4{H)}- zP=8l@Jl~dPR({cgAOH9N`sZi-@zkAFS1l;cNemD2bimrymKHWX!Gk;#@BrFi1_%0x z%@Pn7U;-(o_#C4NOV8k$fJt9TDZgko<^EBrh_Fp#Bk8m_0r|E5qZo-%o-zGL707?k ze^gr|^hwu33N;pgq4k)0vt%XKX9l3Vr%Tk@P*dB~(cR5yHvyhQ{~<*gc8{o`C^HGq zy{)x-Vl|P3qC$b>^(cVBwT%L>zk~<4n;E@)@lwYrqnS}PAl6Vz#zj<9k{ur%9pdX| zZ)^DGx#sFwX?cGXe8Vz&sPMr>}?gvpY%(2M(UtzkAo_4eM7dTC`vRng!>t zxbWbas5{5a>eWM4#WTl_9oV^j`-Zh^mM>kraN)v5OP23feefJKL+J*ZcQ2nib?D%a zJN9ndxN7CfWlNSVTefoTkIFm~aKV>dl~_Jl9WY{Gl3ZU|DFJYDg9`e{XQeP&J-z@n z1d;OZxcZ%uDGcTdlMwFb;{B)*#NI7l1#93Q6pSCq2SA36TQY+I8Nn@=$p7y$fwpGk zRC3OR;+V)xgl&zQJ1t_FgE4ackd!eIWSVF&T*1_b z$>BwCcc6EZc`H;}IT-fF(X$;T@*`sAoIrv`&yJ41gqMJ65H(bb;FF9vLGWIfzr!;D zdsrCRxp{SVgJ$gg&;4yxd97vHdBvGY(cuYEwpQLA7N+*Do?fUBraq>@emtY~*`>vK zz}RuJcMo*7uy%4q5i;KEo&g$N9WC{hC7A(^j&6P-K2AInFaz`8nSh~_P&oQZ0|o-E z2hC*Vnk!Gsy$KcD2k`_F!^V-NZEERwxk20Z-}Rp+uqn-LLiK9~mjAB*tV@%%H+1_R zGqtknY^dmDkqLGq_6leL9Dv$1jyPFc2}5imi<-J9T+%YqtwCiA%`v*FhSt`#4Q=V3 zW&tJO4y&mH8F&jpTmtG>m}RMV_W{oYOm8Jw+}4_cga8-ox3@1VU%RiRt@T3B#LC{q z(-&_l-Y;sNZD}meP6%>!0d|IylM5Oc0)j)sBT@f=1z=nlz-0p>FuB0k0?-Z{2?_Lm zb6Ed$V8p&kG@n#{g9re4I?~ZfBObJMQmGEo&lD^~qz7$0x!}^nVCC?%DTybbkcaC; zF2Dzh$Bi3!=x`)uNiLeeGF&8KL3#*T0*fW;5tU?F_>y80QUL@NN?^;Q8&HxASvCgL zRe~pp{yMxxw1?p*+TZ{C{!jZF;66nn`YZqQ{!bepVpuFJ_aFCvY8MbY2wxls$zv}$ zwu?Os$txIP66bUiT7er-U=I@->JgD5{{(~SC)?d=0{y+1(w>-E!NlkvV6#|gfZC~= z*3w1-6?z37xB(}J6lf{^g9FCeP)R5o zdxf2_b|i;}0tTt0ueH+U@)bu~o0C(NpTs;9FwX=GECU*SFz49hXd}ihih*Z^J$Vd@ zhp2w>z%U|TOrf0?4-*Sb^Gv{`X?SnM4Z_T^`A_-}pcEP~9siH|Pcz7R{G0wG3(%Iq zpp-Cyj1zgy++Ar4rdWNqjjkc-KPM+wfaE0Y>}u+4uJpWgQoj=n5S*M`i4k!>=&~|g zZyov3q^*d-4w2M@o1z~E5GnTrJvy{)BH@Oa%+ zX_-mV3OW`pKK}mT35+E<=^WwfAfuG@bjO?-JD-_3j7zd9zmAD$3#I<>loAL60^+ZvVp#FyP;!Bn@?{@X%?{NsT7Z zGs_0^KgrMMs%6>ffc8kq4Cdj&jfWpj$i0%wu!`h*a$wm(0TV`;d?oYfxh9WZGC}hD zk&Bra#s?F=zMft?T}xtekGTbSCSdmXQT|ZWAS#L1T(a~*M|TG~gj9Tv|IyP);*Q=h zT}>U;J*yXMwzN05p^=fAC7UQiNf%?5=T8TZFPJ1HJ5944XLaIbs>O%Q=|9f|EE3hd zT)%9|3|ZMpQZn1sUl>~2Il6dy`q29i{h`quesb}=X_IB7W%t~FW?*GcJbJ$L&O!s> zX12Aq7in#ln>Jlm_Q-=5rnU}_E*^d%Bxi>z=8ra|Jm0ry_Vx#_Oswr4U44QgU>`;% zkmm7Bz;t$|qE`yx12}vf_tuhifGMMQn%OdDEMO@b8%R#3kK!QoUeaI(Ve)dI1WZnI z82%u$wrom!5lc7-N91%hK4tb{L#G-va8AVn>{7a#B=l*qXF)WW^1A0MIf>ZqMXjW5 zz3o{SQo5-KhnPSya2=^(S9fuAQfE(}_bVOUF0NsjEe&Z~ZCPXQKzn9@O<60?1bl#J z0={i*MAdiH(hqPe@2if*emc;%?Yb zKmt%CVD0_s>1kbYrroRZAp$`!?X;p|&Vin*5 z=jG+*WRqjUIR?Z6^wprqAueHV2?=o2Co91Q`%f;;CGl&-jtIp&F$K<84qHI@FLL%; z*>5_kuM5c)5sI%P2~%TcBih^hqBXlXId={DY0Nsb!2F2%+ENb3buu|F!~bEJxF}z7 zWPC?=ul<9^&p5KRM)A5`tyLur-M#I_A*O|m25*3#NT&z-``E=DwKYa|H4Q20S&kRw zx4h`<#MO9lu#xOlH8+ z*Ph95)YE(rTUr6C1aLGU8ebo0prv@##lzgh!S12C(H-SuFFh>Y`etW?tVn>mz`Q7P z#Y+#ZLmZ7?Ts`~x$(>8bz2nTEKZ%TwPfE)Ywbx|?J6gTc%?`BIKD&RJm9buyAyJ+-*K{1-DjnOqW%D+bYnSC!p6VEy+d4zN(}P_sZ2asW zoxOZT@!;Wu2Y=dqT=n8Hr6;cp&Fx(9`8O1$y9EcmxutwdRZ;P>(z&zeE?zi&>iRQ1 zGaE<9+dA5^y#j-c9^Scq`_8=w4<7=(`@!{lIz|>Y_D&>kYj4aih&6d*Z}H~!TLVK3 zaDbRw*)k7?^XqvgU~fn#u>ZGK7S;6(R2y!c93<3# zRo~Z3_JRBx+!aUus;bu(wZcUI=S!z`OX8>9qrzm>S9hG%OzNwmLtwOFZnfsme z+>Ix1RyBdLr3$4MqK+=1!B0EQUp-zicB=8?B~zuPX6;`x!J@bf4Nd^b?+|rIo}4GM z^7!TX6BlgVw&v_=ga~$>UcdQmYH9{7fQV-T=9z$-aU`#=E<*KnY)n*WfVT(G2fTd! z`~!nR#2KR&5cAbkmY{DpJ(*_$=9z$-dTPs)LoD2QCSc|OlT*n2K-x|)`Vgxh`uK6U z&{CfqVDYha8QX>K=|N` zTr8y>T`ZW(x`m z1;xY!iq(6685Wflq(($1hX&ajyfxK+qOIeXo{^cAlUE?Xsb07yzU zy?5aj&jh@4+1P1xCdEL4k!^sqeIe{kOxQdVFanL)SqzK=m>w+L1*Sj=JO}s(0W_SG zO=vzOt|0zDHi#dE0eBD)X*N|NB2Y$5fW+^OZfc}X0aR2BgbzVM9^@R;DMYoxmd5(Z zqQatznkrfWWGzh2En6q*@9$|BRu`tnMx~TgV+D$qW|fvPqSVT!4^QZUk`Z|SGg;|NAex4q&zz8ZX$j;`jZv5@HUqAgkJkZfl zRhW?w44PKAD98(Qv%z7A@$Qe`e)$9{-uA|d9I&+dxVt*L#Fdg9e_2&^)9-)%?U$d2 zhkD!VO4GulgMB<*ot!=Li}C)@UtZVy_uqf}^nPfdySb_;D={M2*VE0}#VNJ`P_7Wy zHoX7yufKi#fS1<I4-VK77y-#E1HOfdbFX#o0vH zz{uDPdDw;~90B_Kx?3Bo(5x8>9%OfSS2t^&*ZPJ=rU>FTG__(vS7%E-KHOM3fO~j& zI=#_(OV@*yl4k;D9KiI2X97NZ?8iNuH*VOp^9^z>FmX)(YYI~10{qQ&pWM29djIyV zo7S(}u#sm1=9z%Cp1njI7|0(OMA?}h?rNs5Z)Rnxr}G@+<*V1ajHZ{#85?LuYGQm; zkhhDywWT@GNX;!6k1v)e!1aXdnUWYE6CDxa@8#y=;_U3?M3>S%3JWOj2QpEf33$>v z_2h=?>Iy>nD+cQcZO{#IN9ATtm-&9ocVoXBJ$mdi!|K|~(qd4dkg%$%KIZVsrE)W+ z#*Z02cI@cU-;I;KQ(i@K0Tb8eC~n`fPHv_wWMf8;2953*savJ!!6+_d`d@E#Sxx@* z%DK}fjUDstDE#l+(ceveTSaoV{}(>i)VOkd_kyVt$BZ6@>oMWG?^h+2f~f`a%Ia_p zQ~L*pwk?+)HwM>#11jHd$BdVKU6hrXPA0e7S=-s;slwLzGPwWfZ@3$bop35OF)p^8 zTq_G>_h4O~2^c7%BtceaGa_E34hQ3%0-l3^4BG#k@YYZsF{?^A@k$eeBG+3zt-`-@0?3=mb$p zNKC%j*$H08TI!E=^mU&-dT^g-0%pm*K`;&XGrCyr3!@e8>p{UT)%tX_Lq0Gx^zRdT zd;39NMP&hy47~${&)^UZHjk)@2L{ntL;bY)%{%dU|MQ2R-?J$Q5D$Qt6=Ga3mVWry zRb5+F-}m7IreFm$6CktLF@)8z{LuSa=Nm`&@BH!fqpabNLy*(@oE+16CSZkwTURY! zx`<~2WQ>S|&_WCL8il-{Oinv0_&+QKsdcl7px>y)D^7V{MYlm`X-C=6iw z4>`Jb{(SylGq!tx{su z;hBB=w{KiGXZAEOc}|`-P3CkFA}@%NKt9-NsrTyq-aR{)FP=SZ3b;VAz?5Yn=@}pb z%%$sjCSZKp^tkX$z^vdMB{`H+Wz`4JJuo3N5+K^IDISWFcy0zoMM*ayCzuR&4Q+~S z5n2FwKazv282Z4jq7`5S*#himTA$N@s1zpAWhko|(SN!gDnQ!Zi>cfMl;vOr$@)+* z$VpLP2WSbb(aAFb^Gv|uk^%~bNC2SLma%cr!66@)_Rv4vfh-8a3c%)%CLs=o zoi@7K!*sx&kclAYouEV^ zCX)l)i{_gAq);y>mxyXK0(P_$Bmq7zu^8KNOG#3&lcDwRsa^x+M_-sg>4V^zfZyIxids?sF8iNJN(Z!9bg|I-Us5HpV)KLG~O_i(nUg;YcnOa!eIXJtz z(Q``0ZyoJT*w0I{q5^$=yuFY@#PIU*^`qWInu3~gVFT%ZetKdI;|Yrl4G9Yi=W6c& zw}Hb4wHAX3JeM-NFcAr81d5}M8q_RN?=EqP@l3!x6R^~jsnb^*mIyEk3$QcsOu(Jl z*AMUB#4`Z{x)U)+Ab^z?u~tEz3AnO?4hlvpND$es$yM@o@zfguS8b{ z(u3u2?|?ou^tZqL@%fkGp{{yCsP&ttj~+e@YzAQ&J_+nC-M#(4LJlzh!OpsLX9Mjg zcU75(|ZGt&I%Gw5AICr$pY_onvrr_trJ*4qdk^Z6k;_T#rIwvAj#17?xGXd9RC1&O(2RfP==;`Va53+@owGF|| zHX!ORYy!qcZCME_%tQTABilNui#5f&N}926Mf=ZiEy zTLGLk^?2qh2*x9u0ewV8fG9ka$r(m(Bl(j6AS=Qe;0jMpM6+*H1S$y{fl7;40DluI zCoYdK%*{fvUt+wNDV(*{!SiRxV;sjOW@MzLBtaqRS_~}DGXe8Vz*$MrzOD|oHkR(m ziHQk`i9kH=?Ed-B-#)$@>Zq^dnSe9m!=T^pZf z&itjuUFD1C&Ye4@9)%25R21F6sJg4Oxil}#!%1KF@%^icJQJ`UV*ET4Fly+F2p0fn zDE5UM37!c!zm;bK9?LTU^Gv{8*in449|;2BL=R_(QknqL!^wrp8`v3N5+$HG0H*Lv zz&sPMwXLH&7zY0QU7Zf@!{8xLtV{v)uqJ+IjISep+R1*PEL+k!@k^lGd@dwQq^^o-8{*{=KhtqjnETFi2eFFl6Lc+q) zO(Jd^=>-T0z~9P?^Dzg9r}+3dP<{5G0+nY1W(Yu}_!x*3qR9bS2?oPQM<+sf5!(r| z_VY}@8rD$-$kY_0FqN9mljEa8Lqq+Ytn~HXXx_P|tbFClRdu_Z!u;-T#Aa&>Qj+3= z!z2BjZ1nYY9^X>Ca#>07@}*0!k~6zI>pMl2xjE@kIFULznd`lJq^WjU`Qn8O7jRCn zi|=l!?rpCu%ue+7@Nsl%I_1^ z6{bb`IlH)d*qOc7(zu1YE6AVbnSgmFV4ew>X9DJ#fUyA*^&1Dnjj|6GzDENBMiOPd zFff5){ggXHm>*Ey$j)WwWMySx!=x<~OCUklhI$^oa&Cs>@Z7H3k#RtcmM0p?uHIgRZ(_YK}}(8Q(Mm2@XK?uar-9!7zQLLbVQEE4 zLse03RYq12!C06Z*?V;M4-WMVw-0nQ6;(7=6rip#Dmp1C*4x>GX9A`O9AFHY0boXe zx=wMZW;u8y8&EI|IZcrG)gTjvkw9-L(t$Lgq?qc17!)GskV6K#qz0CPr)(n61Wcv_ zCEntJcajJK$b|4rz$Y(j8HqZFyA!>lCDk|O70_|E)Lrr#aMtZ)xgTI-rg_o1*YX=?O z>sM7(u3dZZ+O%EN)mc>@niKA4Xyb3MXJT%7@3zKc^(z-uZ>ZgSWo(HG{I2fy`rJU< z*Fjb<^sL@$-BNw{;H>=h+qcwpjLfaL5`9raL2P7%$4hH#!)KcJm6fik-+Q2?Yh-S1 zPbX690^pf|sg8iW5S|Gb2@TfF$uj}pd)bkfU~u%uz3Y~5zVO5$C^k7GJ1M|Q``)>2 zz%-h*=DN11D$-8%(2WhK?aM^EhjY4^b&x3Al^cJ=I8 za`TsLS5$w2-K!=1$qAL?hqvuHv2W9stvi-2SU7*~^y%|fZaa5hTL<)C?QMB)Fa5NA z{ejIZR&QLjboR`7b7#z6wqgI}2ih-R!{0;TsV+qG^!}f=F5j?b`QpWk7c5+{WuJoD zL+w|3CXfOty0xu7$>!e4y<65SS+sP;hP}rx-cZ-pHMVr%nSjZ4Am@VH0MYG4n<4&N z9LMLGfZ-nSOu#%7FoC&X@Jzt)s90YjjumvYU`LM;`$7IQ4er=MU%yoNau$Xe7B&G= zo(Z@Lz9}J|boW}=hpMb!vPf1+YRauD;3*SKF_;X<{6O5FrKoInaqZk`G83hwH-~5B z0GAF#2Ki_LBzcd>)WrDKwmGsBzMn90(mdm^h}eXr#H5t8bSCfYt$JY-^mw(j)cA4V zkDn-`?Bj=gLUc@Qe4?nG?%z}EYNK{~f%N$C5^vxM%f#yD-s{PzxqZNg#xAwEJe)ZO=9Mo^x|owa0kJ$H??2|$%Py?QEOD6cmm1W zQMkm(nN$Smu=^?x+KF|Hnh}~%|1Mzy2ZRgH1iWaf42ZZVNlR~kZEEl0?(L5xIO#u- zojRI>Pi|d2W3tqw36rFx*FM!Xw|92;23a@ctc|j(@zthf3ujG{!jN9`=#`O`gR{G* zZvftZt^$N-0>)c`2ak}35b38F91Z+NGPpP%J^3j5{10+Aua!>m4MGI*YXALuY`qk? zqC-Bu5|z$Z>m#g1hKKSDJQFa_1k5u5pXHf=0d&SQ0kbB-PM!%k%<=VwUAxUqpR4cO zzHjSV#S1qqu)_xgW7ld;^)D?5vVXm2=WXpf>gQK)+O$gU%yX^V5A8jC{IS1R#Crrs zxEtNs5Ef*7?bxEjz9#tG;;q{Dq07J(zymcqU*5Jq(_`Y9s}V^0Gk&fC2+TIZRF_8a^t@Lkkii z1faXOv?xC(3+*Er85!y6X*fM{(3{*zl)`^B09wRR{xR2?eu;lvc#x)aw6=DTT-b=; z0g;mpz^UOdNz(wJ+nmif1BG-rq(KW%CI-$o9toZam}df>IBtcJsbzadE6)VXGXZz? z4-fXWRo0XgmDf{^10Z;6n6;{42CV<%$4|puO||vKg2fwJH< zzPB{j*S6+G6b75fufO~n0VN2@3Ud)``rZojoEcQ$t8(JZ-DXx`MqVAoRWXOn6Sg+Q zN5^QLRk(sH>6erUYR3%mB5h5bEm>L4_s(D7t{^3*_!uXqMR+FQTA<*R6rqpqUj_^C zHPo{@9;yN4^qZ#OgMvejfxhpNGM^ImD?%9&3lVxGDPVMXCg9ARJe~=dX9C8lm24L` z+9ca#O?4d41e}wdMSx+*L=&eBeJv@*4?qzk5L%uIm}dfZtARt=^6TecJ`DADwl>$~ zB}WH?YS-Dx!NI}W-o?ckJjKG6cfWoF&2o2JQ)PZi6nKo?(4_0=Xl-R{?^q3aTh~X> zB!jBAzAQH}JjmO_4HTD7j^<|OmewS174?63Kh)FNR9lu67aHj8jwWAcCp$wU6H|-o z`o_j)o(ULy(Ks@rJE#Qo)|nY8NwHzU0sekI-bE#)J^?;7GwJmts zt1H7DZA@Q3y{n>pde7GNt5>dEy=L9IjXQWIV4exMnBYEHEU$_z2^*F8sR9U}fB_Sf zRDwzu76@Nz>VPp+TU#$|0Q)-SGDu!bayDv(rM?dpPMuKH_A5g9Lp4rEfK(ij*XQf* zm6IAb_Pg)K@Jzs?MvWRh=KD>-k>Oz_C8d?c4>fGupKY5zdF;2NM}6}Rq@zG)JZ`a# zueWb;Nm*r%%5lXT2e;3W8BgNBuqk84d@rYMZ);avT3(T>uxIt!4Ra<>8uRTp7@YjO z@w0Bf)iWq5D=#ZnUcY+z0=cQ<$1^$2`F70MNi$TRJ%3e#outBK^~No0X2?iM(fYUp zCLfMq&BCfb4kOc|fw{89U#S7=m zlo`WrkITnToPA31%FVkVbH+whaAV#2g>pO-Fn0Eoq=fjmn5YnfGIVuu#(rN=h>@QU zvSPFqr(;4AXhz~A0{wk`yga`Ff=NG^%s3*=KBDa-&OXFZk@cgL-5^+Ti7ibsFoRgn zxEYuRIV10docn>+V0?U(NhGFS5)TY8j$ZaNmII?7cLz}ilXF7$l`Y0I0S9(-ZQ+f? zL;Nn?_`&5<2Yx)bcIk$7bLY&RJ$u%gq*jXJ*47YFai6d56U8IPf7-Qu*^>EkGiS-o zn>+I&Fk#8LgM3(IXZZTkp+oyOtX?u#PHyH8a&oh0uZ!nSbR_RJ*Lr+m*Pb0KS1gnR zSLv+Tv**m-8ke1$UnnR-L-4x~_BZbCKfP_`nzeK1&zU1PTWSC`cqU+GF1lf0*dPX7W;{5)PM8?t5#^bHc_!dzXO^s3 zGjHb1nbT*?-EjK;8*@ib|B$eVD0+VIi1fAlD(zmeeDR`HI~C9xXlUi=K}`rzv5?av z)Ya9~lpWw~?-3Re;^*z{7Z@559hZ=tl1h&qN7G4#E6t77CHYyXd`Zv9%*w)sm;)iv zeDX}dv|STI1S{C#c4vx-(q_#w0S^qmud=$hWAmzwKix{{9|SWpD~$&-3)dU=t}k49 z_fH$H01v$a`{S0iYi7@yA|ov=Gj*!ev0R=Bm}dfRs3+4X9?YdDoo)r35JM#b z#Plfo1qS*07|7#tKj85N>S4RM$&V7aunsIyLPmlO`p++Nl2I=p_nl2aB_Q>1O9pvI zY~UvBcDRG&dX^#^&TjlIeAL{^tsysTn|Vk z-mR|QkAojRgQ3jq?X#N~P98sb`h>iybrD8!aS^WX>Kf=8>Mshhf2;lY>e=JRPM$i> zGXe8Vz=)lI3y8`tNJGW78Im8U9t8g%(N8AkG+9!nLDDaBl1cPmyvWybPB^0f>~{Ym zXU2iED@_0CE(Bae%d&$N83djQm}de$b@(vP1dK=?4lya93ua10jm5+ijF1D7cOi66 zBwyKC8L25uPVM&C@d?GBLhA^=)06@P1+%a#e^mg%X*l~z3W$qI`@Z_^S3fc_Q02ae zzv#bs1-f1`Ncctn>9^eaVmS%v@BuAn`cJ>cesP37BUBp0;rJjR%@9 z^$g9eP%PNm#xns^#}1YC@=U;)DapAF)C`R_YB2rsOu(Heh#&mx-~ai~zkTd$t;&w_ zFwnkz?b1aR*QltN*tq!4E{ZYy_QyZ|_S?^*#){l9JDt0#%1W2ioI^rF!^6TwJtY71 z+viX3`y0vyNj@f8YL_r>JNX6%2Zw}+7!dyZUqAia*Ir$e5#jXak;+BIOG@hYE}s5D z!672>AomZw`}yPhzP75oWPjT?kFK0MuXyE!jf0D)Um(f*`i9;O5BG~23NvE8%(U*Q zoWG!S)4;;s*~8lpIORQkphA8()YDX*pXg_+t8xAEC6(*XjL`tdGXe8Vz~az>sIxoU zQ02(JEqj$;xR>otd7Bo&o9y;tKyMC0SL0&H<|KwDduTjR zy?S`}&ed`=rcRMN9M^!_r4e~&SM+l`!`n)ye%iWhhU_G%sd5MMYAXqhy@=VLu4-FL zKcgonj_g{wV4957M41_LbnB~#s93<_7o91FPT6jE5ANHve6FnYq=}OzO*yZ+q`@V+H5t>A6_pvZL-Y73DT3K zS2!fc$Hv9RFbBBBGd#abpmlQBidoa9N>7|9Ej4+uUT|P=NN6abzqGb^dF9llX&>6W zY!=T1eEr%bBVr~jepe8702a!Q5l*p-1k^v=Q z0;bd;`-TjgfXRsr7R_uOAS!QeAZk}ozJk>mDD05qwvA0K9UWZ*!~MOj4dq3d*%d-) zKUkkhPKZxUZ6ZuCsx%|Rf828a-39z2%`-A_WLix10BMeqSR!6 zSJ#kIbb#UVf;{wVHn)lTe*gXRhj+bgbu~q4ae*#Qj$XOUd}L>V@}Q-?^Yh<6fBG;8 zHmu6R)VKf_)Ysd`=D_@?rl1(5t)uVHzy9)JxVKqYRl+j?2fACCTUp!MJ2-*4$P*v7 zkOAofD!j3_tbk_%<}|CgSRf$%XTAgZ4_srR*gi{4flgL3fpnLYmuat9PD;ua zqTWN!z=+kGt|t}b^q&j}n@~|Eu^=Rt=s%V~kuW!bsXz3e$(e#P;~`mr88H|b0oMPb z|A-2Z?$aPYi1Z)Vi{(_eMV*q2X+q-2;KZm8+`fm2N!LenU9_OkQ7KlO612 zrvFm&;oZBpZ(Ucua`WNSmv4>GLCPK3LIrHPS|?u+M-AKbfl>-OCT8d^H~rj|CW?xefDGCkbO!N$_m*x=2p z7cXDy8ycEg*w{O{dQf2sl&hnorM{vlFEceECOjl4(BIG3-#-8e&M>NKfnK7Vg;+Xk zs>^9oVnSS8Tx=ZA1k5u5^Gv`z6EL<$o(WidKp>u8asL4Y>uRb;Dg(u}gycLR4o49~ zgl)c-a}&N=0Kxd$udWxD4$=gk33%vzM{bIbEzbmu&6^c54Rm1(uPn(7aCCI@3-NJs zbo25>r(gs*&s4?K-_t2-t*a6s?V21Lf%YMyNlHjaB=4HJ=v}nWw*sP~thf*W`he2T z1d?_JYgWM;3;`8fPJhErnwO%~LhWWpeiR zOC}&`&NBfgI_cj%#WMkiN4B(xkGG6ur=hk&kPD2hgm`WwBryC`?&yoNH8M<;x06u zK>H?}K$ScKlCi@&Sr$rQu|z!*bqr^xq?m;GJ^n}1FY-!CvLOwJ=bu>?ItWn=q5bRK z7x$t4nHe97LqJ8^7>6#~o>@HrWYir<#tAmAqXT~>?jn(Y*}rK5cJQyZZ}I-m)Q-Mz z5yby*`+uEe|0nzT#X%77|LuvHoc^PKfaKf^P#o|~z`ECWE}bj4o@W9km}2?|EvdT9DpumGf4un;UfM>R6A5dr~gl;uKm z#GDBe!xWsQ(W%6Q9%@o6+7?OEDV0tJg38}13C$@lZh|%F$tO9*{|K53VaAfN^wLPd znSQWHA#{cG87~V_L7oYiLO_5i=^M(8jtjF0hz-kPE*(A?ygN-oyl-7y5cdz38kibA zvT*Ql%LJqZd_B(I(&dD)^sdO$)GI8=&(6xssS54UEo?KWuftnT@_}BP^nvQUv^Zx= z1O3oOB)zds5C9sxzOVmXmQmhNS5;o5tEtX2tNhY3@-YGNSW0qS-sxH->TIb<_O;Y| z@sIC53%PlD-BP%;6H!m*-ZYhEkL(O475BJoh zd)w$f)zElk78#$Co|ToAot@3DAK;mQc_v_japyc8E(rje1Mh|G2N@8~en`>)$JbQ+|)i9urJ)5<0-4LhH#^r|W-chc`8 zf&n@)@JPUvRYN8V_t|{=l{OR}37D)%(&40kdxWCCz-e1dk1d}wQ%+h&W?f)ndR9hi za&lT`7Dw;y(7pWd%GNpZ(o+G%tMtOlH#jmnIwm$gfzd_c(&MkZAFPp+mqz`8l$@rm zlLt_GL(pc6kidI{J(b4q&L88EfLR2+v9_YP00;Q&92h`fbQ`ye2m$1a(OAb4z+s@1 zXn$34B<46Nke!cAcIvE6tNF_uJQ6V5O|ji}_cYyW!*1Ks!X6NNMtDrL5722uC>lz8 z)%y*dNJNEQO|I6yuAEb8Vusnko-UEdfzcx_jZNw9?e{Z$Y19J?9iIi88r07Eisrt- zuI%^r6&*YhFpmVxBLTz0;gNuwuzz4%;gNuOBw&4AeZ4bhbk1sPYM(x(u6Ft9TT6Qv zcW88BXRdE>sOg>S*REZ^dHc?tyZ0a7zI^kgDdh+ey|b&iuqfXAwUhPhS8q(-VVIel zTM@{Ko2L)i|KMnEEGbBfdLIxF=;!6`;_Tw$=Hcb*2OEqWh;(RxbFnlpBPBUGF*XwL z9Pfi+okif7!cN#=Xl-gh)mTw}ZdQ6~a$w#=S@{_m(3H1rPEzS}Gp(qe4b*xyIb42BvR8{J}2P0cIo`j+INXN#w8 zntx|djME@p-PB0uOJQ|`ARyg9(>Cn#qB-NF=d3?|wWbw$nl*Lx^~lU@G1<4>%J9Lm z@zcy6ESe@QHCtuTWb4ujgm2L#UnmksA6J%HuBtP4%Dl~6R%x!3ow;C#`dS_dm`4J3 zb@%dR_AieF%p(EEB&DRKr=@0A3O{uA)VG#Y2tus=!otD~EklDN;<5xlPoc0)O=VU0 zPd|6^NWeT2Ff0P35>VI=t~BzvQ-lylYF4sFx{F5wMuZFoR^P85MHNNqQL(AvAx9EigEmMh_TeoZt)mBb zZ`!zV*PBYN4Hu)=Rwak}d6*kN)j!Q60rN<}yZ0SBep2`HbpV+@Bb^Cli6O}He59vw z=+J>9Cw{ze>GHK(cMW(XU>*sW_V?lk2KUaX?wmJm%GfcZ#*7&~YBZ2=SEQ5|7Zp^Y z+h}d%JqxGX2e&Mho-lUo=uzK~22$_XNwTj5IoX*6zFzD0#LfGW*5Yacxi0(^#1Kzx9{9VEU}PdAXA%_4WuO{#Ju-%vPLVi zH*buL-f%d+?D`zE^-ZJjUu;ycpNF%fgPon7Ezmq@ilTfJ6krM-2^es~gh&i{Jd`w{ zLKD9OE0>K=KYjk`(=c8D$KYUJw+Lh)6-%g}f9|QRuW0Q5{245mp}&Wcj)VZh{qSLU zX!ujT+m$0K+xM#9&-wWCFz7TtM+f_d!9;ykwOy;$EL$*l<(<@lkBp8t(1FWAZg$kw zII(rds>KTzD9xEY_jL6K%#M7r))tz4NaUb@K~-hn#`TL=FH}@em_2I?j|3bRmy(f_ zlS3cZ(9pnuFyz9)_3Kux+@PZK#LUhuAUq~9H4`$$$SC3M>21po^K|u#j*E>5kBDRW zz4`ftg%Wx{J}y+SwKdmOptovaQ4!=?K&M@z(}L;i1s)C({LoAiW;Qz2(!lOZO9f=W zR|Du5naEVB15=#!(V?E0L}LSpnJnFyh2zPO$0Gp~5+K82<&l6bP8>Y6X8i)-Y)_je zJ6)}8h-!$a3-*{o8Xl8mW{fat8! zU|$(i$Oy*(KbR>!p`S+r zCJn$N0Uua3ciud?X>#&FB9@bv+HL3J^FAyxhVEvTVdApY18Fp<>h1) z^-b(O0>UC9NjDCDD!in*f63yx3O`JrK7E>;jQm2aM{jLiy#XaOFi0IZhVmb&Em*Qb zY39tC)2At{JaqBCp{cF2hnG(P)!q&C_x5-BpWe1)@%(wqwyIsZ^YHl_b6Y2OFCcz{ z4&@9ffc_8haPf=!i`#|66^8(jJE;#DVfH>H*a(9x{Iqi1NiPuP$X>g#NBS5Hez`;vDTwn(D4 zuoU8fkDopWD^mPitPO9R)l@%u?p6k@K~~Dtg^jf5!^h9VtwnKO4rY(8oIIhXp`&Nh z$|@X?gNQx1@5At~zc=TGx>&t4xS*x3uBQIuGwV~B8JQ1Y)uRDuz7Y>=alxb1AA1CXqYBzOl;piWK|NXb&jo0%#dvASCRG_!nJsr&x8XBkXr4t+qq8KFq z!+-hXAO95BBnSJt^GLuv5^!lrA&&&iN+&wnfE3ImxLZ`6>iz8UnKNfDnHToKy9PQ+ zy4xiBzo#uL)b+mhz70#~D=KcbY!fp&NhrIzrzJbm%lxL=zD-M%XU&|q@@``{cReTn z-5o_)5uU~uRd#GxGJBeo+{~pfIxzv2jC1w{j;-sHm#aBLq=-q4CRfXJ?xl; z8bkRaYM=AZNJ~r4$iVRd{tF%nczCEI%jd1JrJbFNqv<JYdYM(C0gIHDsG26`tjeHp!`8ke!ZANRp?L`V)#nYyS* z=v80IVsTSRNk*!mO$gUH+0N`w9ij6`z%B2eT{*mY)#4vyW#pyhcqCvR37AI$E-RJn z4?Uv5t7^ygY+S!)q4I*=Zwi|b<;7k-Lg$fyBRy=dZ(TZnw#?+o6DLc_%wBfi)Xc)l z&Jn5uop6Jn8R+UBU#OrseJV0&CQHdFuDkQ{EsEVO&~de`wIk>L#nXp2&7CzvdeVgP z<0nf^SKNN{!SgpJX69%>3%%81dHKxY%}ZuYlbQsE$+ENNAJM-BhIgPt9kh1l-&5bS zZr7!>(Pb=da$nZ}80Uu5w1?6lZGcNZrI zJ98`l_dvo4ZEkJq7k~WgufyV=_S!N*WJhb+ zm*%EKhx&LRwcXjl&cV$G49(50=)^bhQ=h1{2B){!@Bq+VolH$FZQr_ zc68v8fYJ8=n+A^rEFKv6{QGa82ay8a0S9ksQBG=1L|g%n1dI|_Cl9}&!Pfu!2aXTj zo%OYiHKhefQBeWTuFkerR@T-$5^!z~j|7b48@c~6@SO`KAXd=DA_2{Wv%x|vEJvCB zGQxrYJWhXfEd_BXZ<&3;?<^caP#Xw(fQpyJ;@%!nM^jyWt57UPEQf_Fh~5M$GOT+= zO@i!{@E{KdJFnzgQ4ed_O!P*Chj49k3D936-+Nh_zIguPrE6AOw~%g+Km{)E71fpH zCdS5w1$a6+ynFr3;MN_Z5FQCwj9SHGMhR6d_})6{P{xwvrvgaTI`|PlkMfWG%A^ zSYJ!YIh{Mk3(%*W1@9zuTG8wrPsa(yg4iY#8^{nhsI3kshj?2Z9grJNt|&amSXsml zx5eAW+$fy_k__tM=oLs}Pd9@`B(WrNhnPy~&}VZ*vWbrgER({{c1$Z2idlu$H}a3x zn^b~;UYvAWaew~^l9%MIQCJZZbhNjS@S=)=-We*5dRZ+4Zh@OXh&$Sv8BHwi8|>?9 z5EK;E3Hy*B^o8Ei(kF(1_YX8DTRps@t$z6M=?A`L-Mx$hplB_vy_j~OE$FV+@nZ+~ zZe6=}`Reui?nbsi+2iBFfr-chlM5#f9acTKch8<3YgesWxnlhZ>%7($hS$a;0rN<} zSdTmsutaUsKF1>gQ~iLjKk0>^udk@7Rs@^mixGrvllDjKwS86(g8lv4GU6)Qgp}0C zrHXLoPVd0baAUHqyPseC@b99k>|(TrYi@04=_cLSFb8=gU~4;951;|zQSwN@Xmd#Y z3n`1J86AmWM%2}yI|Uq$Wn_LZ*&HzuI8PvD`B0J#rTCDI!r2j&;loYB^(o}&BOH<$ z{zoKGQoH|8<)23aHnkOf`qYu~_U2>H`0PSKWo=_i8#>G5Y$Wh`eogz5p_S<4FP$-A z{?BebwD(WWE-I~RXliJvuHun^eFIQJ#3KQd)}bAO%zPdRm>n(Jk#<78mh05#-!$bM z5ClR*gKd;ldH|00g6+p286h@J%KWSUf_6cMb0%_L{PfM6?v}D1YAEu>0H9+H0y6FG z<=Ts^OkOuX-f=suzE_A{wT)pu0J9cA*1;WhmA5T&<&`zp_%1- z0%0lFTyN8wMF*a|stXHxd~Ds49rwKxvkC+iH4QCo?ajshJQDETCq`yA&K`sUO#kqi z@kqd&tBn>T7BW-kJQ6UZnnwcWk$|a}0S&mK;QVF2VG7CvsTnSOauWBm(Z_k9;2ok= zGV-fYF(IndlgJ<=+Nf-VN6rq22#? zI!P|+A;K$!p^rO^TukbTs7#-CL5DTdJ8WtoeMAG|J~Fsw=hX8Y|2QK1DmevEEdU)S z=nIbojHk+i58~c3^LOtwW&`HuRX0^bv?6&5$0QbkfCVs6?0Nt2?#0ToGSUl^x`0TF zB44(C@VV1R-)HRo-A8pO{QYpg1Gfd)Qx6pC^qB-3b$?Ox@$)0fwmPnAkY(diZr?)Hj1Zal(WN zlcuaUck~JfiHHK&DCo>JEN*+XR{00n$>_2-Y0BESwr;*a;>8SX-7(v-@#RuQCF#kN zCrzHZ>$MeHfRKR=q+bjyQXUCdB7$_t=AeY@X^aTv$aj)FO4q^aLeAcQ(uqoB675FR z(R`(|VnL$P?39mceF# zt@^dXhker6f^Y=|=deG4%p(DlcEIUMq;lG2&wS;%(`01iFO@^^c_d)M(F>s60nSfa zG#y=n#~T%<&ybZpeEYeDgR_gfcVHONS-ggPOYN;`&-N@(+7ZX5+0L~l$u8BQnGF#VmuNsJ!?D&rsAcM5wIfpmM?U=8XE;g4q?LZNWc-LrPRY+C~D4)b-Z%bz$Vbq z{K<{Wk8WQ%t?3?a`_?ckFE78SxVy7DEzI5i`Rf#ao2Odp$M&fm-L~PjhwUq)^c=*9 zb49|kxB&MYH}i1Y*Vi=8T|T{Gho-v9?dLZhMkc3aWoL=HTT=s5nqgA5nKlrX-qPhN zZ{b0Qeeq)XgdMwQ{pn9qH&=}Q?z`_tj~h2ZMsJ7Q^sxu6ZJcnnv7I-5)MSMprtETE zJ{I)v$BddV$3S%oj|9vk0i)Q6^C9v`z&sK#sS0QVv6%VVKmGF4=gv3>OLnxRqa!o~ zr-8bA(AQ{?M*`-NfC(h09gc~C-sYmx()5U6XHWMB=g-_Q4bCkA&;#6PJQA?0cT94I zpuTwoZWg}d+M4E)DDP0e>wEU|NWc{3;*o&Kc&C(qcu~pf$A4%jhK^!2)iA#$20BFv znGcmBj|gvab;+P3HHiFwv^%3QHaj*T^u-bg>3TByMLojS8bNVseTN8%^~hIcbZRKT z{I}g5nL(C@S2Rway=kA;)QQ}B;H=QytE#9JKvWZC<79m8%!v~_*Uew4X^T^UEG{3Tz!acQB<$}V@0OND2XMfzdWSZ2%hi|Q%Lt~Q)>zXK941X=!#8sCk znq1dDxNpsj$9qbaaHJ(7MRpO}V(Y)Q+H=pw z7oc;43TD_qhAoZ8x~lTLoI>t+N-X3?r>Hq$4YjnOogXUgOQ}ZZOL8|8FPy8%u@3Ey zhJjTTM5jUX;2Y<4#~rAY2OyW|l1G6H$so@?s@Cc5AS1+FTg{yL90Pa%R_cqy3W2DL zU-fqk_ZK}sFsSJVj|5ztlN=uC;~ifPq~D_4TpkG+ZFc_h$FDzs9O@Ib)eABb!ruG) zc(}PZJ3G4hc=1TUJQ6Uq7h%9@NNM5l)6i_Bq8zP+(2EpWoFS@VGh!)Il=i~4BjFe% zmw;Rc4Tf$f$U!H~&$?h^0^(<*uAwT!&(ipnkwZ*bV?8mHQ@7JEiOOCC)DeXP`r0NqM7DhnQRq>%= z5w13m_4RQ1=Cwp!y=LvE1Loe|-W8SAwN(**j&_zW?&zOY-?w!Gj|9vk0VCra(p^$g zT3TBzX;Q>VH>I^f(9w;kj2Hww5-^VhTvlbia{Z=NGi9Wt#*9Mz;Cn;?fP^fy@DiZ3 zu}n(K(ls`(U$a0#VdjKUqrM*n`l!(pCQ0wnyL|I5vrH=-x2#>eaK6$knX!mZV207- zCQVUPJ9F;p4WQY;axJ>DX6<|hbP=5}8d%9=#!Q$pl}7^h^7L?bLr3wVVhr>P$ji+I zen>`IN($PKqn~0>fWNQL_XLC5*Gmb?ToQ91YYRYU9*Vqm14$P^Z2kQN=*9++1A7U5 zka6G(=p$gL+z-ff?n7xkSN1|kyF?xwB*azjXH18xeeMn-A$yG@e#3#Peg;k`?4tRH z8GNsViQTQQZvgEDC@hFq0k#Wjr!Kod)wk=;aUjbO9vlSJU6gMa2<`)*12=Gupe0I8WLq&bd z@>Q$n%vDlSP*hNy^MiJLDpHV31oYrPwmrY0bzt*~g^L%=Ri3M)sH8M|nORsuW?n&Y z3HwAomEP4;M+b$)^OcpA=P1rr+GyYr9G8}zn_s|vsvnBq=&WA1dFi})bLS{8-f`Z@ z(U(U8#!0=Ysc%H|AXc>EQ6i2n%n`uS_)A2~a z(Degkg7o*1-M|%DvBV-D=fJgw%D;dnBr&vJ;EC)EuDiHU=?3d1*6 zgglb}hY3+}iPHt+V1`ip2oj*EyQ{6PFeTjA)jg_~Aps)t$RYW%+ak)tBLTm8^5BA| z>e1tBs%p9>Svfg5xp{eb=siLlrWy-_Y+gRTbMC~EBdV%LkDW6@fee7eSP`L6*im1Z z=V$u(uKthehYlS9n#OsfXp|Y^EJ@dkTI(wEW1U~!xq4npRb~I-qpF%0-v)<)BRQGq zJOi7p%-`&+5`d#EIWrq9l7!*)cqmu2iG{Z9&)}H7va#) z=!iQqgfGILL|~9y^5_Q+p5j967c4*sTOR{*%Kbo*Ll!b5$;B9*>#;iMd%j zo6ECegWTQ1E2;G$?m$SsUy#6i`+6FSQ^TBJ-@J4;sJ&;z(BhGRA%1k2(=hW<$}H)SYi|Rcrg!<*s$1=bLy7tmpX8|ai(TgoE= z2l7b3_wGD3wX$<|_xAPm$MFL`a0(*vNWeT2@Ci*VRjnf$R%NXOMT}I1N=a{nu0Sg{ zGq>lDp6YJhzIp!Q)9I~W=&&|KJt>wsF>VefFAXoPUo>}?@?!OpCZsEI*Q3s@$=|m) zKE@7t%;$D2RG2YSaiJN3J_Ak(hbp14r`RpHGTH5|@dG`Ljq_(tlarZsva}j-YJw6v zbP2nY+|$Gb-tXRAKE7?yEO{Au8M*E80y^a7;T(j%3}NAd-l7PLr6Md_0VPidrOuAA%9tpUmE+;uVKQ-9J(&VktTf#xMwzab-FL@KZ zWyP8C;X!`h-d;$sp+RnR>b6ID^}v=cEiKMVPmGO<2oDPl34R|KKwuFx z16uMm!uM81IDEMosYwZOF;S5b5#fwZV8*Dbp>)x? z8)@~RLo*!CST;D4mKEh?l6=QTM@Iq4v$2VA%3IO8nTp@)k@8+%Qka{WmXZvfxY*c6 zbY{n(ML2!`*yzYe7ztsakZ7o7Hh3gpN}a&5nw1m*j~pRNL0cPF zzACX|08heG{ZH33 z67A6)@HeA4?BvKmZ+90S2^jVa0`O3aETGQ<{9l6o z-rR)i#K^NALK(q;sH48Dps1=9K`WX8*ApE$ zfx|!j{9$N7ENm$&$;wDi&Z+K1Is!~GLIUNHfdBVDe)}}o(?*tPRY^`-Lb#8cvxBt_ zj|7~Rm6_Fz@|uA`B$D<~3O*(8;}lSdBp!0_=H=$VyG&X7{U~{%mDvGPv!$W7nv@gc zA^$L2+#(_6_;om^RYNr{Hk=9d`04iaq2y`duu4mCI#fq}ArMi?nH z-;UjZ_6JC=vh;`Z9U zuIl34WIu0z7grYxBf}>K*Y$opby8bfQ&Y<*q^GA-+*OyC9{1M5!_nKr+Vu64d-@m8 zYH4U{p3u-X@}=#uuPHk%!OhFx$J5Qy%E;jQWgTrTb#)C5%^yu&#iGJ~QA2S?OrV>) zr?;c!tH<~Bad#~Zbsh;gAu%zDEo@4sr5rwV=C6a@iSvDSCWUW!Bw!v1xUcxlgIgCM zFly==M|Yvvb@kGP^XJZAxOl~$$C-tB;!ZyYiznC5>z-EA)H=F%$GUZ^mMxe+ciy~t ziKN>6c;m)JZ8dc@6_xF4S1npLXAUaq=FVHNaLKC}afW9`@T>cGFC0F6 z{NMqV9UIrKTDfHYTxBICu*|>e+f(#DE6VcT4ZW?qj~+OFWcT(>^akcC&Q?&GtGwW< zOHXxRLYmuyo0ktAQP)sY-M4GQ+U1MpDk_1lJa5H)%kF0Dm@t=@w{$fQscNVn-iOPV zFPw`?KY9my9vF(cc_d({Mji>63g3%GOyk~VJ)yXT^jYk4TFrloJtaWO0 z$gFm9aPvsO*kIaNF;{_}dH#RMKSUq31Bln4(5hNp;dQu#F4;hcEP0fz?ZOx9o;duc z{1XFXY3ppcd(p(^Kjojwz%cNshy#vV*xGhCRdqAZ1iKO2qKJ+IQ0KkFu6DL9VfN92 z)*f<~w9~8Xayrzt#Tn_nv$Lyj>df@9d|w9au(}3-fwvQ43o4>oa%|q-xXmL0)1#yv z7l){%_wIIYuIcDry!H6W_y}2?sDa6Aay)#^?LpP5EOr1DM z|B_aNUgME~Is1V}0@m8QX65oZGnKboe%RHWZgXwlUQI1^)gvkgR8Oj(P&>G1-TGAv z=P1lsbolC{PGP72;ahW1(J8v4voXc~=JDN| zRxMhvc-gw$s;95;NWkP~EE`WMg~XdPP|0|OuEX!7rpOz}(K#kCa=t|3AX^^lhA8wp zH)tY>0gMV<8lKIE7d z4ENL&M0;4gd}>=*UP19q0s5R1y{~_;+e0AgZm&uWuzCCNp;25;ZUJllR9wtmKV0k3 z_tQXIPL%t*CwCt__Do63LIrSsK|vnEFL*S=Z4p0x?5)f6vp0Hl@7{gO=)|JdlQhAm9H+Cn}Lyn2$#S#tuUA&Ey}) zm!969Tjy_gvG_i`kgXJi9HDlLnnY!Z28$No7K(-VEQk&Xm&iYQi1bAm8NAfnwQ`|B zdska0@roPylvr9B}F+|Y2~Sn2T}mw9!E(5r4#=L z9ard~;TGiIU_19PNc zHFM@YUgME~5rJrIgn`ppkPsT?WN&0+R}gJ{+9;@O!jWg4{tj(f)9853i?%cdxSLfm@3v(+^|B$dQVOvS4i*ZPdr|rczUbgyL z`!{abdj87AGk47F-F)AJ-kSQ})zl)`{o$!Aw{B=@sUA|npv0?hcwbG-q^qywl z!}4R7OqG7SQ+nJu+3A16n6z}O?1XjR!J(nyG2LBOOU8cxZlm(KsiVI8PGK>R1pNJX zqvg>IY4iDO_Yvmn>MlPz`TIX9?~ofc8Xc0RDbJJ|KTc|f)R?8`KsN@^s4&;^)Wj3| zGygPZ?##KHc5GR(XwB*$CXL^I>CUq^rZ%uoL`^s3c5BOjC%C~GUr3&k$`z5VCE{}k$@#WLQ=l9)n%p7!x(frXXkMEw8_E8 zTzpVL6=m#K3Y9UBI$nokiab-)6O;;B=@nMtPWqgA2gq@Sg1N>9Dgr2Ho_dxeL>_yZ zoi4@ER)XY76O;Fm-Jjf>i~;V0uXG8O)d7%~0TX;nXPyQ45!&GsYiviA-4{A4qN~~w zpv57&t);p&#W&c;KS9t8|CnTYR0C3sQ7CGyZLZ0T3=DR#xcXq6PeAz_ms0toG*Gl4$ikTu|Not+QT z$dIryiG+g=n5-3oLXzbX1}4i)gt^ZW;w>Qhw}Xi=bN|;NgkN6D$v1I8%pv?3h%QJ< zVG$-~FG1qjv>;+;7JNVv5Y+&caXvjB3HZSA6YA>PXU}OK-LY$s;N|4jk7yqoZ@`@F}gsD;6(Ro~LH-8yppvC=v&2UAugE+lGzXb|2N&y>u3A zN04SeTXwg(qenn!m(%QZx7D_9+of{w@KH5wohy1LcOT!sebuZPvMQ!F4z4%n90;_y zd*`{8lZ&&Xot4ScEBa^jjvU^;W!VpMYA_QQ>^6FBZ0(nnTUeN%osp6d?Q8n%`B@E} zt*aDg&Ny#szMn?|M*7}2gGxuC;2JC6p3z(|S&9NITuJ8$4`px>5^urvee1_hAeZ*H z3_KDrT8v;I7rUsaFfTWUeq)GH9Bx!uDSD8A1@w}lLUbCTw&bPdK1=ZB^NWd*^ z?a%|l_PRZf;>^ zL(8iUdH1B`QQliwn3IVr1uB!aw?mn<4XwXgFfgl~%74pB^3oHcLj1iw++1CpoXXkq zt!<#NT{AFpE6a-WveOb{!h-z$e7wCp;bh`CP+wRF!?;XPn46uImJ%NkiZ&$ven=Cm zq!msBZOY1l449jfjTS=*=wuWc{GR#>L1<}I(BQt8LuA=TP)t$_OB=Q}eIbwmVusLy zf%XYp%}jB^`=wjZ00)l*ywkCon!;db!&Ax5Obqq1GkSRK)bX90wyp`kHl{ zZ{lN4CZyk*{6axRvWLk-9tl`w3viHEt=_P8`%cY^`nL_90J0WqwW7rF(ZkEXmqehJ$ zJ4yDHASXMMw%b~_CvM)4v^LL`84r}(@3|X{pR9&%Qt_2!T3MTUg&G}OIdkfmaief8 z{*4+lcH)N6=*S3IU)7~|?%8`i-7;5h{AhyWCGzMoV<#-M5AX{pEvu-`JFj}?%7Lv) zGLwk>9b*|gcA~-)CkMyU^2(}wtz9cuuTzqnI(9TrcnOMk)TpuJCe6O~=B)|zPerNj z+LcS^DNLI*iP4E?^w{xJXX-tDW=O^#j|5y)08=?PH_6xR@!bb6jg6k(zkTcWox6AM zKP=>6ddbwz$wFsvPaLAT2c+tpTHg{XCo<9qjDv zY++>66h-;0r3f^88kz^9$7q-zi~}ACSOlcvp%0Sr>8H;>ePUq2gBX3l6+^237xL$y zdur<|8v8$g1`Du+QA~jH1>rEDa?v4Z_*1>xl_M(K_p0B|`S|lN=)F`b!02HAFqo*X zs>-uDp{v@R89`Gt`00L2h=`)i|+r$Ew8(7bwk{J@<6=2h5Htht?K`Vk~md zzo4qJZ{zyKs~0LND9oO*($E2N*p(b69oXj;%`;%~hB+TS0lwtkXOaaBhBnVIe~r22vzGE)i-vn(HcHbrlvB z72|{owO&eeAV~rZ8-4<0e&VUZ%tjYEX317n(oz8#P?Np%c@qpD2AXOT&@xOrqOk#( zN_?c$=!ZOnaJZr82UteP13>b{ilHS#9QbhIutT?=Z|Rcq6)LYGItGsfOgORqcy?a~ zgGNQ&3iU+7lfvr&0R=-p4aUU~iTkLh60#Zb%E%+8jbMne5WFJj6jS*^9{$utNT5B# z!(gGCFd)PBehy-E=wpq&&aSO%HXpo^KKQYpVJ8!iAA43K~7-1`OT zljyh}ku+`w+8-zoFoN_slDz{u7%115Me=|IN)$G5Ih&tNFhVDJz-lIG0+nT~qm~Bz z_?3L7kSWMhH#DVWkV{p*9H{~8Bs{En1djyF?I#fL5hP!< z&cS9uwSe4rRx3c|d^{2`cDbGpA3qPb7R7lvm_533@`Rd(j-E{`v+eGGg%--HUd*Zm7hPw8(_goD~HBI05 z^U&wtYvMgD-#oo~>bUB0^ncBK| z1q6piMB(u=m6c;>WT1ck%o&X%$2CBDAXHOsBJbP02`fEEE zfQ(U>kcddC7l&Psy`dGsuafMj_d&s-z)%g3jEahmiH$@43KfluXzOUGLujig2LK+) zDHw_95}3@@*5ELRs_+Ih2rUQBC%gqY0Qt=V6mvS2-%wo+mJ5z1JQ6UE1gxQPLPP7a zFVI{FNDM2pd-$)v|K%T|(l~#2yBAkZqMhJzjdK<`IXS4|&n5XE_~rBOKey#Yde~b& zx~P3zP5qeKkA_jPaq(DxMCXx!yL&3aylh`TxOwZm<_RsGn~x3Om|595y108%wh(DE zQF(}?#XIAtSI=L#X=rR>YGG~X=nSM&N**G`C+uoP4%*>3;G%!*19tOD-$RUIfA;s4bddi1SF)KZdqf(L*xf9^FK;967^27q5 zxx5^x?0?UY7|E2NKMWVs{x$N_zklc6`L|>|g>RX;>qjmkA~*1?*!VX(+k{8X1ri3_ z{worAkGQ+DrKP<~3{MQ53|K}aj|7ZoA+Bhx>iEPX0neW>)-P37S!&jt6)NYh-F@=P*wn@b9fE*_*4g2(Ywc3yS<`38 z$tf*bbLjld2ajL8Ha54lqZUGx0N4?6@!)jxVSjGxVd}KnSr{1wl+im*H>3o6lEqd43O|ppoF{+W*0*^ z(YTW7{|Yn-&L%H@Vq$z0ObC)eoaV`;$mE~$UI8`4BLVYBz(v^Nb911+(MaJ#EDWf=BT`UXJzl~<__iA-qF@!jsQlfx*R{CC0b5=Redsym8^eIWkgHF=o!&bnTI$iKVThE2Yli?A#u$ zvt!%pMYE?*laZF0wRr96dyikev$U~yq|7?#&*tV}^$k1{Fy-^XhXBV99RYYGU>*sW zy1#U^`}*cJWbjD9d-tmz)3|)`N8QWUkRAB!7BahxvLz^a1MQJ0@QGcJ4oXBXp;;N3V<84|txJ$T zc_d(Dr+15g`uiV0e;5`vHV6vS6T`e+op~f+PoR8~6OUF8j|5D0g(TfvMF46CCDJbt zFbax>x!OW1Ev&AtB;l28AdF5zN(@L@BNK({ghqB4yhtL-$-m?l5MI;}a5E5jME)t| zfuR|bbd&gF3u1I8;B504nStE{^b*YfRsOl=iAc=CyP*96W)iNK(CLswi4i;!u(tLI zjpKSAegOz(H8eIfM&^n;Yx5EVT`i1W+`9%8FfC2Zqo;W!U>*s$6iB@^pnL&n&Xg|{ zd<|rLM1F}%uaoj7K9nleqSB8m1cZKvZvs?K{-Kj|?)MWSU&bcGYX3dbBCOu)oxN#F?C(0Y8B__nj z$AjJ?EPZL|@ZjkB6+cKxO&m9F?6`50r;gqcK>_SYkc;gMU;BnUIHELv+Jq_N#*6`d z{KTc6tT|MNfp{^y^E`o(yJ9Sv0w`qY>( ze{U~0_khH*azX#l-~RRYKRyo)^dS=6USCyGQji)M;_v0|?BeY7KEH6_=fD2*AHRP3 zI3z}txvsXXq$o2pD!|tb^`lO9c2QYFKmYlkfB)_Ca9>MPD>mMe+?pfBV-z|MvUG!JeYhhN`B9(!!k7gzz8_Cwm768%x{3*rA{Q&wu^vcfcUmHzMd# zTT+^r92w%{jJfS>tnK|nhlU4vBw*xG4zenPt~S*1R+bhcMxUL9!6N}%;~ddFWq}?Vu`Gg-qzBrSU*=s3k#FiPYrHe)4y`z-1+kt zs0*Nl4bsuYJxzI0Ud}e=rlv;EpFOyJ^QQi_8@KO0erarBW6$!{#a-2zk-l(ySeTi- zHhli#mGQfG7U<#U>fuc(Qpf;83xLKdK|ywUQe0$MNN`YK0OI$^^oyXZ79;^e<)Nep z88(%~l$?~1kPx51BLR1Ih^dqg&>AE1&vpYmP_FYzCyxZoBLREb8s5=6bK>aH{oA*0 zUAKDG(!~qs&!4|w(NYz?+s{Oz-b@pN8#>x*2M_GswtLI^70Z_|S+r!ylI5%S>fU%{ zNb(nSS6}DY{=GYPZQHVW)7mv_*Q{E;a?R!==dK$(dyUkx?yks3I;#8k?B2a=+xDGX zHf`FxapR67r!U`n_`=8(DQ-xju1~PJr+ebK>X9Rd4<0yp957*zo}55fNpM;*Dz$j4PtPH&DIl4K(C#5GU@dv+AB9!Cm#L^p$lvY9AZ5Yuq} ze#+eYcLr8IL<}q*02(BGgoEmf@TYPUfc_Q9S8@*t13CP-3BF_oO0oy(EqwD1zD4?F zf66x#@JPV!etzx4zl*A}i^;{(ie5Ds=%m>{IQ&VNpXTr2=G*(P|NXltJ2ED(w6dnY zskMUyqHplyPXpD-@9iD!y~Ka}KY#9OYeZ)4GtIC>c1o<^tIUzy5p6)yna7i)p6WM2mFHn?# zt{N#HhWNrmEkJ?=go_(pRCUTcW~ft!mkq&w(20SGIGwvuNx=MY@L3Q8j|5Cl_v@es zMMai860pGM`8DlJhE}4FzjVUk@a)z@d;jF@qSC4csF>=iWLM)GYR7dRn~J(Wij#d~ z6FqH=Us(HD`$s2cmFK0tw=g!kbWH2~LlY6~>nwApl(g7j9~U>@n2^YDKX+r(*AK3p zIdk^vW5BNUHr3^(W@Q$7IR{xfSo^wKymEeNboqkb`HL5Czq05O^>o)%hUY~FzOxT< zdTVZFbMxB02Y1h%*1K})rlA>fba98S#{6K1S0T2~-`c);tgmRMyCuH zT9hPKBv-KA>3X^YMdDevO)||ENioQXNA#?*8 zMDHiG3PP3{PS0#W@u52fb&?^TK^_U1s||#=0ecSqvE7ZV05+f?L1!ls76aswfN|c2 zW!2L&Q0#gC@b1OR=)$ousjIaWK#tTPv<9{*bvzOGSv!TFU%P05tdx|zehpF}pd+aN zGF=byK+YLm%hRjpOqZD=CA~2+E3cp+H#;Yt6DLiP(e)1ujf|!$;ABx3-M_cq!~T-`Jn2c3CQO(( zW$GRW4=}JIcJBI~?yea1&7e=5Fk!-^DeKK0y#hiaqN1Z?Ku7-wuK(wyl|$R)YQpSrKDFsGO}`V^YROP9}K#Pyy!j6 zh8vd5pDiziA-(Fpp{cF2n-}hZ&kx|T^eHyHSi5A=Oj+5fQZifbK7VI}5_=yXf1*(S z2nLS?OiQ1gpQ*zab;bXRnoc32D`G~+gEhhTQm&}bfXI=&SZ?*EoyA?2x#ss%uaODzi`l` z4~rhf-{dnP_mQZ*y(U)RW1o~^_gL@rfn#rcgInMx;E{l1;*%i01;I9MUY3t9Yz_#t zxN>aE_5*ts?FtOEdwI$zDkcsOzr@GP;I^^VV}s%V2ScsnyH$4Wybu%SV8bH;$Hc~y zanLQI{DImE6!&LkWuzu0kar;-J%W;wQ#c0$>k(835CC9fptg^)KQl5iF|s%Z17}pw zp}q>?LjZK-<>hjNc_3hQQ2s3oK_WdDjstWL=8=Huk>K$|hY9QJOdac*(lT>gPHSv> z-rr5qPrGmf+aKE6Dns+E4MU<#ZyiFRFK8miON2%Ow-g;gAMRK#>?&?@$R{q8i;c%6e#_Iq6b* zne0Rsh3FCvM%`tNuv^%>;erfA?P?@vet$>O-h>`@J!yZs5Qk(OL`AMAZg=+eTVKBQ z1nM92H!}|?^nXXSprLoL_T6T=kQQUZ#{NEP#tiwVi;_KZACpge-=ZzmVS>cB%yIhx#1^xRmqbAHTP@OVm!ZK3} zn=WBT`pre(?=sjpdAs?taiEW#FnWs2;+2!fP1k&DiU?s_(1DqwjIYc8N&c|n=+ToV zjU6{eddeh)`CD}#8XB8*byvSq9R1zib>sf@owCxTiPIeqj)kqm!VsWSy)frPJL+J%7*Ezwn`H<3r1(kixd$J_Ga+C zqop=4w*ap4_OD4i^k;yXln<0&eU+eyykKn|>_Q@vFftz@NePbx%p(CGI`3fb>>Zg| zBw)QAvs06b8ycg6GQ6ChT+?{z;&SFbj|9x5hBgY49val3zP+Wc(q7{@={Kh0I10W} z;0i1}5-=6Y^GLv5q7H%6`Q1DcFa!fp6H+@hsTMa+>2IGaJ8{`fWK#i$9`Zz*i`+=a zQmL;?dVNh@XT>xr*~49oNJ6BZyPh#~yh zSnc!X%9+J-Qa|8qNRo>|x|Q7le%Gd^+{brrZIh9b1D)nb{ zsw^}y!>eE%^o2(PhV{+z{19TUttN9qV!_i-@M}@e3rfES;w}<}zGJ<7&^HVN8OPK* zo_$C55sw61T9lj1;RrYX@yBn!{Pb~9*i=)Tl@tn?R?nC+L1}S*E<*yW`S7rWqt`??y+Ga^yY=Hub&=3R)R7}0AR`u_3f zKYsZ%JSc9f5#%ICg$DR|y1Bc?7s1yHa(&aMzyIZrpFabrxD{Blv0=gffa-N|4y1n3 za6>k={PMRy{`@O$FKnzW$&3mO^7Zy`b#nAfN=l5ct!-#-`{OTv{PD}jf!>aW>cZ5h zus~nb5IZ}$Ma4u#0;aaP<1c^t{nyVQ`@1_(ZkH0pBLSn0x9jVdZ%jfN<*{kjd?UnACnUg}K#>x$A7-UnG3J=E7xSJ}FG!`e0L*7Hcfdmo#d zTLG!Kx+c}p-p2UB&5OFKd$z1uv259jRjb!-+NS#G<=b}zL0w%+5GRlBTs^0{dm}3N zmakm7di|Ciy7wPFf5ot=t5a;uj1BHw{83}qhSjT9EMK{5?dGiqF5kTO_*ppzUhQZ6 z`o2DpdU+(^bm}3V5E~gpcm%$_lt&D;UV^UwMFcmRmB~N`l9OV>Lf!)_9P5`k`jGL6 zh*d=eC*xcTt`ZzNi4IEvef7v)hp((73XcTLBLVYBz`B?8Zxo{t7oz~azPMLM*UVR( zqr7m<&Z8%^PyKlQvi|j3pcfYw@<_nQu%^LqQwjW!e7zLi6SCJh;x`<<)Vl!;!Y9 zvV7I*Idhei6ciN{=lq}@pNbUZ5&=EozZ)H*fA7<;6SB z8#(%h#UTMACx@dC3qpCa&sAdaG%!z$LLZ@VjEqDKbhDA`dnIpzO~u_sBL57BlzW4ccZunc^^Th``KS96 zn&_9orr?o)4@7=fsB1>*guXlAk6oCnrB`n*7Ru=-7nh zv~rIhfN4#05<$T~7Dnf4{YHd`=xo9BNWjqRY>QxzgJhpa0)BTz zWB=9-o9FRJz{|F(UAgn{`5SXvs!|9H#`ZNZ0AzO3{~;bOevy$Oz8>ygKK?-=;Z){? z{SCo>(2)iv7In2ZmKS8DAwwo5H4P&*jrKPJyJmp|XvOBH`s#9I$WnqVCDL+whg2I% zA$TNUgjguwjtx8y9toIg1Vr5fA3uE%R;2j3SR39rtEqnS+^vjWJRT}#qQ*Qt5^z~j zdWfCjb-mLkj_g%Abm*w|B{QGEkcgQ0M3%nUUXqjK>+tIGSxvPgD*Fx`Ry}#m+8qqx z(XsS=d%DZhqkXKOUOKI*p?UxlsGh!S>mo z%)ui8^GLu)j-S?l_QuTG!P(uL68w6^!s?vlFef8}Ti4I29XNDML-+R6*QP}GLV_Pw zPET`2bdbxN2iI?0AgJ%N*B(B9Yi^4&M9@3C=t#>W0gp(cD6GNy$%}vGQFRLOY1- z;A!zlz}>l*5AEE*BLSmzC$;~qsVWz+Qo)Q=4k;XZl#;iQ8U(aqis#i-ms3zMKPPK6 z6E}%yaw%8@x79=~JXvT^B94>=O($|zSc@bg#y}H6uD=X$FyA6vi-0xft|!fpIhfj~ zNzjrAd1kD>j*(S>a- zwWWYjPR(zk3~Q88Q!6A^qE4no|M0+IPis?cUYwUlay@!BF?uDVcZ>UmfBEf~&jUSO z_34q0FOA&F+VL0ER@YRavl-lteLwyFw?F^-({NvBO}vNcqbJX-s#}{HP^4XnE;?dy z@9JwM11w)XHn__p0i!)AqW;(ecqCwI0*TQh z41Idx?nNF6m`4J>eCgbk`;QIZnp#@h**j8uOx9VqvAH%YAtNjCy&Jk46UMN!t2?@V zL)S4DQfNSNDJjT^ivSD&8$lQnIy{HaN7a>R;GLV193LAK6CE8D6&VphbPOO}p{R}; z^Fs0FXCY4qC{1y3v9Z*!lBfWzqpCZG<0~jG$jwSmLoQE30(*Cuo#>>y!9W0bgfs${ z)D&t|0W8xmbRG%#vSWECbbC#8T{Rw+#JuPc1zuG-MEC02uk@ZS z?|ZlPE*#pqeWk+8Y4Qq(5}L?SGID))PwX?tch}CU?c2O$Cfe9cQ`lcnUro)}1#JGF zS_hjz(}%|n?^r%>x{TBmnVCvPjWsn;?b6aSaJ zw~US}Nw$UGoo+M9*d8-u+ihr58`}&r%GhRRkZjAcEnBvjnWYjlGc%O9N?f8MvCQq} z={IxVjyxsVJ@?-At+&?qu3G@9Y_(y-)JBY+{W2&ZC^#g9R|1BAk%voCU!3HXfLY94T1*7v!hB32 zsr@i_a19d-uLO*p18q+j4KdA5?LYtg@l8)#b9JRKH8#M-$x)EQrjM*l zJlTyc@{hlMeE+6P(NJAc05r0TqocKb3`)IIQj+oPH%r@p{{y_K^0zCRhpk37wYHb;qK-}QXihY5->#;s@9u4pxQxI#SpIq z%yf)tg=wfO&qxUo*jt;rXVp}R7zs1{o0N*#5>!)}pBm}!Vxy~d`I>EU)nDy2rBr06 zg?QK-Xlb24{=<_T5oVWy{M=j`zrMb)v97)}J0Zx~#pL0w3&)NfyAzk6O>9u9_-v4| zX^~2*OEaSUTpf%a+&*{g$nk?GTp>pVG&tECAh*;g5~c)r+nGPPd;Qe0!-o&=yAuhT z&&Wu6e?qZB-cXVo>fxlXr+M?-ab5}drM`iYiJ66!jU6;~s;VHq1A$vi_ysvB(IJ>0 z++1DIcqL$730Oh_0`_2yHKEtnt(-e!>g?q=qpPclMw)2a3n=Z1{aa(U=FSDvCyX6F zV({SMQ?3-$^xE0wwT&qk)i=&xI8JT&pnih~4;ryEwUTJyc_m;=1WICYUD=%t3;#Z2 z?8qU*hYwR5KWXYW-|std`L6ae1Jf$9i$%GQ*3XaRl$xvyb>@wwd*~&BQ_wCC6Gwifj+jez2|zY2MWq@e8(#R^Gd*8 zU7eDD{_*j3r@XmJTq7#VjSmm^ad2|5GB>xdu(ow{@9t{<{kIRV+hmQ^qO!u=ywq@C z7bgc44O&=OSzD3))f*HrE2Rxp6~%dlnMq**-mZ>L_BPfw)>aN~0@NtH`SD#h_CDg0 zqWtWX_=pgI0G*s1F@}SyhZjl|dfvS0meq=JES8^@oD>rl9N^>S>F$X0e}T7OCw1uQ zZpZ86SS&v`D>W%LCNjj|#|I?H!J%QOCSesQotg9JLfXI+48rh=VM4V)?}ZzcVprdEXtnF9nKL3E%9K}TwMS?1Wy zt?Y=LPWCC+jq_|dS7pTY;gx_Dt?g+~&z(B7chA1P`*v*F`0Y1e&z&`M#@r3J-P0)r zsN|J^c_m;}%Q4njUI~~Eya>NT`(~y^+^h75`Gi9TVxA*76Fax4sjd3l{;31f;?$)n zJ-dR#7g7BcH8x$gkX$XDyp}w$FJN|7pT#=REJkw~D6yLBwANb0kex~rnHAS_FeaKC zWnEz!rryksnUU*2;SSBE7z6dkq0b#Xp|e2~XYU)}Mj**VX?y$=&@>=2l_^o4$-PhZ z7BP)tB2~1RYX*3GHKazDHb|)er58g<#@3Fm?wSNEUI|zL-cSm`yE^cx)nt_v`OcIFr>GM=jAuT>e8Ng?t>n|$EMR`zW21xtU)7f%I9|^3)=TGoCq<<-W zgG!?8>?Za+paVW)*lEvPUx$rh1to~ki8w|Hp+=uPuLS)6um3kEW^ziPobxj0LOg^C zhF1dSm4K;?(9@?8p)*Ci;0>)S%Zd+lbwPID$qC5;ACwS-|dui6#@iYeoVgWvm;R?yXsDx=|VXGduMPNrzr~)S=evki= zl~zU77J^p-uH%(}Z#FYoli-yL}Ox%yE4hXR^bv1rwr*Y+^+0A<3E3j{kpDO_Zi!V)slq*c!JCNc9xIqz5 zgG;2NtJ7Fp4{;dY13F28=#h|}BI>qwX?s(J%gNJ@bS-CxC@k3t#oyN6of8!sYT+Lf zn#n>sDu*DkAlMPB1BTvJVqj`?&%(jOEkj9nfGQa<-_qq6yuC;0X(|W}^tH1xa}t3b z1$?Z8uBcYMcxRhUTBkTSHP+eEKtBXDx7gbd^O}fV50HDNQEs zOkr_J3EA7)JLRrInY^()$;a~L{rh^+nOV8{L_k$gz+K-hwrzXc(U2MLVyJyb^PyW} zGNS;=&CNzwid)m&5c>92Yh{|3joyR1ckh`+#6c4&GczkIi(TJ|JpJqTma-TRvzHHU zUcaLs7#WwCl982}nT@}{m9prs-n7@}#s)YVKi9l=^SMuGR029+1l*o>mSuncR$7#q z7G!7o=JqHui@{tB|Uk#azH7ucfO8-+2R)|PI8ksv6+p8%CXYLI)!~1E#$E{6e9WDF1mS;NCPL#VQnvz_alU|G1Pr`%1fK7R!){I?zAP0+(bB zUAP{q0sb5R@9XeC_&-qxp<#gk(f_FjUHxzVkMv}7IyP$9EHir}Sf@YvKc)a>tI|f# zko=#slh_ZvZQ7vA6%u(vh35}@^&tzSvQx;`XYNQ0%1C#;vh#bBW;uI(j?4vicD1s# z*u>EAz{FAGXFZeC>x1kXUR=YC&MN`SWHq|;Cr?ouI&{d;;TxWtJ9)4G8%6qbhcRC^ zH#Y?Dm@*Mkq{BvRe`@OB0hv**h@UzTqf}#4q2`jYY9oiMY3N$Gc>DPU1ck-m(fRnS1g`{)#gSJ6CJJ0y{}l*DWLu6oZkjo5`q+^p$6P1@L0@5B4z1yA{*cPre8;Xf zQeQZA{Ajh2BbWKcr9dz;At5;}owLiEPF=izY0cCzYQrJKJLQSHARr7f-BB^I%q~+F z?S1B^ZgL<8CkKVXw4}a9T#6zbB=|BB0MX{3W)Dm= z>?_gdfk(fR0l-m)&B9-1VrGnjT|Bngr1V|}Zr#7GkMt@yrXyVVe^vTr(+sZ!%+1GL z$?T6ra4t7m#CnFzqMWYXejAD2vLlMt)tR}Y-NcGQ=-o5ttw#CE46iEzD1k2@eeo4-F0q zKxhLMb~ZyJfS{8B#0oH|8V*CC@lp=QuT~ph;AY`4@&`OM?p<<@{U+Jv(p4j zNe1lFDGn@(h?6SY>~Cp4;>6bal)#%r#kI<|mZD(Of;xle)vZd}Jz%2dF6NbhLmi(T zUANxcROimxHJes_bNuKf3ukxmXyKkVrTCTP2iiZ|u=c9TBD&KGqZm+CSO5 zbK|y+>nCYqY@th4}@ANTZdM!2jDm zypomXr-Vl(g#_9gyfD?iudVBw#twn<3vv1D4^<&{f}pUVkjRAOP=WOet!rnFT80w~ zemXKhiqD&OVlQmRI&_J3G7ZO2GI8k(xupp)hMKRo7fCk>ozqIM9c{ zVe25*fmZ_VgZUKW5E=^sx&h;B*4oZ zMZV5Xc7{eKrWWEF&?L#^ojskcE%lY9IdLI=0(Uo8Hy39UJyP_B&OjJLD1aCc5NY#Dz+SE< z`j4(0eg>Fb+WnZDH3L3{G_nI4nrW%fKK)SHqJ7a8c~VsC9}4mMJA z3#Q|XA@Xs3PF6-*azb2mRCutTzzvftrb)V#UITihc{$+qOXHP*hcCI41ev^Ytl+{T zka^MqUmLq?(uA)@4(>l-;DCPp2F^1SL+_~wQYd5;iE5&EESx)O!iXXL`wbk}uit<{ zqpp<^4_8qkGgoCFU$b(_qzR*7>))^6m;L(p=aqnYC173&7<(gvR+HEo8utT}?F6*| zJ4=*9sh7+I!KTK#q6e=8jIv$;LnQTJpy}$8*IRyJaKVi6ZV1NrUr#gPrHxa{o_0LY+ko!-kj-^ zCQO_(ZR&($P8~2 zf7dIK&B+aGmaN)-J*D#%lqZ=oBvLl8;{oaE2tTuF>xx;kr%d>2#^o4gC(L-B@G;_Y zm}^ZBZr`zF`7G#cj~zQ|+`i&2_8Gy0@yeauQuDLNH?LW}aQ>v(lc2;qYUC1cDRAUO z_(E^3Cq@7Aq3!Edt(`q--ds}Z9W#0kuLSH97z!XiJ}dgTh%6|}-`UHF*sX!1zQ^#mnk%se-DofXwXE z#t16EV+t`GjjK?|gsDl+mxv-b&xO|!>px}qN=iOg`X%?IEFZ4~%!r3@J7iJ{22f&$ z#%>}PHt^S z=Q0UW?@G{=D=PuaP zqb3za0rR;%XNOKtFTwxMx)AXTqY`8GS06wA*xgi|8185L@a&NTM^0W% zl2M?CJs2^}C_nu8(}%X&{McZD`Tf&J4jejh%CwpQ#`rk!;mbQdy#M9*)~d{KUk~HE zCl4JwaNyY86cmwF0HZ;(ZTBxf{qYZ_C?P=L^5WWYUJ0020tOTqo0w$GBh0@)`9EL- zjQ$fmMA=y+74~ob&!ommP`pek6JROJ#PiR=h}kOq*%vqfRXyYRrX~HqY0L}>n@{H6 z^N%x3aqOoKQR6>t|1$_~C*xab<*t zf#V15{ZBwhb<~y>CVHDZyzm3sRVSZ-prGJj86(1f{p0($?JZ(qdbrc`duNUv|KY?P zaP0X71_guU2ieu0x9?uJH;ZzU{A{1!JAL@b@zalO99%qo1IXUq-re)+Rfnt=033nY z!y9Lg96fQ_z{1|y!^=02(KjJ?+ubS==Oy^s>fOC~@`p1Q9~q+n(1TY3PL(uZ^+!D` zojKG~r4YnIiJ<@+0A2|gL=0?yg)Ka<1f1u5uX)GQm z2sl!U41SCwP!WWSv6=W}M-qY~6D52ycBf)yM_f-$719$$3g#hnpaDo|ppPPu>%(;* zq0I`imlWediv3xm{9j8W3KnXsh$tCHF=X#Gw<>E23sVyc>YJh1$drCbYPZ)eS7sX= zUcYMblI^?_Fs}r>=gj4s_q24L>OKeDo(QM#bN71RMq;v3g0v>yEd-LCUMGsRkt2$x*&8j`p_JCg$G${((V3b&}e4 z<*Q#mbSo8&;$mT1Y?#1_guEFQ{2?_g_f z>+FdRz^NdK)bX}WCK2UkBu9n#!0u#kWMl@JFE4LD)U6?jD(`A*t|>}Siwh6%7Pz{* zym(?@X6@kWDe#272^n;`vQdKgSYTtQuXX+6i9-+?*4TH>*xcI2-m$8>wk#&TT$mme>~3WIrV{Kt?e8jvs+Um%n_!93mjfN*1CS_)XC#VfB50(xtkB38(Z2rk`6D46~_m= zS?NBxcjMx@v!~CUJ#ymGZSChq=GOMi4)*=5ASW~ZCtA0!U%z_g;;GY@Z$EhQ!U#n~ zurndws^S%xNq+mTwgJJ^thn|zeIh+m;D9|8Pj@@|^G+ibZRh5-BHG!88X)GQy ze)O=xgN6L zA`(VGjLp1ibKh5JjH7Srfq8*TJE{)!`jP+`j|KrlIIjc@5V)wdz2p7wKfUkiXjMvO zja8zOyv+Due}RWzTz)wU!OPm)-~8kEpWlI@9=EWmx*YP~Ns+>sMV$WN0eI#fAB4Y2iKs zX9U^y*4E+aUGIMW$L~MB>29m9l|a+FFe@`PG2GW3MY?u~gZ<(=fBf?wzkPhwsmL#? zF0ZXF$^#cwh@Y#yjjgSvnU!x;*Sr7x=bsK=P1fbh_*fED25 zR#~kuBN5NNt+jiCSf*f5O0w60UKH2X6{f_;g!#Lh89jOYMAs?3K@L1Q!(8F=R#_#A z%cG)#ecbGA4WH|1-MpUHZmd@>1)hMX8RAIwHdFKsQ=!rc5yDV0j~+3V}ulyLC&j=BW%`cZ~?cc9ix^&?;%QxQ%t8XPuN5WK+S!i(X;EtVpwtv5A)4HXL7ccsH z`9X_pNj>|ZpkkB8D*->cc0yz8HuWv**R5E#biu4yGiFSmHhubnqqiQ(l-axzFc%mQ zI}k!FoUD=m1HzY$34oqMYFH1k^y87MMy8Hdf2{3=1qJ!+S*NEJOFON&9I%vfd6eTW zEn%|?UOYLM3>CNNRO05-a66t;4s4<2lS6JahAiwGmjUY`bG9bjkW1-*9U zSNnRuAMGWTN~N^EN?cV6w8$e@i4>gr$-DSl^ zG%YZtQ7#~djY<^R5j+VN7x(!;i$UOf2)qC4|GnO=>OkK4$$c?DpmF}++u{Ei|L2u} zjjUv^UpFPbyz$U2CL>Q+Ca$S(Kv@>{KD-hzuLKOLuVU5mj!FQQWI)jS@BYu$e>gch zV0EEw^S}AOYW=4)1aerQIs6~ie@qjIAXtF#X#(k6{<-BHRfYdI>wk4`e8BPpeE|Xh zAM_?$5;8a?a84sc6+#fh0;dGdD*^LLz^Jf8?wh5fDzn3#40IknePLi^YHkC3fwx~^ zFjc`~eI>pY#OF2Q64da;$1ovaAgE(waV^)Ou@O%kVry|3X}F>OCM`83B_;I}M<>hO z;lW4TOcazz?;!)23&jnro3z8H++01@W|p2qat;S@h=xAVi&EkZ?}F?^@kzOSj6x4J zxfS^pOQ|9Bh8;j);vgt4qNN!L29UfW3Sdbq?gc0|M|@B`km7pkf`QN|)dpX}e^btZ zvVokP>x7QI$vF0+6kCvme$O=;hz)@sP$Y-e2jT1M(c6Lk9$pC;cY~CIc_m<830Of) zB9y3S>1CR5(8!xUZ!nXv7{PH_htF+XmLHA=eklSz;_}*FJ99sHC0p>hYy#~Rs>#2p z!QFcs%`~)Y;FW+e;o`ngXe3wgO2DgTs0|rBXwaY`!1WAXGbUj(+vRYYj zoYtJVx1>rb1%$9;qoDGCaEP>p>S^ho-LPo3R%1&;GxT2RC{{uzlyor$@!Y&^&y3+C zMvc>IX=rRFU8X9;gWU8Xm34US{BHLADU(N!Qkxdth=~{J7hVb2)0^%;3qYmPhOoV} zr;QsuQf<_Rn~w~v>>ZumJbmcSBEX~-s;NczaK)r?UyT~I^VVZiTL(uM58q(2v-29- zTQ*9PbvDhKyyn(Z6Ki`%S17eWDZH-(3zB&yU~Dm{FoNv^Dys%@w-&2CZDr_AbD=*4 z0kr9E?9} zjXgWIY}|kO%%Q8s2vP!|Ie7R5kzL-D z>Mrm!*3s5}r1SK-o}S)I!zXtiTe}E+0?Cd?mRADq+dSY0mTQn?Q$hkt-n|z3&N(~% zBo$w*rwOK>{sWCHeg2Q1c_rZc5pi*eshP5t>hvH-tEYNd0oK}wwrtpbKz-ff3j$Nk zhrtmj<4=%DOXK{Ub39CAZ4GW8J$m89$_&O2Ai6T{(OF_{kH84;?;sbpO7Kk6xPD zI6^77S=yW>2naH|eeLSiYd3D)zJ2H3{aY7r=o&%i(~0cOEp>VMF(%LLEuKGnVPJ@6 zY+_V~xOmqwg9Ka$L&oYpVdbm#sGz($?$;nB{ z_?MK#>~KjO%o9TZGKVF?yzI=3^tAN!^t7~8AP-smO3OCoA6P1#R{}OcNE&RLn21eP93~C}{OEg|Zc#V@hTRe0_GB;;;=soJ4b_6npnxJ4p<2 zmn%A6b+tEFR2B=%Y6$uZs%63-q1K2^U>)z?y?>>URMiv}MntFNSFtKyvX+&R{q65R zc6N2jCAFm$74iNa!AZOlu%fNHB-ql-(=Q}4BezflbP#tmL+QE}#rqyvZF5by{!2&q zQUHVM8k!l5oMo3=0jO;I*xl08Sm1ce(g9%{WU`2JmF@lHzz+e>-u1q-xu?$0%F?p2 znFeONH_D?R6$pE0=ev&}YfW{wwZun`*TfTmJPYC6m91bU>g??P`R9%fQKX#(Jsd2l zLmg-ZQYwj9z4P_^w{M!GZOve(ohSB(eSd+1jqv)X51k1%CMQ(2LM2QXhLlS;}jA7oo&yf=0++n?em zwrWZn$_)0IGCS>Rxt%M1U~dNBo9e~cS-F%<2S%r_(Vs(5d&TpVC5Xa7kBIfUJ(%aE9&%oL6ztEkYOeuG>VHTns^ zr^FR$NL0gt%1T(OpJ9aXm(VdHyA&8fmH*Pf^rq-@`0xCm{yfe~H-Y=Zdi|R!hUs$l zC$}^rLaAw_vx{Ck`9ChFPqv}Hq9{=i;OQMJtV4cG_44p=UJ2ON#=#>jEguD!I5f^k ziZ7_H3HM8NchJ6iK-ban_&sA+@8|*%^zOK`;nMsfNwQCThNtc2eMd}fojf8FQiW9@ ze&!m^kj0|9!f=luuWOsOYMJE~gRZ59gh1JYhwqIIHC0Wy;RQh^2bP|EMo$l6$hb$7 zK56vB^qdix?{h}|;B_!3P;!WU_^c0mgzKAXuG`LHB*Pi%5qWUox=yoj;p8xgUWOL(ScV2#u0u=5$E4*Mqs!XocdAC zKA`~MU8TGdFrK=~G|y*u&mBE-^5CKE->q7`VAhWaK1DCG!HL+oJh93B}SCW4f2(=We3%J)^f98$bliQ$1hp6e(#Fa%VOn%pkiV~=uZzB(fuWJHsRbTB#7~k2Y;9ZWD})&_5uqU=0dD5T$O4*} znp=_%CA5ZF{Dtz~GRP1m#RO5Aw2d{&q%BE_T8s`XYNztw;==5d*ziDa4_9X=RC1TF zr(ax6$99k(7nKzkWM?GDMF#tMdwF`eyA`YaLyVf9N>m3GL*62#2Y)zs;o{YsceHpV z;3A^?WVt*n?c_>m6bTSMnIO!?&?KR1C0hTh3B62Q6PR;pb3^tbva?oIU*dCHW1sqQ zZC@dH6e#%tkaVBDCQonUq!EJ#4j9m%R|5Xxi!b{1AN*}lL|7=IuZp7EcWvAst)4!5 z;FtZr_}kxL{Q_h?gJ#?Kc=;3+msVt-*>n8Twlz~m4k7b@urB@k51yoLZ);anQdXX$ zv0>3S%chJT-v7(Lp>g&Byb>_41dNnv7t>KCy-Fl*>1#V_ZSqRM9V9)Aj3|_NyE=R7 z>`omxxMtnrxwB_YnL2U$v5FpE2{<}ABP%DDlz6+kdJ0~g{ASsz`Ctc_I&JQ{GkSJ{ z;Aj9KGBY`QM~5`<-1gfVHD=t{ap*F3 z+{hgQ|M2M8ghaaWoi%sQ?p(Wg*5oM@$A2{*UB-@{e&4|>I5IXqfqiUUd6y1vpFe*F z?BmCOHFos4`DYDmJp;lbqM_tVpKxc+ql0r6ES@%D!i2BJPhGbE=5upLPrqPD{gM@r zO?!*aiS-NS&z|-5T8*39x`tMc9=sAT)g1OJeX`R_aydNn01VM_AJrs636w0XF@GbU zyux~b`HbcO_A`yoD*>CGIl5`pig~jq%=-JtQKQsGEaH`bjjSA8-93F+SlQm% z&MN`KWtd0tO2E7l@czSB{i|4Bqyn?3qU~MRn~$OxSF;z7E+5^yXYYRX181!%HH9QD zn~gga-5tVU`xn}p=ML@Jy?5W9eWwi4GcyqYWRqR0RLE-b{48}J-adVB*RDN#cB`M( ziwB4_B{hxNrA<|3*D8&~b zGmt7;Isd0P29PB}hR~%be6A}=kMeVI4k^RqOtC*H`SvM+x3(#23X*~yp5M4|$FGrB z0)84E6&(X5YVzN<*RNl-)};kFS{Z7dJ*d7{U48G#hrR)LiXj9DyRxTC-dvO9Wn=X8 z=BfR=cB}6_c=EoRz{d|rb!KmsH`NwJx|-=~T|S{s^Ml5z2X>C`)Pvb&O%ic-n6rV- ztxL!E?A*0W{qX5W;P8jipASAfg;FM|DN74?d8T#!!tuSkcJ5L?a{h@SIykutXv3;h zG>8jRf*o}4UcY>DA10u^$FAtSFt)IDaPc63pF%0E$V>>f*VDRr?exBFJJb)Hy7lO} z5!u}V@WTj-y3`0i#}}H{uAe)wYwv**SMNW5X<`MX5ZDQ$C|8sQyIVciym9l)p@SMH zZ#;bZ0x|}qBtrNmdM;%pfp(^b`j0N3Id|i!K6pYPG~(dw>P8PT;h&@}5*(xzXGRA2 zczX#vJUr0^-aftr@TV@ol-Jji5_n!(LUdGQWJE+n2%-E{ZU7(^DuX#C@SKd4BoYgc zC!jxhi3;8a&>yt7kQgrnh(9eYl~)2rWrNE9<+6$-51ot0j~~Bak_Vzs%pf2X0-%wb z9uy7fK~DD$Z~k`v%*m5inKdYxop6|J_E6Mkgt?pC*tdD*ylE50&scP)M$TQ&DS^wI z^3y}z^v`cu_wBriV@Hf0KmSQH24F>wG;zsA`9-<@Iwv-*Ts-5ekt2qGHEl(Zf^Duja%N9%-r#5`}m}#>vR)X&ZIISvr{_FB`8#=wN?pd{X;p8zRhK*1g zJ#k}rC5~coN<#j_D*>CIIk0Z=%u&1&Fiz_+%a9B}N^(jVVHk^AvVz|fLP z%BdyK2Gt=^ApvP9yoc$dJ2y_9xRcINMaku@?Oi=T|Nh6H?|Qo0B{`99X4c+B&*=2)w*~XmMwS32j(Ige4`piJ`u1?e?YcCJ+G*8YQq$=g=sr5|!qsC&WfY zgolTRg(1cd2XvXm2grOOs}2ZHNntJ_Y!c$*Ed>>N z=td--WGCVt2@N0u3ovjxIhpB@a!yJFwg}+^{!vb^K|<0Jpa~~MgG@+n*C14g|Mu(Tw@k+qQ)@|Ckdmpa^%qs!s0cJ#i$X+F3 zMT^JXTW8PhSig4Br14|NOxh7!3+z&#U9O1Iu`|4SV&CRf^Tv-FK4R>ot+`bdv>D@c zRup1eOJAe=>O0phoH1_Xh+!kgPtmKvM2@{0HYf-Yk`0}*+^%oi^zHnqqtu2E8$Nv8 z!9s|^K#K`>>`?>FqUv4m9@w^W=7g~$hYuMsV#KPjT$K zd~9@71a=8wVKuclj>k<#vKo&jj%~0*$jeGg#ttDiIyx$<2F&bKdQ1Oc#{gw`YIC#F zv5AO}i-Dc%K=D7&v4GEl+`mYO>K0IaBqk=1kSZQ~R`yGKTI@;5URXd>#MnTfFaWsP zTD&*lYnjwjF$-kC-8`#m%JW0hTk7(oB6uZWs?*L)iA5Pu5GDsdUq3%TlHp`Nh!QuV{}w`` z6REqXh_JBGkl^4TDtc2jmJuo}$j{HsOifCl83gkO>|6(0nah~YCwgFU=g5Gf$j%zF zQRQV=c<~-ky_Z9ads0Gt9BforhdLHo6p>X1a6UgTF9+EpN|=pOH}pns zidOZ} z@Jhg3A{Dt(I=({#T?Jwy{FZ|)GVC$eN7aGIgu$Z@ydiSH3~LB@0YQsD+3RY_3KVfy zPj^>`tf{IvH@{p0B6fNKTu*lB1a`kg`n>~MSH*?tsVNDW70rMnuv3OAg|egL&Bq_# zbhbep5DDI*{LG}t(CFMM1bn;_Fs}qG=aqorOmxJd@;dTd;2L-(U|tEBR{~Dvm4Jzr zO-TiFm?HXS32?u&a)zb`fUh8I+0N>hRI58pBsez3BF!rSCo7BW0t8R5Uq63%-~N4D zwya&cc+P^UQ&CAbea5WW^PWX2Q{7Snp5421Zs*Ru+qZ35w_@qyMe}A(pEhL*y3D*R zP~`ikhnwBKes;~q-P`u=+PHS*(r@O^o<4cvq$$&<&ARNUsPK(VcGkRcamTLx2lnmR zyy4rW3+GHnwa=s}(`I~q&rDut5gF{Ld-K$R9eWP!-?jVtV7=C+k3V zT+(K)8Q|^JkQ!auASF;E2Nhv}6GbavKQ#$fF1!*juLR610W;TRu0y*EMzPBLpH~9r zm4F#UA2VuGWq!QBi}j1ECr_Qf`B3{I)d1MLc=|AaKAxQx(A1V?#Rs~&xVgGGIk}*K z!9OS@EP_~W3FA-sCg4h|$_sPAu?2V^Yw_`nKNVYAIszozX${GIg0B_FYoOUrOCyq0 zuF-Qzb-9QFC|OvDY++7L4#59t45vkllBxre{e&OEaR9^rqZo!7BL}7d7`P3|ew_5N z->Xvi>;Q|A!BW1Fj`53&7_%>_2D0NVS`}5-BgcK(J5{3q0o52d@2hVp zSv%?x)u@nw7`ME0sl!Hu4s<9`xqs#jqkmuj>3ZT)uCHs%UAy;z9uUH`n5(YGVCcZ< zoXH&KK6>#&USF)>2t)eb0b^rsY%DoE$K2p~-NSXaf~#7k2p~B!XsDOrIJrsedv?u2 zt(VW!Z{9z;b*ElhDTo$9_(zP-5-2QXnVem@WX?A2XO+Qz57n2=TX)YREe+*P2JQiov5BZcPR*d{x4jcoQ{6q?J#Cd~VeY6^ zHgyk*ic3sNN>0xtdmAYSbaw&h-`*&U@wB#d3ycIoNpe~yq_zRzZzX#d#^^#Osi8D4 zJ0m>(JS*z|jt&%z%Sq4J9DNDFYH2>eGn1F+D1@;X~tE?fiLE!#s-x6{+kkLb2OY?g)}sS*iLhdZ{|lgUt;9dsS7hz4tpk*R&B}JBj~d zCo7q`oy6zYvwa3xX>?ke*Z|diO5hZiQlotgTe&a@0Ta33e*P6l->e6L;#79T3!I;_ z1RJ{pcm>u$RieY0$$6;@?Ugulpa1rC_!J?t@jv~Z`9H4&tTuAwGT*oq0(d1Pr=_#$ z30jq>F5bViX6hKV;Uh+Knv&)o4d!Kn|E*U*W4fO{jMjx`WclCq{ z9cZ>>$WSVziZXq}GwQR(j#L{qbhw(@nrEi=F795vMgCahqzyrPSIr(jdc^Rd!$+un z^FYts-r3#D*FON`vocCW-P3R9&73#}7Yli9cv}|CG0@T6Kt7eV}L*6m4IpPlFMppU481B^D^S>ubkU%(1x@<4gqMM zqlCSzu~8Hy^t6djwSIW^*f#YSf`EFY32GsiR0Df+ZfsDny^Wrwb#8?I^#>Xo40N=U z#5nCC| z+Wo}C;)PFER!&|)p;XqG8)<(0huhY{j>eDA9eQ^E+7ElYV$F4UCE$xXb`Gux4(iiG ztn+ewtqh|)EOd|T|8C`Xr%#=I{6gokiKV?WYA7pW1eU>(o;K%o9bTN+y>aD=)o0G1 zJaFcLuA#Xt?2@z~7YiF-`+J8@o<6>9$F^;o*Y7!dZ1;)#PYuoOpyVs96{fib1wQAM zfU(b|^dB|sjXD2Ec*y0iVPGLRz9Ob;YLasPkKa)bYJlx9?p}02Ml}mF2-Z*9K2xJb zmd?Hr|6|2%k%>+rPYpYbNte>EDNdvW34ZPjzoTS!_HGb>Qr-mCmKp*BwKv6oAFE(? z7CO=ru3%iHP6ux_x3ybbys3=@9Zg`kj;>TRRS2tFJH>{pMhDjGKdotRqd61v8(FdC z7m1!(RMjW=>C7Fwa^~$$C9eeh^+V8Txq8ZYC16?Y_0b!7C18ppD80eL0;<*JDu%Jz zQv|{qoQcCSq4M^VogxtUG?$p;m4FpZ#gaO4s=(FQgQy~7^feR1JU#s*f&xOL(}gI_qGB*nS-Je}yJksiRasK7gG9fusVEsbt+L@!4;lzTUp49^0(JEhe7kkej(Avj^yGKWOY2MMf zdE^%dMBm~hU4LtfXl$**CMNpch-HbbLGU5ox8U#oHk*`cKyVB zY_5eQK}xX)uLKOWPc+I6S65b)#2H*WynXYMuZF4})ZvwYu>s|3K3NRQD*+b~_YPGn z^Gd))`B_;^h_#~br=Na&|Mpd+3 zR9d~=U7cNGOUMq5W06?$^DjSrc>Aimt);poH7qK~+tby_*(0wAO39>wT;2A^&p*9? z-QB5d5D7CA!h?J~-JD&VV)8-s2y<2K>)(I*>D`;2cDV#vvr)kT-jM2bbnqp)-$O)6XC9^3ob{VOn^QpTNV_$==QlOq9G5Feq>&NW*rtE1T*>#aVG7&_Q;0cXhMY zeWq_{WQt8ytpr@1m<1YZ@ZrXU_4a}n9&n}RI8j?8NQKNFp~;_fjB|^ayNJnv8j+0DfjW*HqDKlz{O>*lVvWiL`xs@6xHmd$w*lZk$m=7%tcgRYIbbLLXCu zXAiHR)KK5Le$~qT&SEj4tc3-f&ZxL3%+t~6<^7u{HFj@D4td3f7iGlRQkc)|yb`dT zwb>J13HaE7?VHwb*uX0R1946idxb?s;tCZ>5$E0HE^u@e4Pc>=R|3Wz5f^A`-95c$ z{fx20`uF>yU%xNE`0~pEgTGEJDag;o#8@E?yK8EHYy0Z?YJ>Xs|MH8!efh-~U-lm| z>X|SzBaNn8v9q?b#{-R3(?<^M*RS8-zM$*-4IH`;Y*I00!2VZQ7`q4QsV^Eoyx)K? za4r7vO28vm?AQ3=!c`$EX@rHsRE^p5CQTSKV(`F0g9i^9GGzG3SzFZ)oj895OBBj+ z@(U9$d_Dc|<3OQ0Y}n9YBSwvzxqjz&%H?jTt>^%-Hc0r+vL? z_kkn45-^VMx_BjE+=8CYxGQ`1ZCJcy!K~?vZYOoT>fn`tW7D#83ksPM@9UyFXZJ5% zx^V8yY2XK#JaNhjE!O~E2^cknl}w|SzCbaP;OnRu0HBziZZn`B>D;oWvSX_PP$g$a zty(?l`C+Hl2Y%O(Q~3f2}{IrG=8hc z)7u9(ZCSH?$&|_CMvWXfdfd2?`-Q9qvrlCA zJE;!Sjh45v545EjQzQJ7weDB1UiEa)4I|hQ>`3`GlK-*R^F~Vkuk7h*MF)Z#(flA$ znY&(#td4!R;_Ky`uOxSL5ex|^VMx2wbM~J0uv6ip#T!@sl!Ojr{veA2L z4EJr>y6Btf6DRzA^yuMZ)P)^@7^4!Tx{_YIOUf$&KiD^G-q%wg6h3b3q(wWQH6o0or3Xe5W>lqCDPTIyUrdGxUQwoO}h9XRvE z+}0KLkZ_8r0Rb)bwl#Tt_0*AL2X<`RrGEUbk(HynPe2fgpb&7=Y@cbZr*-Ab@#7>E z3bVC?o4_{!G$1sQQwLeDqn^&Ahc`|fId$#1wIf8vh$SR6j7mfZXUZ!96Gj72UG5;2 zY67TQ;4k<`VOlz;I163?yxL*7(uLLY`d2#LdfddB*Xj~Lf4Jwkb zxLfk>UqAlxhpZ^t+r|3Hr6c?HAJ}`~v}tB00^F=Dic~t@zxnvCAv?^~#_YlQ!+ZDb zSKs#ouLR610q5mrXToJa13)n6<-Vvql)Ue=-+uNZ=ir~4KlwlF#ya#}Kt`^S|5N*m zos3j2(Ch!y0n^7P{|CmuxxT)!McK{~0nkneg$gw5(51W*@X-y6=T03pZ1CU_qeqUJ zeI+F&B{eObiiBJ9a}%p8^mnhGGwJV?0v|C-ZPd)ee$e@jh>9kAZhUf*;K`1~(-%{VP(wVp|A1j?Bgf9zX$~n(H+Od|j*40z zUGys zAY@B&vdD3I<8f4xpjMdp1FDdFM2!!U%$)sG4^{#M%|PlvcC72HsaAAGGQvjawQ~zJ zUWL63>WAr_5CsZdFRH+Gy@3JkwUBnB4)hk7z5J6|sjMw5OidCtNWq{*$x&Qi(P!tC zfb0ErF6~^kc<$dvjU1ykdY*XQ=mDvV|*RNW< zWcx+Cl4hc4!}U0%QP~wT-^=^dH?3H{WcIXK8(;8Bz;hnhIe_cd3;dVZX9=vsT&=FH znLl&l$e}|A4;?Xb;(~id#-`@hc33*Vgd3ovb?VgK*^?%Z8x91`&=I32FT1V#($Lt% z6puuMq$%^>`C~g)PM`3V+K@p52M!%EZt~h2nvY)?7@IK0sfK#9i^q4anl}Mk1aufW zYQoH2S8k$%A?#QVB+WT@_itJ@W8znkNgg&r?e8;|9KCS;?xSZfjcEC*gA`}@$@MGe zPoFSu3=TLaFIc92_VR5l9o^?I>4BpIGf9BPn$_RTpTF?yMc;1Nz$*dsO2C-sC_~08 z0ZW>t?Z5x>;mxbIhI&zPW@40&tAnkLr8{WE;u8|!JiHPxEl!{cud6D}Pmc@r^YUVO31D?;)(-dVeJDiYp05bpu z{RGcP4>kns3^aiORB;d~f#Z6Woi<4Xyx^694<9~wVDDL1%Slq;%Gw{MSXo8 z?iNWC6FiFfH$pdfHs}8c7KuBA+#8b!mGB6K4GmoRD)WDuAz0pz%8}sW=zwGaH$5=_ zS4|=0{}m*;BrGJRAI|sbE1P3jYC}{G$#nQXUC-=T2*GrQO%BNY=rt&Q;FW-VYwAP> z!&m7!*&LofYRr%U$nf|35?T7eUru;xY-VNWSXsv_0poc_Kk}ol(qXWA9r5Cc1SdZ!V#1qE#V7+=z5P2nF|D3#zcfbDQ zj}Nb3bty4xX^V8D8eFV;q_V)JH*5T=0?|%Nr??1ljZmX}A;6W|S%1liR_jPx2 zaImv7xAKec{PE9!{Pyuxry{?oy1cf!C@(W9HpI`>-p1C}(#*;?s_Wf<{`1d|kU_4h zp~OyMQFcODpr-@Iwzjme@eb)xLJ-)UkbsGpz%t`k)wKOwxYVZ2-kM@#E zrBYg7C9bL_k>#4oyn>9lU}py#BMX<-u2-+$ceZu3cU6|xmy{RRiiA0$^vpm%ft!oD zk-di;n#QfKS~{f?VR>D7K1%K*qY@KiyqrBeER5{j1af6(SNH3;9nGTLrqZn3qKw3- zu=q$@D=!ZVQ>d8;cqL$737DRGYHW+asx0Z?xvrL10_K%~k&VI<&MN^EP=YeusB&hf z6-dFeV>p&{qZA!w*(lVZtQ%XM(G7`kT7Nm14?4idIC_D;<(nXYNZjEou@6Ato_MHL z1K|2k>o;|PgMPMrtJZ(!xqaRX+&|Ix_q~JvkM+O0*Us{T|L*@S2^rAYK_iU-)~E+D zEHp5Zgsh{6nPlgcfHy9gzvAe9hrpPm^sGdGEA1PHS1+78ZsOvL+A>ju-P!Fs_ikCY zYQ^S5yHA|GaQW6bjqjH%S~zw5wAB~yx5!g0uWtVSkjDN!yS8lGb7cR)ecLxJTfTVq z)Jaq4?7aM-S=#LV@cfCBC(iEJrM`ai`fcB@S+ee%MUy8^nm%XE@jH*PdNqdKS3k37 z$LbC0o4#GS>brR}W=@~_)mPINu0DKITelfiWX-uRe%QQb>DCns7A^mJ?&Jy6rjDOH zZ`qcUx3r%;!^eqqa&@rQ{w!Za()3 zjY>d=%*-szz7X>5?(TX2R$7#q7G!7o= z2)jbw63Ht8<5ZjCTVPIjC18SH!$CQvU|RNBV<9*f!mB#`*YzK<1g`{KTMdK~_>{{i z_)s7ek!?BVxM}9J>0?KZ9CM+hsJOT=FDEy@pn&{WA(gfHj$Lh}j-Y!quLL}5)XrOv zO>G^(#Niu^$BxlC(Z&w-Wyw05W=&pm>#2#gy`w9XT4Lz2RCOTdZIB2y#H9tfS(zCWzCZ^?^=zv;vND(Yn#ywGm`0=g|ECZa<1_=( zMhWgJl-hv}o8srbG3dAe0|2vz{P%xR`ek7@uLO)6j$7W9{H&F69kLt9b{KS<<+3KC z=xS@pJesUznhky17fN6#ltodA^44~*r@DFy#)VkNZa<>X=Bm=Vw$7Fef1A=K8`Ij3 z4moxhNQkhZc_rW!FN?=V)sHw>=|1>w)y8$Jj~u)084?+jkc{nAb)<7vqO+0CmTk9m zZfKoYy!_jx^EI>|-+U4n8556yEjPf@+1>2nxm7;CrkB)Luidt3&IVs!Yu%%I;gQk! zEDJr2wQlK~Kh!Glv3;tscjJ}~>(507+ge_^8ypsa&!;-UPSZTe%gQs$$IkHN;aywT zA30_0W^2YP0mmmKG8sL3l#XjDSLp48fHu@(yB@$yUN<+v`oii2Ub3A zm*Z;6L)KKYs~Z~1g0d~128J8m+`WIzMqPKGntI@bi2)s#OABMXJPeF7Ugt(cN>NF_k&8?>#w60objwyo=9O>v<8lf64PZrix-Vsenh%QF`pUEFd1oAU$o z?DE3x-HZM0ULM@Nef#<&XRM%=%qszhg-0?PEyOkw;4jOI;r!_-NeOW=z!#vxJ1#yR z8B8>axiLYAQC2I=%}P&8Nls2m#=oQ_W@mgkD%y?|becx6d!!YW)>hgH zBKyS}uLMld2aZc987Qr&7W$-U9kL3(IA`hrwW-VZUKUAE=PZJBiA<`fH`u(^{Hf-G zfn$v|=ZsYwF>%YBp%z7@C}1cqEtATW5qqbNT)5}t^kFkrtzLX+(WvpW*6m-q;zdeI zI<&w+FL!zVxB=t7TA{w+>u<-+UaIzGzp-a;PaHDeCoC*FF-1`)*g2;Eyy0pu*Q*T} zFlyZYLK`xF&8R`kJOY9sT_|rcpV$9yhAXC>9{$CD{KurZZ z6=nX$ei!o&O_}t!|J84f`u2$f2Yxkj_?W*B8l`Pv&t3__odqBTG0JuLMj}3^l^`t4ex4Q~Lc2(;s6fX8<2|fGDbHVlxnS zMu;Ra0IvkhD*<<+b^rWxM~5iV&H|4W9m!A}MoSS8JxWHM{QCXdH~)vdw~mkM>e_}Y zxP=IT;)UYHt$179BS1nE2<`-u5Huvj-QC^YT_>5iXX2TOCrFL9_ucz>zW05=3=KArQowlo$8W=Fj+SiyqiyRG z1qmteOu#%7FwX?+(P>w*v& zzIU`YH+B}smPS}AZPa*)dJYI_5vRNi7I4}H5CP8%7X)2Ap?n*ileiSd$>LX}Kw7Px zEh!0!`YNgya3#A6HcYj$BqW_SNk@LZ$DOn1*l)~;p&%8LLPtYQUA*=gWs>7zwhZ*p zwIrl}O>J$(`l?Fwy4hudWFze^1S7q%$@k z#mNwlc8D6YV}IC!-`Ly2bfC$^Eco5pn0ssYOZuJ5Rk5Gw7glNm=dqzGFRw`Yw$nGJ zfc=xnakd1?jj)6|&p+zWZxCYxr2-jhD`by+@`Y9LNSobxCSaZk_<+VEYnO13`~Vfoeh7dfn9smCfa$@)U1;7PgXaMMS}9FH zfFLBUsU-vv8XN!s<^{o8Q2|vVB2Y$5pp+9qH#K$Ob1)-if?~+485u7`jjbJ`=2~H? zu%@AoAV5IY!sKk$x+XEmx4K*FOLLOqGb`#b14T>oE2}sz-!_z(j0}of>x6l!k%1vO z#PcaE!Ru#KpS7L8zyI|usCHZHO7qjAg8h7xfC0cW0f&UtHHt)?;4%K?&B%bby-}E* z92p8CV-OL$I(zu}cw=d4?fCsYc!-B2o$U?983_@f+Vwz_v8#iNr>6&aid#Eg|NaZ! z+`g{1+LFw8@ECi$ySur$IoLb7xYa}6)%y!*l0ntmTvdb?;{ac84|izTSlie+ki1ho z^zQ9wzof0PDnB_YEWpRh-QC08+05M1%C^2)Bx)B+K*fd+zoDuqB`O4MI$qwM9+pPL zLwOQv5+Rtr=P!0yl& zDAGSd@!>!`cqU+4KNO!pt|d?ytZaA#^Kw!md>oAQ zZk{{2XB!B+Hf-3mO)0w)3mnPo%X5p0V?1n)wY5}^{jhTj$v1A>?NE+n4*{ao3krl~ z34XTE?rNSo_QSSK8zJAYY5Ser%=9#TAa!*`CBmvSFH^mnXH*aG*|KTFdYWLXS{!=O zNM4(fSu9Ajd8U0)`P2bSzkdCCsBAm#7Zn*v%WF*%Fu~pJ^lxb4^6eW*x*jj!pd~1= ztEy}3YoY^qCg8Hr=NEYVlpRqra`SVP`<)w^iyxCskiqdIR&Ci$wCSOL>J5l~s z9UP@nX5Ty$@aApn=E%y(jQ{crjL*OL0!+v<%dXtl(PxfjmeTgk8@`!8f6nADzx?7$ z$iEyndFssXwXWXLCU3L6${Eek%a$&jD?0)C3Cu8V;?(I2PN`qGep^@$msKdZwqfJa z`9Kk!JPusR$0Wjv#7#IMDC5yx&6G$P~krATVTWt}^GXWFeB-sobFdSq$D49VCdkPaG7|SyOGuB-xS22BK%%`YJ zptuaZSG3%4JjYTcrhuw4z7}ta8nl_~Vwt`R(7vJdE$l!p0;IOFJ z1S;{(;^qerFYRZY?K~4OQXNz<#PSTl$c5#Q&9V0f8$P9BKkWY$F@^2Ho`|4BI;dt{ zszHesvQ5ZgukZ#)1#_@T*c;Bt@#!$HLRxHuMUYLvv}yi6`jFswkPcX<&CfFd`x`w_+4sY?_3IYQQ;?kro~~Ik$BIgdi;Iei@#c(nJ6OC@ z+p}xOiY4=4{h;}hlapH-kdl&?mXQVdNUxcmj_RhZ8x}8^BL^CCSvioAuW}6xi;PJ~ z!h1jb+UESF9S7F|dPshjyaH6@6=e1~xcP-f#>5XpVN~?s;-Q^um&}_#m(~Y41$o&8 zH%uM9f+C}1Xz?9>TXIF^z{(Y0&;LqM5sL$O%2Xd2+q?UMigRd~KE9D6o(Y)U+LRm!kO5j%(940P11kkBGpMb_7h(!!LSQ|$HJ0dQCM4-bk(rHNslCc4*tx_sTGji5=T*W=lG zM@9#Fn(`z3T}`#MR8`Mh@x}B^-intbmJGdl`?{wpJ;2TO#qIMdr_Wxvo86C>K-fHpX1~%G5^{rzMM&WvawcRIUQm)MP(1}*`N95)6|%RVvw!kq@K=x7 zzqqGMRGFI);^`4p-ADFMeZ*Kw$Lzm1&-;I;(LrLrh*4+P%pB`+ocVkKYDb1j&*9HhLG%Dyf{;v}&RPV|*OATr%|AuYdjB z-}&BrLq%ChN$o)vn#d@fO7=hc*B}4+ufDppFrEqcy2h!)hmW3AyYcjug{_mT zr!Q(t(f-j>o1YfxVx)Wb)`e3C51&xdy!XV=oa8-Ht0`aXYC z@0l^r1k5u5OYOh6H7~};^3JIrx2;?}cg~Wv+RYNIJ0KcvsgKx2&8u zOGbXqs^@?)|3qFVC@&6udj9)u>z2%xm6vsYsUMTG?g`I6oN z0BQd9U;q98{OjF_q@g&TX9DJ#fEk4j&jgI(rfLF##OUpbcydYmva*Wman++rHWh6| zMNDizoLtf!Y~x|!@$AtP%^kb8FI}OQ)%HP-^N6@N-8w(s!^za(#g)y=zn;5z#p$vZ zbO3YLBbd+<=wF%??_mAn+J)WA=Fgt9V3~!8l`>YLAibxjx6~u7I?cn_L`O?$>(V)h zfz3TzjtXy7Q9zC+m{iZqzG7dqS65H&Ts~JpRzX&NSCUXrQe0Gsg9vKpBcp`db^@=U-~B#w$co(Y&|0+vzanSdXINZ802kY0>P4qqDb)AEWk!rZJ)jg5?n2iex% z!4ZJBEvP~Uja_p?Lt|A1D$Jw66zc1P0vj6C?}{0q(%gg+TNHg2W~C&=Mn^?PgolL& z2O-VRW@rO+7-DZV1mjVVospWH7#|xG9UaBw45L@nM5VeQx5FGoT&sUVd@Kc#L|neE z31#xE{0=owrA7HUSt#U5rjr3$8t7%9I1hVHZ4F1E35{Ho`%u3FlLB`Nv zLZP4#8sj+*K3tA~a}&v_B6y6(4_Y*{ba24ER1r*t{amkra7;+wnF1>r{y4#>a*9B* zLkymRYKW=Gn3GFi1$_g;8X}a$63&L4@){4s|(V@ zL(=LR>VWA$QCi#ptpMjA{^QTz-VFD&HVCsaLcF{pE71Xl%LT=_aoW4YgMa?{{@v?= zuBHYQ;fHy;yZINDLs(dlkHcX{x8(i5-v9b;1Y}&brKm>sM18$WQej?BR#qnN51t7a z0bz+)R8f?b5+5HG<^u*a2PYR-cTaC$KYZA&T@;h%nSjYZz{3A4fioo${!xUH#n z=FFK>+VOz-jE|?+C#>(4v{x2K`?{ML>D;}f&NBfUo0ytgTHD$?Is@pX5k$Y(8)?BW zEy_xy{U6n}7(5ejIhcBBU|VjbvIWX&!AZdeJmvwAlpd$eoAh`l;3+c}-Y9Lx`oa7I z=6|$wWL-V6Z`B&42PTi7GG(&Nk?aP>LtIkK{Es%F-r1{1*DRbPKX>M&iIXQym)Rax zoKGN5StM_1ad>+F!qL@pXUor;IeGHLiIe4~C>Uj?B!h{HmXDrt18XOpW1GMIN=9bN z#EBCoPMkJl-0tXzsOXp&h)W$_82X3n99_6{*5v76`h|SblnJXG+`aq)c_!dS+|I1; z1s|87I4>4;#|!$0mFxq4hl(8)`VvQ#yO*ZaOl^+|M>Ow zP=8;KxTCSIvLrt>GSuHUBt=lu2-vp4!FT`l_n*Iv4ECW$qO+-{th6K}J~GhP$HOxy zrJ_c)Gf|y6{ZE{-dL#?|%Pf6lb6M%5p(r zR%%>SxIdbN-CUg=UA=q*h=SnVyHRmVeWg$!D9Fr6iir#h3h?uB0|udgUFY!79|m%-1@>YUntg9ulsH!YGIX*5jI@rz0((tMFl}i`3cqU+F z6`l#0X99*{^Gv`z6R?)L^08wFcJ0`)Y5lrYE0!%?y7ZgntM+T%dkUJN98=xf8fQ)& zI{3rRecx^VcFmfV%aQ3@v;GIo+mBw*j=(bk(}qR>Wo%EP$}(xLud0%u*SH}qCSsxf zX2kW_QM`5{+)j)SDpbw%k%tdE7}d!BQ-QS)NyGglM~UtS4Jv&5|6~HBCy^@P(8}ct z#+dwQV`ndd%9%fT1)s<{g-<3xFrH@u=9z#AHgsfU2sd$aL1lSyMpA^ki%*z`Eg~5H zK_PGs`-f@t_H;DYR^)}cxp@ai2D-a>`v;*@FcxVY2|4jY@ZUR|>dFvQ%}9y`OA^r} zrKYA)vk08H0rZu?!S4i!VpVx5N;3eZod*v;7soPce`76cm;p{JC_uR65lT1;3p?ov z*g#u}7+xc}wKy2E0uV%yQJ=_MSTX=Kvat3%pe(XfJtgW;pGXGE1|ZYvR1LQkVFye> zrT#c*vjhg&7Np<{AUTp?OrfHjmIZ<^|D>S4QV_kqOq=v1R z47{HD{7@r9TQizjSwkOjU~^k{15wx@{9I8niC!G3U?L38Q(^l#7suzBfGLCsn1aF4 zqJ-pV+t8%weCE>OgF(em8$~F4(U=UD;Yw30^M|&szTSC#cpKphvZ5opoG_MN3;nG8 zqr-!p?XBJGz;+5xjjAh}@RpN&c)&4dxV|_$*~8A%Bua$5JkkS%AjqyC9D1E^UOd`c zR~+YMW$?tl1mLD9P!a-QmE^cw;w40ape7^8&R9>+C^5f)SOjTs*N@ga4g5URo*(OJ z_E=j--#a~%L4Xt&7s4$CN%ClW^v`el8*&01jUGLC@X$Ig1)NCv`2__9?0TLFn6M0J z4C1o|-91)=W-6D%=R=Sd2oe)a1%N8X&_gb$7JP44p@G{9Uq_Pj+=yGt_;qKxTJ_y(kWVyH_8OI&P*Ad~C4m?Ur1&Og>01KH4 z3vrT=u1@5=j}2OB=+wZ{cudYMVst`hI&3Y10x2>1hxLOy25`p>PKztSXV1>P1nCsG zmU?*qClff&1gvnQ4keBRQw-!~vOnAzL;32O)@th)DaxWpaBEC%VR3Om9?t|E1Ex>d z1KnBJ1aZfwKiau)&aNlc9yt4lM<=8bk)d>NV@;cFEhQ0tk&#iL*h)&x%*iWYhgYf5 z;EtsovI&K_rNB@rE&+lbZSMFVnWm+hDv-SU!}Uxm26Win zi}$)&1`tosMoCDzo}?0Si?||1clnBYJ$*fB@h5pZ{u>kb3`8608ffiZyG*yEyB)b; za_8_qK+Yi(N&J31cznqW897DW?)Hu@;$>^3{hel)^oWN7j_h2vYT*KTxtWU-JLo3k znSee0`~vCyXAWpjPkYSCWs4Q%WoOFmz5B$}-o?$s+b@XTe^?+s@~+Nqq5jtSinDnp zV3fLx5ND)8)-pDzp&0ii1^EzzBSesoazIv)UD7#*D~e|ajfTd_+*?aa8+LhmrwJc} zmO6Ia07?zbj^9}z1s?_tW`dLh$8#|`dj)WGxYd@abR#xN3LoUmF=4AImRm`0%Y$|% zAA9jkLU#i>DLfOfz443O!ong!sidnmGt$%XnPGaM-4oT*Cw@G2Z0DAHUiL4IvhoY^ z^9#g16^TKf`5u;0_J%i=E?iaHvRmc!{(H}E>&2vH=H}&!C2bkO8KM|FJA>Qm8a9FE z*S}Xfz3;%8%lCrflGC$t@R8QX1*P#!z-D)^KYVce*4>*If6}^g{_HtDYggZph$t9^ zq%+&c-_PRdRmP*vT!_q4_}N1wp3s2IF+P3aMNCDCp#&+Xo0WA#*f*N*SEuU9{J&DO&w zAT$E+X=heQr6Anp<=$O4AK%hGyLQW#Z|5sN)xUY)#g~{wdumdABVv8bZ*7VWx43-l zyWNMsU$8SK+(ze=j+d`LKDOEfo(Z_4xvV%d7KMeu0Y09<2XXW8^6?J{3Js_Go3h#n zE|wQ&r>CW*B*cK8gVi5K^Gv{82_USWvwulZJItSelipb?Z0aAbH`^{B-fHp!=%;i}fipzThNMqiSNGDku{AB^>55s~ zmfj!68GvR-goe(SJQJ{^!MZ7*Ph7NO?zoBL0mDCG+E+_ft=VsE=jw&^<@uV)yZ6og z>@%4=-;VqIb71sOoGh!gTV8R(AzM2agtP3IO!{)#{I90(bzd_9@-N1JIeC%p@#)hi zuQs=`12b&ao#kKb)!jO6m*wh-kWZLAZo2G>wbLdlsu-JFb&K0W4$k?~LG#(n<7riq_@zIfr(DT-6S_+JnSjeX%5#(AQnIT%Bz>Jt z?c$c4ywnggH1;OQjUY;i7+^t`BrM z7Fqmh9f*lFcTnzPOpfVma5*{f?X9)t>HcAUfyqJ<;$zaMM=O5^rC`Nv^`g3*nBXuc zt6Ms@(N!p+!nM@sUyaLqhWbT<^75?cFjsF+or~(X&BF@H1f^w&pI265_JQBth^quy zu?ZPb;V!1HtRCw>m*m5fz`78SU@zO83^qb9S+Wf}dN^ z4cyVjU>{ExzX-tEr^Nas#s%nTKfQbBRbY5*a%y^ae}z#*hM$A6frUdzYIbhCUu0_V zGoz6!Yb338&%1O44!s+~Km?e66pXn9CA#@o!q z);lO9BskD7ASRJKfv0uxK?z<0;xhN}eSZJ9x-d45jUPo1^mnSklmrW^Rfpu`pKXPya|&^{=` zgh4TCu26uIn`kQ)wY8w{8I?jf0J0GFKkOgH5HM!8K0w~fT4vxVBRR@>-BbZ6T|a1k zV0QQRwABeq%NskXP3lwoNBRZ;44pY4K+sZBJAcP9v!#obInea5aN>s+*M-=*nA}uX zR^GE|=~@-XGHNuI+CPa~Qaua|pWZls_QyiIP*hSXB>Tte(cO^a_wvD| zb7wV_RSxak{@vl%=)ZH~WkXO>6^F3rb-8+3Cq~ z{^n1gombM>v2MYf*%!?%c_v_X_tEXijZU^Bikhp9)m6TkCPV3WuIXz`foB3H#3B?Y zpiZd1_H!hfk!ogxexe`g8>)|ZCSaZkm}de;v3*l(dk6M`o{olUVNPO9L}+kGu&0TU zsTrWcY_StHG$R`hN3y%MR+yI*2Z-M=ZyO5>OG`^D8#^lFYCzpRIr2aPt}e;XL6?G` zhpUsLgT1|-9WhbYLxFki=pe4GC@aiLjtvj=_406cb8)F;MAr39gk>c{>3?-aX<=Sw zN_=EUV1S>mk9UQXDH~MqfGMl5s{p-q9w=gyq9a1#Q3jw)jAsJIM<5KcGJUCkTSN84 zfj!%|o%X1&XD}S4^!eZuD39@TGdI?|tD$=A(7tV3x9)va&B1Und3{YN4GC(p5D8Ceb(_TfcGJ&f|{^jLkqRU0qvF6eo}FU%zmC-_{N5aQ)i# zo4?zw`S8)RmyDabHr>v`MECyXpOp4)S-5310jA#l z-+>2t-TEy%cI{EQeB++(V^G$DwYsXz`H|k$3ujL5-??q`rj6To@7a6gyw>%*#6&6t z99>0av8&DvjkCx1@7lg?%l4gnemHza^YX3xdQWIK1RNGXv3Mq60^j4AfX9y?H-5qd znH!ar@RfKb;Hv7Xa?OovS1p-8YwFZ-NDr_vZo;G)bF`2#hxJv|Sgz%nfD7^q3R3+o z^tE*iOpKm9ym$BBeQoWBdL>k+T~bm~Sdh=!1~OBV<3oL1Y%NWVU%fIidd2bh3X0KH zkYA9OlbMm0l9Uh|7U1RT?Bw9!U|%XIp(zAKXefY69*{p%Q-CoV835;iX95<3sh9{r z*?9Z&yPw}mAzc9Kg3trP4_9KBJ=w7=*Z~XMvrSp z_wV}Q^uzo&zl=hTrctRJ>O2!L&jdUG?l;^KEC|j*^wi4BGXXPVUa+~c!SVbO%_mWZ zV)QT`Qcn1Z211hfA7Y*f7~LnYvrMk59NN8o*RuI5R}fRLg8cHt7W9cDLP|`&{k|{m zD}TR#$L0+S7bt?sQ(jR~_OuW<%5ZHVAL+C+esOl+-d(Gf@l3$=6*z+DWK#kNJ91$m zZcoM>%?evPsWA&JdKJLHrH#3?tdzub*HWQ3Q9DwK4TjBi*x_)%?r6xVe+LRwIf8*2 zZ))VLs;ndt8{7@g%6$+GK%AZH^&1m&{XaAV1<|Qimoyq1{xLrpgmeZ<=TXlgks#Ax zb+<4mN+P01iCs@kW?<w&8{gO*s*2%lCPF61y3(DmVhX%irN4%|42X21l-r#-(D2%hO(1*5D(x-o(Y&|0zQ0Pjb{SpnSfarF_s5f z9I$5aOu&Jj4$rThJ#|{?q|ybe{CqgL1qI}(4E_4<{V(l>FOh-%k#K{vUPHN}}hlQh2g?a>f`(BSox|%Zr9L-0&<$4{tgK5};RAq^%6B0_y(jECvdd)L&D zA31vT#F-0EY#dxXd;@~;;bCKLYp%|T^?a#&`-=L>qeqUOID7fI85G=o{AnZT>us+u z%ZhY0cyOC%0*2>N29#ks#AK$WrLzJ)DjMWaX^|pi{5S+L0wj{GOeUvxdvvt|AB3Pk z5R)a29e6Lrh54|ae^yA0RZ39F6fUOSckH2me&?zNKNV9w{U^%Y^0e_;OOz`0wG3s-(WvfNdqtM0~5GJT$|zh^s2hL`W4F(Ahwa5#8M{k z-uB!G_lIYG+_Gxvf(6^H+xwUtz8c2_&NBgL_&-0qZt+}&8B-@tnlV#GcIhz}H1Grj zg#g2%uOcbZ!dUl*m2(wlPMh7Y2ky)8i&de@XUEuJMab@IgV z6Q<9cIp>?>MrO879$wz?*(5z-k8Z2)-|&t6v`LdDjGs7NMsEI!vri06E$v-B(QF}> z6zN`9-L+%QOi&n%pD=0a47r6HwH`ixX=-8Xf={`tr_<)5((ZLj<)%)a3XXzlGZmNa zxpq$%Oushv@VGm>Bn4Lw@7c1HX98|&Ld+5HegHHtLLoqQ2Dd(Zu>TgY4Rd5Zm2`Xo zSJ*gaOj6UN2nWtUz!zyjJ;8?>KQb096ZnsyZxFkn-~%a8jWF!`!+-`N7??qX!>zOi z@Z$!J{$f(-K?6NzK*1_2@?&#z3j<^n_UmCC{y2$|yq2yS8|dn9Z34MA&jidf0bAI? zfA9|s#-0HPrq`pR12y>>LDnzzb+vgWV4ew>f~T~8@JztW%}R`|6qR_~Q9reF&DXPM z$;!#jUi~UCC=^sc%+2eH@yw10vOas{@Xo~w@-t`7l%K!W(4G2J1F3|*vnSfn;NJZ! zo7ZkzKX<0=^yxCQ7H&6ka3-pIaxlBP^Yw3CQQfy{`HF=y(`HPWA~So@egiW=>3Mjf z#iXONU1Wdzit3(CD;CI2lR+z#}8wWMyVx%vrMS z=A##;*7nZsR62(}rz1{d_s;dp=PAyTohdta#YVLU`Y+9_?Hrw{vW_+_QP}A%JQFZw zg#Zp*j>07z0Z=lKg}s^!^}`~@(7~aA+y!YA=Cl1Dn=pa*K$YG~0vWEY0c|K9CO*pH ziUE3zwHJ{>Eoez0mu~)S28Nvj@1Yel&;&#&i2a+}z&W=c#}I-qV=%-lj*1IEZ19w- zKv2D{tp%^Nt`@17%8HNT{=Vk2vh1|d)~-$w^9>s6kg6GzOZo~;&+OU0Zo{Fg&XrvR z(T3}BSdz+n#lhE4o%nw1<_*ghf5S5Y^Gv{~4&<4Dc_v_<2^iUCN#Dq_4ZHqn(XI6wvVF6OgZZJM{Cv-@hFm=xnYjE6Pj=_H=V`a&0q*vTpSzeGH7vblH(soxT z2PY2@4Tj>dh_KPn&jaGNy5hXdgs33M-CfMht?gaC0|G+;WeJ=e$;d!gb9rt~N^Dr5 zzn72atLLWH4nXSl_k+9>2UAI3N1Y%S2>#(=A%Wf&ugq=XpGcX0=?QoyV5tK@;Q=@Z zXz@W+VIDC_q7HxtaQ}HGU`p_U^_8PxU|gK&ttBFII6m#|EvO$UDy?p7g`8&swsm#| zPG4hljUYO=TO>$`^LI2e(Y<~3yb6ehRZm^AuyJs7acgX9sZL6*5#}aD`j}fh*S@ZG z{@j_fYU<~1KR2~;aCQaFZgaD+NSGDt@A~SQ?(J)u8tUhM`sv)IyN?Vl?3~?*hnL8T zQzO0Y4W2)|bM=zeg-cpz&tJR$*w7qF0w!;6ZYYRww>EjMd;j+Bn>Vg%Ubue$(eqd4 z);4yKGa}!{iX=}v<7bZ^>FV6Ob^E@~<7Y2{F=A~yCdX%9Rh*p^73g7)@>`w>xT*@? zH%s#Z zUhS&(6Jtxe2BO&&Rcr5E^VO1BvQuZwm_BpP{6*`3ICcK|1LO#-8W|05UGbAWOBXGk z_tm_)3l=Y3`Q5?O=daw+(SQERl;m|td)1^rKelW0`t_T)?)veBirU3%x3zWkpBWnC zEQ_`lEa%lVC8?4AuC^uyk98mF=;}XtZurW~!m5#H0%nJMh62L*KIH7HA2;0-c z2m~-GeW9QdVTpwU?NV*#9o9oJpQ)6R;T-{ymZES399h}^JMn?w! z{`YTh2f)NzS6xC-_s6eq`g+0_lX{=zu0# zS8tvPm`smV`BI@A3QH-!hU^(STiF)PMZib_a;mALZJe@i6y;+PI%%$-ES3i0^J8%P zK7;usg^z<&NX*)r+mS-2vSrTxIVDQXcMvQQW!OcKV`ZQ?;-?D)N(*=$0Zig$tLooeN7#`uY%?Z4hLpCr8A@g}6JKm>B5XxN9uKfTBR=SVZkABqZd;={wk+}&-AUp&;kqM@mF?%X*wH8tmyzK;5V?%L9Vv;f~g zH+MHHqZf~LZ)yE>?(CT}Dk`c*;k~_GeccU(S&7D0Ue3N=w&sS9AKbWnUR6m&MOo>L zkv~W-1_oO4GLt=g0{y%_tZj^RZ(Y?mqk8(Zl9I|#=I(vsl0k7(X?A?D2hQ8hNWne0 zfoE4$I<2IvddcgDD&oczVBD~6DpTgJB?Xeh zAn7Ssoj@&9q3#B*gMcj}RA)P8tpp3y2?lMhG6xd+p;y8p_IO%m*j@b+Y2Kb5LAe&> zta_Kr9CUS3=``VNj@dt34e(SPG)otu`tnS`JQFa_1WdMxof|bg$ZWy>QNmZ4pPx_m zPXj1=2u`6khsCG3y&Zi@0tlpoZOhce1Op$-_3=!=wOoD?)k|z}Z&z0vvd+jwRz7km zWpb=S(m|^jz1zr#c8PR%*(Q-3tCduN3X+&7MWt&aF{)O9Oin$G-2C{cq|O$i*3pJ; ze%)vm`XGi5n*qpZJw1b|&jbAZ#Vz&XJ~S0^uahZY0i`a<0UMpLz<~Db#H#ilDry|7 zp(J7d(8y?Wn!RU00M7)>GXYC|YqrPpOu)>OqELPd&jidf0mJiUjwudNsiB?@uWo8+ zUcRgUSRWAK_AZ`&K@7we%PzsxRu`m(dwBvo!`tdw85~jsr&SD-2uy?U3urc+3 zB$29o#^nEC0;fbQopYe@ae@!~9)vU~c<{+9s2|()*v<&~Clj6s<00f?Rh zPt}QM_>>6{e5^e=0Plf_$pPFr#uSLf3tX*3Bf}PtjljEv3FxItu-eg!BaeDuuxGHd z)>Gqx8(quEsg0k+JQFbD^lt~dtCM`KjUU~;t!)w>4~*ozg8ckKyg&WJBcr2l-VL@E zCx^LN80y@*YZw%rkOqbP`~vj&4U+uzuRr&c=jTK?TRnO3z|=ni6J%uN=H-dSeFMXQ zY8rX>%iF%Xl1xAQmyaHr`h>+NQ=fh|mXYX9IT$fI0>MB|thn6EHb5ToQmh7M=+h_df+tag$Y8nwcrh zlbiF+O9>pp&NldZaFb~H0F`g=P^tIBBl}h?mXn>iEVY};oa-3EC@ns8`GBoU)Wwa< zzmb!X;hBIBy|8li4Gsm9UMyB~yv5j)sN1hhXTz+SvNL9?8rXUUhJb00X9DI97h_-9 zVS&Mr&{2cRn?6Wjv`k9=HN0O2A%x;6+TTANbdDi0D|$ng?^7A~Gd4x0!9g3d$7tJU zgSp_G3r{m(2mhb@KdxY>2=03Bx=+8-j64%?1!BO(g3?m9e)Nb3f@ghaeqzm{Ir1}Q zWj6(&l{N1WWg zY>vFljA=7uX0Cr^Wa9#!K$s}mCVj@eq8D3ME}f?!gE4d6!x!fEt{y&k4t%n7yu@gF zzH#O9IdXC{WMp?}KQlvlAI}8*k*OCqJ#(``lL6Z#^Q0vHKjdT>-ROTMIYJ3M6ELA{ zwbJ{>mLCdW^!Ak}q)Yk-16~*y^>Pi%?2X0!+0|Gj8W`@*3w5mObhK(28Ukqvg(BGD zMIvr)@d^@kmE@(k+_-eebO2#{s+OSn=`hgIQI{a}b4<;4(AQEsc;c0RSSv$}!y*fL zS8;Mgq>H1GokMY)$?Zp~drhA{&ZtKihsw{85UopbHoE)B)6K%n!Ai&U#ht6?o_IJ~ zMFV{r816k{Q9*+9wd=Ze!OoVCZ(n_M@0yy5XB5u_>|}l45<5JOmoH!bWv{KJUpx2-J2%|DQ50%j+Ac50_D6gfns zjs-W#bHGubdxTh*5#G}a9D%r{O{<(8k&ht#b zTfe(_Swra}N{nrsJYe2A5uUb=!7dL~G%lzgJbdurk9&@5sU177_rlD^*%P;2i!jGK zBHZwX<_#@%b&d09RL-cKJALZv6Ju*MDZ^Xs=_>FKi!i@`>*mc{ckbQ45A^PPSML~@ z+d8_qlf0{2R3b>SG<2~weEG`M3Hug>~Zs?Ifa1jIWgFF*3#nlLL2$l?PFU@v_ zsY+2jY~Ac(vT%ybL1j2ryN}7q=*a=)?4M@>{tRR4svUBZH~EG|fVxo9ZL@O17iL=* zUzqXb=bz7Cp*U{JY=!AFr+m58+}$rYG+f*#*f;)aiORzHUwk%x`H4gGCQX_xJ44~C z$#Rcv9leOjdmwn@(z^!x6vjkX4x_SRi-RDN8R`%GQx;h08-uD)-yr*{qodpjbK6s#`_vo2{v8g3i z2-?27I(s^6(=(C+J>A?rTHNBLbR}orM7$)X%sSB`ssLwPV}o(vVapj07Lg1l---_t!6t)xwgh z##XS7w%2uaHl`$HL`Q-KGy`OUJ^ft~JQFZW0#Sh|N`_ga{FnDA`^@%mvcu+wlnRz~ zN~A#Ehj=~)&jbwbnP&p#nSgmFU~-XZm*X((xdS0$#0@od@!DsU$$OS6uu^&Yo&9TS zYb(}QRiYCoJ;0`*?=-scTSsd{wWHEW+TT7@;V>a3uAss*0rO12JQMKK;&Kr6HMh3G zCI|+KyubRo468fmZtVJ6Zp!LANEd;J9`*!3l7U1b_}ti#YIyUs#<#O%O#~Kj-mID3zcX!Il$U{!gOatdedI?m`CTec+*1msIeuf-84(1~>4f;aZucW{;0VBm+ zU(3bg*t^4d_-zE@$;t$ICSZ`J<`WYuopXMF|Lfb=gOb*|()_fjU_aj^U?A{Jz&sN$ z&jbwXsKKbFF9eo=e`qj7{V-t(mZ6#$kbpJd2xXkO^bL*$>ZdpblG7vDWsnY&4o-tp z!0c>*4jBatz!q-ePzdQc=o!YYscUGe$qukKd1>SnU(wu1&jBjVkMf4fc&8T+uW6n+ zeqg`4MP4)2a8aWqdsRFWaIqlK=9%_IZCZUGj?`VH$hY&-536&XoH(ltpy{CBt0 zzoDgic+d8YBwfE@dm`=8F})YY=jM>?97SrvNkcXwzo1ic#84- z#Y-cw408&g11%)v{UYik?ZGE#ts0M`?)XJ%STVnS?Wh`+a|r-z5T zJIzfBr2@zcK_;3-4GPH#F(E;LXfgAr$vJt6fIUGD!h;A7rp8Bxhk`2{_Q3*u%wkdW z$k{lW17KTaK=~m#Lkz$^$uj{@8b5vl&jgGd!Iu*zPMvr2m9Z(R79RcZp80q=Yf~@=U-Z#IQRwGzjK3)V*?jJM1y40hbO= z1H^0y7voIW4LNGN$E5hq6b1%{h}#W^PCNpxUMP&xV0yz)0X-fiKX@i!yg6^g&So!v zI&^6NrnSo#&7VK_tNHU6EZC5O5+n?g57_AIoZG#3=bF_^=P#T;Z{C6h3m0rpE+{G~ zEfeBRef`em+U@4v37BUBW~`-1 z15r|+b;Tnz3#MN@%rgNiA3D5Y^Ea@rS+nF6PgRUCN);4pQ*+5^kByf4k2}6wvuggb z`C$5$lid*5Lrf{?)}#fKX9DgU><&7=XZ5OO-+a4E_3mQ>GkZ7Rpz!F}_$1t4L?(?M zueO3v4;SC)*vR03fZ(vG*aW0#GqZ@v6L%oH9TA`u@l3#o38G+t%J)#g$}<5=o4a@> z;H?`LFPH9x)IOFIs(`+CU|`C0M`P?1-V+2`Qq7aAE8 zPwv>L=)uK9JJ&9mH-E0;EJX!51$o&8H%uM9f(}txk}E0)R<8JZ{#S~MinHWp z6_%+!GPZa34GN1G8b)*7&`8mvQ{SxocHx{ka};OIUwim6Ya;MM69IiZLxcT;-GOR5 zSFTvPWc7|y*Y4{*dqpk$XhH~s93CYMoM!^&wp`>(k!4}!xzvhJOQUpV!q&N802^2^^Nefi`|*s;s!^qnu`DSs?bEsi1GGi<*R` zgDc{tiKr_CbKn`qu4mIqC+1!z6!2fE z5D?7Er@}o9s@=ne4){+B(i2D^1qBh8;G|U1kKQ3v0XDFRJupC!;36)dvHA*D9YJ*z z+z*sFWGO?b@v=v`pFb+l++%Y3K4$;YN!U|M<(v?vP1cRIf+qQgoK_gNYDkwUQlL%* znwAdwBoq@XWN$x;%dj+%AA>)OCZHgxH zs_GdnPbxcMP-0B}=KcHMM>{LhV?&(uwazM?)wr1Q(Y;(!zYw9?>#X@3A3xG zPgfW1HNB$jxDdBjI=60LQaXB4>HJMSLIFY>BIHz~DCw<=^szV8xpViTin6N49sL)t zKx05mB2?i(_ocWp+}X;^4H zluTBFnkC%ouy26y6SgnU1k4;6yfq)}pJxL0IJbA*ibZmu*^?m;`vyw9vvYE(Nw{B7 zoZeJxa_qb1^S_drGHJ4m+)TNpXF`zmi%Uo(d2wnc&jh@8Li@I@jK zFf_5WcR<-U!?f>=zI^Dwf&H78t=V?);)6$zpQGK!!p^~w5QGS!x2?0KOjuEx7U=2f z?&0q0>gwj^N<@j&@XiXC0O8rx2yib!PD)%%OmtLKL`Vp_tl7oboKe^WmR?ZnRg{4a ziNTB|QK3KCKdX8|SrgemF>s(20CoPES!u~BscaKvnB27g^Gv{a^D>HBuv$_Zbv?FF z)}l_k=-}wkaBo{nePN=HS6X8Su@ORE&E%54fze-o|MlHaZ+Bx>jI)7}M@0vgXN+7A3c6*Q;V()lqXaZ2UTDH=)eB;@AtpG8SQN@i*hi0 z^zi=uuy$(WuBpV{0t}hoA^+pu$cUsV$HVlo-fgW*CN*GF0n{7hKqvb1U;qBkZ*PW& zyDCzBt)6J#(LAr6TZICK(h>k44UWA2^Y8!s_b;zU2HT3_y{#YLyTvmB^Gv|-|8sM) zQK1R%fP7iR^r%y)9`_ab|9}Z6EJ76g0gMAuRCkJiAxPK)wbexNL+*c30lCg&{x}>B zc1{3%2wYRj{MXlU@{by@EVj^)0?Enup+Sy5=ab`g;3PujJWT$~Z@P7UyoZyi!HX-Kmw!EX@ru)BEqH^t>#;(#1p1dI#XDHPxOQRpviY;; zELdhCqUo!un0we;>Je6*=3#82qouTU=^Vts=AJDF79=bj^6nlWs>)=xYsq zdhN*ebt}G-lU0~0ztSctAt^Z}g`}OGJQHwBOXf?p-QOQMcIxVt3)dd%zc4npwsmlH zCbS#6BU_q9^|{H}xhbI@cBs7p$1r;I(2GZ)8>B(h_KkIwWyRTv(P1IMY+wfuk;*Xv z>%oI_UXY!Z#60}i*qG>Ol4FQSj_n(kS5{h-i#na;q@={e1Q0E9QljF+9nW}tg{8#> zxmn}|Bqy_1M=y_q|0IQq@^Y9i%6+mjGt%+e$*@1j@f1Y##~C-z1k5u5V^Qzy@b@ol z%6@!k>&kiZGp9|TCc`rU^Gv`(bQI@ikH^}<4Jbtz%=?OJ4W!qK41pF33X14(P*+1i z(lRE;{Ah&1J`LPZfDg9;Cz8$vRAO8Tp3oLiTSre%@9>+UfzFm{VO~K^Yct%e8rt7W z!R^q})+H7X{__6kVM#}0bwPS~NLn2T?rQM#WuYcL^*C7#SIXTajtt3yTO*F@JGBFdk_I!TLe>DpMdt4)nd06Z;RW zKQ}8QEhQ;2f#ht^j#CZrf4l~1I{+q0O@R#EH@KK>is-DtriN~WBJd1hf5=FuEE*eh zlE4NHHE0t{0QPX~znqwsCwBCwASBNOOtC}w546m-aSnv2qdA&q0-iQ$+_>=*#*Lpi zb^3%=j}1&MtnC^aMG2}qPn|xzdGW02a?__ynlNqx&jg$pha5m;1bhgr4X|7`;Imc< zk(bBupLq~WhiqRet;Gt0!##ENPzoS3JtZ+d4skLJGHzzKbo?(XEyKTJED`BxDY#W> z{h%cR)4(gn0w63W*AwnzUUtR=irlgI9{DgdSWbaFVK~HC6S&wO!74}xr-5~W3p=ue zKIP$A43O#2B*bq*8TeMjueH)vCo{t*BDcnfmIfCtH-~XXS?cAK4aKbnZrX_tQ zBwpYX;Ss7hra_5XHlzv!CM-gP9sLA=`A6dc__mTHzXd@UBcQ#fO z3u@YWIBX@Uyl-%5^k;{>3$lNFeBy?Rf|P`W$RKYQCo{vRx_9pzg_q+bLF+P!MO9_l$?FQ{1Z1UvpHO=#C;3-yBJ$F^d zz_J&`{moSc2@!$rPPQhNFZFI+yK>?Dd3Ck(=dV0?W@-)F=x-~}O$cyzwz4uce4=~z z=8bEYE?m5L>E^>{##VOp`S!LH#`?J0S(=+0J$tHi@6MeYH*ep2pl@JeW#`CtkG}5O zoEU#sM>{JEQ^Oa}p1(9PGqWO;PcL7#V^D2uM{^Aj_OenFVJ{(mPwBg{M z!7AQ<2Hsr;h?!s4XTnOn#+0sr^^elO08 zi7za#u4`;*L$X0E85n-^^H6PCsH3x^Pv5`)&!64xO<8dX1%lefmezK0-yj-)duj`! z?W}BVJqKR@ufO|Rdc<|Yf^0!UX=7Vg@92P}MOc&@XoXz4`{2m${~4@o=)>u#vA(g1 zNS2!$N=oxmB0aF7+j{noym|ZU@W9aENJC9)Wlcp(ov^4bH$R+UENskOd?nyC?tjxg z+|wqk5!DEAx{XgrPfrT)@b$GdclP#|^npg~?axD9b;X@k1;yog=?O8Z@lN&uzP46i zGx3*5cqZVnLpoK(^Gv{O0b%yfGXe8Vz=%d6{)F@%&jbvcpk0yj^jwS|S$Liam=16V z-bn}f80f}8%C@z=a+lnbAc|e~0<(mZGZzGCnTW!)wl$nyddHmP6lCNS*h-XQ>%pw5sR)cA6#`YET8qHJB6|fW@(s;CAN%McS`!OBL?y=HRC^0$rtn|cV z8ca@FA4>SS?F|~-6kKCt`zK7_QU!tzz@WYc+6KoSm99;U37lJ-I@+Q1DHAw?!Ckon z1P%rT_S~3)yPkGviq-cy8krFjI47s5EXheK>1~s=*ZTc*(nQiXCa0ss2k}sPL|(4f zjUzu;c1iI1NJc42$l29A6Y$H8i@%baHhIdFsna(agJdrRC_QoU^#0NtEEYE#tXi;e z=Co;3r_I=FXafk4(1@rQF#VDow|!Svd*tDT^FT#9U4~}@MomAuR4|w^eDa0vYpPQn zQ}{&0?n+Kfrp+?}cVh)%_ZLg^^pYcg_u{>7mH~uA-9}y?yZx|oNW?ATiWJ@DEAI95 z^^ikIy;Jy)?oJZ-3`8608ffiZyG*yEyS)qbpp=Rovwza^`|;rMB{O8?6m`4XJGuxA zzY!lIyIj&E9tt?JbJ?ne3*_ZyE>7&g!ixqW;3-$L(*tt2;{9rF-!EPK^(G$6(*m3WL zrGtx`S73N-669kF6kBd@6GjJwhXe+Pgh#~_Oc;0WVh20e56hr8*H@tmCm#qK`2_`( zV5d2x2Q;>`0!P1r6@VjLi46TGTO93wusv8GF})KS_CNPW`ZG{>LUrN)lj)a>F*UBf2O{QCDwr}rH=bNOCSTylC=4&KxH zxS+JG0NZEhPMmeMH+Zyj`@Y@ZomIQ<7ZsnBmWeyRDc++X-NXFp{)6|P-qAh3Zu6Fn zt5hF9yZbymJ_#xA?&2^z4kt(n*IZIkQe2E8qyh+;vkZq^`W-F=4Q!s|Mlg4r`TLZelMaYGvAfbH$mAcr5Go{u z63+x&Sp%vBfb0W8ra9SEU;UD&uZ^Xv^L-ohTbjq7``W$=DquLfJ>rhycpLSf?mI-f zSvf*O~fjt3;$Jk^U!NlDMn7k4-1M!4C(Fe(Uhc&xI2?;$0g33%rPO|56Io<6g* za{<$DS8bBNU1YqU<7ETaSLcuI+r~2iqdh1y1NB8285vyEnAty7D51K)Qiuj(;0NU9 z=H}#N)4`aFUgHc*(E{wU7&seq_D|j~Q-FX46Yv##LT6_WXa9JNG-$xhr7}iRfSXf5 z-WKBAQZen_l!<|hge`$*0^a&6D=QZ(fLL67eU;)w#o1d=tp0Y3;JQMJQiQ{KZpE`f(4o$rmCKlb2+LsH)eg4Cy ziJyJGc;VD3ic`P%e4GNnkhWi>8bHf#N#(I=UwpQBxBQpmfRHq6@f?{+6J=)0j9+yD zauaLZjs@1|rYPT-^V#^X=X||w_jlhe-?09xsgrhHx&QQ)xgB;-am#J_eLNE|`4JTN zVr~KT>Qd_gV+w|Z8#pX6rZ%e2`6y>D3?q8O1l>J|U5?3dc?}ms>>YYDGT2qyP$8^t zrpP2=F0j?AhAm)2zx?v+o8Gp@=JK+*#4JH0>*8gINYy0&`S0I{M}{SBEmgI(siD4+ z8O0T}`{G|^1;oSu`@jDf84!1L^t82B7UdM6Ix{}2xI`c*1<(YunJ3-Qi)tfE<64Vb34-bs|I^6YI6k=~@*TG=+s1hBK zP!?uok#e3180Gx+Ub^Ys=I9!~6o1g(Aeq zq{|1|qHxlHxUF7PmlG2l=45qC$2Pi(&M{o0e>E=e8R{1a%FDB&!(6>RbuOykHV-QR zaZ(w=XqA&xD{)!0<~=Jgx5?&+EOrU`PQ%>(`2 zU#guutc?=$K%NPhaP(;Zpz4URlO)dsOockYi4r1}_>aM=cv$$KgABrM$-xxiJN~Dn zA#ejel=)P!hggF>5cPe zj~qL&X7Su5hfLB1Ah8jW{bTj$ZpiU_`QXyIvl_}OhjwoNZuK|If3!)@#0JkZ0rO12 zbb{q>Z|20Zs4`3iZFb4KR!&l+`yT^GT8JsSMfS$-|N2_*@3%gOoEwB`&jzqOXi$=% zFu#QMd`^MFaFjY{835`JMW=l^G6>`la2m|LhJ#IZjCOqV`PFb41g67$2No13xEr=U z0EvdZD4K)@1>f4))03EI0!Fknzp|=^ny72r-u?1+WKi5%Cj^>zU`P%_WZ;>AW9k}O zM4f;A^&TiegOX0P+oi{b2l@GU0Lj)hAe3hU=9z$Ln6IK6+z_AMJXY}&lXxt2g-K!J=mFfS)1!pFf#@8-Fad$#S^ykWzJ zP1}^RD}ls7^7``JqT(12TVri4m194Esu%K&+jcvYBiTb7%k_c+VOfHo?X$a@r;h!w zZPP}`!Nhwf7g$pGKN`zHuUZ#3C&w$Ez%O=qLVuGz|aY+dYB(KfL0P~W z=H&jJ+ct07xPAAYy+_V#UB9cNUr`|hYF0&Mv8&DvjkCx1@7lg?%l4gnemHza^YX3x zdQaF*4&03Lve4%j)szl>zi02>eLo&Pc~p#(NI1M3XB4DM7|R!(v@8e1!a?yH_Uq5e2n zi290poQ{wW{UC2HG1@m@X7Z$o6DQ2rpq&A{v>HM&DhE$FUEY#>bpG7gvQs8ZoHTL# z_(?0x>KlQm0>~9^^5(?DYgWvkD>HS%_(@>j7l^|Xn0o2a;o%WB#x!^) z;9iQjAX>s$jKxkjE*;-lr%&0oA|t{TAF2>OX<0>+I; zT!M5zcD0Kds^E1|_6IvQw)JwlE$PM`=tmfU)#ouNJh)vLaCTWf19$M?U_X@sqIw?@ z00Pydw-*~8yQ3i=0?HIVQfN?g9|Jl--DFIzB!+wdX%r}MU0#?5g}jXoOeU2xZfgpd zQE@sw4MJf+sNx2$XXWjbjA3~;yb3%^|5*0{tPXO}S|D6OB|{Xaz{}_-j3G7{2o{p> zBxg0QOb(h5b@xce4!v6tW1D%JuE814_Q8&L^=U?JQ67UcP$W;<X@Ozu10@2+m_A{2BxhL^gz-sEZ2~ zKF@ug`_I`kT}f)~vrlnquf5k^>s{*w6DUboBQd4NumOhTv|B^}x$PRO6NlJAe>PTI z8&%Jvm4gjVBYd+$kXMwpd&ue2aUTF6v+=P2QifGGx^f)|YeZ=!cp7+{am8}WWDm1EaA8LmJ`FuAT*sDhybQd3 z`*yIG9vJ?4^nu|sZNb=_eDHmHSw&G*=is39CL|XLrQ-f~RaUB7H~g?}_qCMXey*G# z#&2ojtf~m_QeO?JH+4U$qw!;)Ox4S%!zCf1ta)E-To}!9eRGm_Ri=h&__zj48CE_wBpE z_Oc{zM~fFX)s+=boxhz1YY=7{av1QfO9tP*9}pJAxZ4^(x^n8oaiy~tErnb#2laJO zgkW&smrsq^!H(vJ+Lu%m6^|>Pd7RzE#c^pR;hBI-QzJbso@l5lD;?SQ(;l7)m}df} za3>`ydYy%E`&X{`U<4 zg3JSH!$E;C3)ugd(J=J(*Pl#`s&Ze&gm(D)7Xu^=F@=+w($Rn#!!lqTZhYy0U(OBE zg9E*d+ka&OmvnZBnwnbLI^l_7h7yo@_ME@2rSnSP$kGydzo60-wc7Gbz@$|ySCDf} zvcMg~0Fq8)nIII!Wk-1!=v=>iM)iVgR8$P8LOUenWBB~%KmYjrv$(M$ zH_Yzk&5P&M&uBP@goK8Ng^RmL{_*ptkMDXL%8QbGOde~TLA&nc8yFlM5+d#*AHus| zKmOd)RxQYgaC&|Jf~wjXbuD`rPk$837J~=5cVO`65AS+V$ursC_VxYqCr_!Je`e$0 z;^`Mi@}8c7!MAUF#SMiSv0i46Z(TTbTK%elg}t+fw_gy-eD{C~d2pahSe>8fXRCWp z^X!=mnooEpV4ew>V9Z0y2hRk|GXZa0xn#+bWy{xW+o62-;j=eJrl9&|&NK!~=b39a!H%Vy*n2#9r$tEiWyTUO`LHct^ptLi@ZY;{nXCzy87|mo0d$KpCB`7 z#@@WzN>H(s2-x_NYFkS`qlZTiZeKocvYgC#xv8^s!3BzsM^$3rOiVF!%67ZCZ`b;z zv*l$cjGr)J@`)ml!2me|IY^O$%%YoI?6mSwU|6IN1rZ%N&y9W8-3D;8AWBwRnc-ONt&J+rDi2*+Gb)7xGNN4Gn^7Nk?;OUYLiIzAi|KcqU*yJ$(Zs6Eh1d z8#^M>2GK7vVu|{_FgG)l?G{()CP6Z0#_$TZNz(V(1$(?~4kNQ%U3F z;L|EBiuLjEkIiod(+y=%U_)=QyDnJpN92Mf@ z;qL6>8(&f?=;{CSUw{AnzQ4B{=eq^KZ0J;K+^+0owK-r71MqyNKi|NQ%}?+3b@8ibgJMcG+tNfCbTP7V%sR_0dz z34Oo*>z{vqdfO){D6Xq$s4LFTN{#~~s=bY^t)-cjUv&S6|MS28^$9e{l$}*qT~wTt z7#`&5fU&JDEo^*(`v>~|_dkAp*C%PNgDq82l$8<}>gnuYYhh_+Wohr~-On=t_wY=> zUp#=EqY&5vcnh)j6H_mogY<%ipI_hs2;Gutz?j0BYmnow|O?6-BXy3Sa<}|o=m6cU=5n&T`w$z!dPJLSo2oz*0vp$1li1>Ap@GOo$^gj{(mF?Csq$@JU>eS%~M+ zC`1)gG;rhg^bNdg&rR{Mb@uAwnSdFHsZY}0QeRn;8Q|#X<`?4Q=IAqZK78nD2>UY;#)a2CN|xEQZlaW-SG{;YCONkV4=S*>^@8rWgTm<`JXxVK@xn zp%MU}$V>!!L9`h8eYl=t@U+Ln3II-)1u)n~4^%wQ1k4r?mIK8z0rO12uzs0g$}<6D z;XwOxgrFcB!f@=+&Ct1&M2I!!+uaw=dfI=v1qkLp7Ada0fHYdi?w0gzz*;D6i)_mC3k!pE;_fN_yiboFZ*>~iW;)&z?cdcEw za^dV5vlkt_`bg9+@_Bq&{jB=M1BZ_OxckR_KW$mPebtJY(`U?Gv_(zp8FsIh@P|h) z967Lg$I)HuH*VUxWZwL_v!_g%vwZW(+d3~n|J5eSdvj*@mNk1fEL*Yehs86e&6z!Q z=90C0&fd{^{t7b+C)~OaZN)viH!WSea_Pc_3+K&WwsDt=#$BBkdM1#fQgo}RKFQ|R zv7H-NE?Tg7+1i~)RIh01=o(w{Ou(Z^=}5sj1>r0erq9f$z%v2U`oJ>*^Gv|fY7jgV zFs%zz&X}O3JQFa_1Pq4*xrRHNc_v`yYG?K*LCpw;UQvPb2hkjmvz%+p!lhdM#PnNJ zBYhXWq#Y==9bN=Z&NBgXb_u;Q+oT(_jZO+IPY}h9{(}j;HL6G20Ve<<&jcJBN9Kz} z+*y3=mB)kC3KM0Ke;}ivY-R82=^GG)GF##{$lE35`i2*dE|??-BJK&YvRhu6+Pk=W z`v(LD(e-q~YYslPY2j1_nF-@2$jGjGq-$>P?C$Ls5C}Oc&!NyszSdYouiAVrw>V(0ovZ)9DZ!!9OO92 z%I~=S#K6j)X95Oxi0t!k8}eUqzGgYHtU~R#GKvz?DZjA+uT&!Po9ihT1X9S!SIo}n zSfXlbN0TOa>>m(n` zCn}0ZcOO5zb^RSzt5>?IS=m`x**p_)b5XFPeo&N~)#W$tR@YSaZdkwN!j;Qvca3eF zy#hko+J(shPDZAIE)P#%xqVYb<;a0OI~A{9P`++#b7X6@qT8$@zU>!$p? zNNWoZM=L8E2PbDIXBSsjPaprl;80>Z#%xEy*E&H#Mr=e_SVUMza9{xPv%(@GBcq~Y zN$V(bK%s?#90V28k`v-$Vq#)r<9Q}vtRHwj)X2$*1w`CVm;rH*DCIz02V3nRA}LlB z=H)AnjBoGkw!ib>2}icpNY_FvX{{=0=^jwDDO_2d-v|XdSz{xw$+z$Kpj$gwgH8id%NRboZ@qf`L~JG%m!BxD)6>2a%QFE#IR3!Z!wV0;GTK#N$JX`PgX$pr=f@82+_!VPW=gPy-UUrZ z7k4~=Q9+=tU4DeUdr5$u-igEe_y2h6f)%)uHJ{o!xZ?RYWrSMi=lWS0MtfMiJf*mG zBhLgJi}fKco>mA(l*#5gsJWBR8{rLqP8i~|LNjM8|UBc>qM-TnLqUumu;`C6ZocTD_ezVE}A`3cJ{hs zSF41`bFM;yh`3$SWUzai`HKh3Molt)uxOI3%=A5r##t1XA$(g>R^Be|j660+Zuyb3 zbH~ryw0WiS3i+uEwkxjTnSjTSUS?!!31-;TTZ{g-Lwm!xZ6?b`{`Iea8!>v=c)7(Z z#*Lh;tY>5jYF7V!Q@_){G4U@G56&DmZ0uN`2{<-7G9n@(GAb%MI+`otMV9{;{m)EG zO-@QmAl*dP?Sj3xhHJO7~#a zN7?}Z9ch&x92M9O&30*6@tod#E2qn`Nn`WIO?W0?No$F)u{zDm)z~96JR%}i|3Ol? zr)NM^0=NlNlGDrE2St)vVNsbN$ih1$B;h1bT6x_ZD?#3F`x<=SskcqE9w3;&^Fjo=y=D{0XbYe6EGeNJJ*s0%8X5TO*-0} z%MFg3az|&{Mrfy@#+`!^4Ck4Ev3_7yVgbOy(AI(#s=kFvi4cB|W&6QJ$}<70gu5B) zTe$i9`}_HLdWR<$g(W!qTH0A&JEr68>~i3Ot&M|6czOXyi%{D!GdZEKu0Fy)&D}xg zy3$KWN45LLu0An^RkcW#CQwCHSwXQd#Wx|-)As7|Q>L~~9#M&Df?BK(T%(g_Rbx?v zN3i#eU3<07@=H*zrCx|{iFN?`wpyC&Yg_Xo3WH6Q)|`C>UlD}#9nwA_-6>GxEi&LMom+ zY!E|Y>d;bCQ5B_i@&w6o6?I@xd9FV?)Cq-ok5!cDbF)#Y2YzRb3e>kW)s)*P9ivZ2 zeZDsKj}&1#Ru@Go?!w1M`!^|0hP0G6)}}@LwEjz|i~67^F?$21hlklzn{i|NEBYIo zB39p_Ms6=OaEX>>X68yCJN;lCDD#EHw82)E3Gx969Qta|h5mvV3PRr{&jidf0Wa98 z`&!?^J0UwC%Ab}L7wKj6^qIQS*)1z)PMvbW$Yk%?M`rfHx#iWAtPb)sL4LZg{`n*7 zXErREB(w0Lp1DJCbV5GZK3OU?z@w;*6mM|j0M^?s- zL_d(NTu_jolbuEH7Ne#I(Iv7UR3HQiNYo)8c}lDp2f&1-rDY%q<*IK`jS$oj0SG!L zn+k%ExPtiqSOWtxRT&`wbU|)5ArWyU=m25eQnqw8&K&@GaR;`^3!Cc7^Rv>EV}mJ8+Qu4b(w1fDP>l{$XrieJZl#i1>kWa4@do&YEO5hZeBR2xMS0r70Z{eSh;%jx~)GwHZd^=6LDo#vYm~k z{)1bW&mGyddG!y=mi@4D)tZf4k34#*XGjyTve?VoSntu@tLKmG+^~8ju3xcg-RAA* z?mv3=ip|K%Buis`?Yo!HDD7CkYUK|Yf6b;X`!sLed;GMNo3h^eukT+2Q}3S5;6Yxw zYW=7HFn^h&5898%R9436I9HZ&iu6yWk-(~G3>j)4g2o9VI#)MzY=6+ zrqhyJ?X2VM@knLUTsbfi5C7YDbp7yAJMCX=P#T9;|zG#ncJIM}CKE@!xmD zM~qn?92p)4>#MT(?mZj#C!6Ogj2bqa(fFdz@DZaI+W30=7MGM&=3F?Uc4gm|S#o1Z z{8x_Ycf^<(I`+19#iivHxhgwWtXexuVZw-EAOhx@fTvF9nShB*k2yvA8ILL>D5qqf3Wb) z*;Q*dEuA-S?(8{>w_nh;^9qScO3TX1;^e)(?Ln9JuUorv#ri#Gb&Rc@eM6(-lhd=Z zvzZ(zyppcw+z>Y>@5q?wu+XrWgp_oQo|~U9mGex%#Mg-k23MnlRqW=OfB}B&22BNP z1Mk|12~;vLfCY^B6@hI9sIrHn80~*sWpj4Nmerf~-%Ra$+e30_FcJ{s1||3cqU*eR0Orjh=hVQ1aK)6gnlOo z1Z(u?S2@WL24;N-NrFZj=vRCYq!@%6!TqTNZJ=M|OvsJPmN|65SS$&d&=t6h1_X&X zP8T!=3>mz%d|?9ZXlt&?PYU&Na*3!$#g=wZ^&({4B9z`1+i^=tQm~Vu&J7Li5E0J= zY><(am6e^7gXbz~hkDfK`&+(zeE0l`Lx+wWIehfIZUQtUH7%XV+goePbG(fnYh62| zc;LVxuxVV-jf_noH6poKSW}i8?eOaE)e9;|_Ut`)_=xgly}%IkNK7QTq_LzRHOTtK zjf<)$4uQz`z~Pe`#-4sbVNtR1G@T?ZMOg`6wy!kRm5(3VvwPpcBd4xgfF>*`G%}jz zo1}wh0){O~@muCwD@N=LWdgwjmYKy63_vEpra*xYKtRYzPvwCqz?PSj&EyQE0D}Q2 zKw{uQseYkp4@%vs-i5NQF>b zGy;pzc>gBwfn7&E=_S2_d2U<&#BK&P1Up%FB>g@GoF_~#7>_&yT zI)C}~^Do^E1#ux><`2)GQc_kwXIh7?h73Gh-qHKZ$KU_nRht#z=V5&BEXr>xsoqPi z<;V;m?il#}^FRLCS(O;*Hf*x>zB@-IdhX)b0?WAOhcLeOPVsn-A!&C-@S3koM}_% zt-xqWi`+GVpQ%TAauan1tG8d}b4YicpPq%l9QxzGFhkxeU? z&zvYTUPe}7`p$?Ngk%wj!R3x!kHA&2V;o<;F?V26DF}u_RId?(1~Yq3CSXZh zZECpPOI_y@pp8(bzp8?jIqvHI`O}}j{qplbx2P)C)##DVQ}aq;Lmk4M{KqeE`}#yB2_B|Tv~HbK*UA9G0HgpYQVAm5-~RrOe|;G2?-AxkxtZzQ zxuJGSGok=w@7Y-$lJ4IAf#3i6um9_h_x&9;c~LwQ@SS^ipBR~2JGgjwdHLY@fg+xL z0|Wi78J>FjX4ck@c1DI~mbQ+_Q1tfkg?&Y3K70DQ!6q*#EzL^`^Yird^z!ke8VEtC zf`BIB9J(#ST0pEb6607BPIx#{I3mcv!w}FSG5iOB@RWk-1B#cJkPsgarbbGjV2w_z z!bXJK%S(X5$wVX`Eh&+Tc#<4>!c9UN00ffw;`5=KpmI)50=9@lK@+sNS%^uAGXRGC7QX*2vK3Y;t@~8evHO#Xx7b!DppU0@Iht zt15AsboHU`f9lZXJQFa_1k5u5Lt&{n zFyUuOxv|PX|4Ct`RfkBUc_v_<30P+GqDOWPF7965pkslJ=w%)5YIS4F()rWn#*G^@ zPDXC}viktInp@jp>p+@T;8X2$=Z-C$F>~^SF=IxLlTnzt_Ac;y#wMnyxY{gi&ANYC z^}xot)27Id9X)E)IGM>ax7~X1?2Uo32^C~*YHl*qR6Dq7$+Sr_W6@!p{IvOpuH8ll zL&&im2t~Q~6nCwiH+>3dl5qg~e%|WS8aMAfd8KDW+ZRaYL=k6y+_-e^w8<0Y

      Hu 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%0Aj{1S#ihB)5$-MyW;P!EBkw=_Iy^8m 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�T&YHF`!* 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

      0hI8XA>B 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�C=`0~Ou#-~wwA7b{ophn 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

      ^uW1Ok)+#1Pnhu=Ve4C0?!0Y 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�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 Vo~6qjrFnNAT|;HT|6@qN{~yp>D<%K{ diff --git a/mindspore-lite/minddata/wrapper/x86-example.cc b/mindspore-lite/minddata/wrapper/x86-example.cc deleted file mode 100644 index f566d8b4d..000000000 --- a/mindspore-lite/minddata/wrapper/x86-example.cc +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ - -#include -#include -#include "minddata/dataset/include/dataset/datasets.h" - -using Dataset = mindspore::dataset::Dataset; -using Iterator = mindspore::dataset::Iterator; -using mindspore::dataset::Cifar10; -using mindspore::dataset::RandomSampler; -using mindspore::dataset::Tensor; - -int main() { - MS_LOG(INFO) << "Doing MindDataTestPipeline-TestCifar10Dataset."; - - // Create a Cifar10 Dataset - const int64_t num_samples = 10; - std::string folder_path = "./testCifar10Data/"; - std::shared_ptr ds = Cifar10(folder_path, std::string(), RandomSampler(false, num_samples)); - - // Create an iterator over the result of the above dataset - // This will trigger the creation of the Execution Tree and launch it. - std::shared_ptr iter = ds->CreateIterator(); - - // Iterate the dataset and get each row - std::unordered_map> row; - iter->GetNextRow(&row); - - while (row.size() != 0) { - auto image = row["image"]; - MS_LOG(INFO) << "Tensor image shape: " << image->shape(); - iter->GetNextRow(&row); - } - - // Manually terminate the pipeline - iter->Stop(); -} diff --git a/mindspore-lite/providers/siteai/CMakeLists.txt b/mindspore-lite/providers/siteai/CMakeLists.txt index f30013476..b5f1f6bf6 100644 --- a/mindspore-lite/providers/siteai/CMakeLists.txt +++ b/mindspore-lite/providers/siteai/CMakeLists.txt @@ -7,7 +7,4 @@ set(MSLITE_DEPS_OPENSSL off CACHE INTERNAL "setting MSLITE_DEPS_OPENSSL value") ##enable prune simplest cloud inferenc set(MSLITE_SIMPLEST_CLOUD_INFERENCE on CACHE INTERNAL "setting MSLITE_SIMPLEST_CLOUD_INFERENCE value") -if(NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(MSLITE_MINDDATA_IMPLEMENT "off" CACHE INTERNAL "setting MSLITE_MINDDATA_IMPLEMENT value") -endif() set(MSLITE_ENABLE_OPENCV on CACHE INTERNAL "setting MSLITE_ENABLE_OPENCV value") \ No newline at end of file diff --git a/mindspore-lite/src/CMakeLists.txt b/mindspore-lite/src/CMakeLists.txt index be964557a..1ca8d9fa2 100644 --- a/mindspore-lite/src/CMakeLists.txt +++ b/mindspore-lite/src/CMakeLists.txt @@ -97,18 +97,6 @@ set(API_SRC ${CXX_API_SRCS} ${C_API_SRCS}) if(NOT MSLITE_ENABLE_RUNTIME_CONVERT) set(API_SRC ${API_SRC} ${CORE_DIR}/utils/status.cc) endif() -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - file(GLOB CXX_API_TRAIN_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/litert/cxx_api/train/model.cc - ${CMAKE_CURRENT_SOURCE_DIR}/litert/cxx_api/train/model_impl.cc - ${CMAKE_CURRENT_SOURCE_DIR}/litert/cxx_api/metrics/*.cc - ${CMAKE_CURRENT_SOURCE_DIR}/litert/cxx_api/callback/*.cc - ) - set(API_TRAIN_SRC - ${CXX_API_TRAIN_SRCS} - ) -endif() if(SUPPORT_NPU) include_directories(${DDK_PATH}) @@ -268,12 +256,6 @@ if(MSLITE_GPU_BACKEND STREQUAL cuda) ${CUDA_RUNTIME_SRC} ) endif() -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - set(TRAIN_SRC_WITH_MD - ${CMAKE_CURRENT_SOURCE_DIR}/train/train_loop.cc - ) -endif() set(TRAIN_SRC ${API_TRAIN_SRC} @@ -504,11 +486,6 @@ if(TARGET_OHOS) target_link_libraries(mindspore-lite_static hilog_ndk.z.so) endif() -if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "lite" AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - target_link_libraries(mindspore-lite minddata_eager_mid minddata-lite) - target_link_libraries(mindspore-lite_static minddata_eager_mid) -endif() if(SUPPORT_TRAIN) add_library(lite_train_src_mid OBJECT ${TRAIN_SRC}) @@ -518,23 +495,13 @@ if(SUPPORT_TRAIN) target_link_libraries(mindspore-lite-train lite_src_train_common_mid mindspore::securec) set_target_properties(mindspore-lite-train PROPERTIES OUTPUT_NAME "mindspore-lite-train") set_target_properties(mindspore-lite-train PROPERTIES CLEAN_DIRECT_OUTPUT 1) - if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - target_link_libraries(mindspore-lite-train minddata-lite mindspore-lite) - else() - target_link_libraries(mindspore-lite-train mindspore-lite) - endif() + target_link_libraries(mindspore-lite-train mindspore-lite) add_library(mindspore-lite-train_static STATIC $) target_link_libraries(mindspore-lite-train_static lite_src_train_common_mid) set_target_properties(mindspore-lite-train_static PROPERTIES OUTPUT_NAME "mindspore-lite-train") set_target_properties(mindspore-lite-train_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) - if(MSLITE_MINDDATA_IMPLEMENT STREQUAL "full" AND NOT - (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - target_link_libraries(mindspore-lite-train_static minddata-lite mindspore-lite) - else() - target_link_libraries(mindspore-lite-train_static mindspore-lite) - endif() + target_link_libraries(mindspore-lite-train_static mindspore-lite) endif() if(MSLITE_ENABLE_KERNEL_EXECUTOR) diff --git a/mindspore-lite/test/CMakeLists.txt b/mindspore-lite/test/CMakeLists.txt index a6c7372b2..e68b94e11 100644 --- a/mindspore-lite/test/CMakeLists.txt +++ b/mindspore-lite/test/CMakeLists.txt @@ -190,10 +190,6 @@ target_link_libraries(lite-test gmock_tests) if(MSLITE_ENABLE_TRAIN) target_link_libraries(lite-test mindspore-lite-train) - if(NOT MSLITE_MINDDATA_IMPLEMENT STREQUAL "off" - AND NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) - target_link_libraries(lite-test minddata-lite) - endif() endif() if(PLATFORM_ARM AND NOT (MSLITE_ENABLE_CLOUD_FUSION_INFERENCE OR MSLITE_ENABLE_CLOUD_INFERENCE)) diff --git a/mindspore-lite/test/runtest.sh b/mindspore-lite/test/runtest.sh index c7cb791f2..809f5137e 100644 --- a/mindspore-lite/test/runtest.sh +++ b/mindspore-lite/test/runtest.sh @@ -79,11 +79,6 @@ if [[ "${MSLITE_ENABLE_CLOUD_FUSION_INFERENCE}" == "on" || "${MSLITE_ENABLE_CLOU exit 0 fi - -# test cases of MindData -./lite-test --gtest_filter="*MindDataTestTensorDE*" -./lite-test --gtest_filter="*MindDataTestEager*" - # test cases of Converter ## ./lite-test --gtest_filter="TestTfliteParser*" ./lite-test --gtest_filter="ConvActFusionInoutTest*" diff --git a/mindspore-lite/test/st/scripts/base_functions.sh b/mindspore-lite/test/st/scripts/base_functions.sh index de0b44108..493c0eacb 100644 --- a/mindspore-lite/test/st/scripts/base_functions.sh +++ b/mindspore-lite/test/st/scripts/base_functions.sh @@ -288,11 +288,6 @@ function Push_Files() { cd $1 || exit 1 tar -zxf mindspore-lite-$3-android-$2.tar.gz || exit 1 - # If build with minddata, copy the minddata related libs - cd $4 || exit 1 - if [ -f $1/mindspore-lite-$3-android-$2/runtime/lib/libminddata-lite.so ]; then - cp -a $1/mindspore-lite-$3-android-$2/runtime/lib/libminddata-lite.so $4/libminddata-lite.so || exit 1 - fi if [ -f $1/mindspore-lite-$3-android-$2/runtime/third_party/hiai_ddk/lib/libhiai.so ]; then cp -a $1/mindspore-lite-$3-android-$2/runtime/third_party/hiai_ddk/lib/libhiai.so $4/libhiai.so || exit 1 cp -a $1/mindspore-lite-$3-android-$2/runtime/third_party/hiai_ddk/lib/libhiai_ir.so $4/libhiai_ir.so || exit 1 diff --git a/mindspore-lite/test/st/scripts/run_net_train.sh b/mindspore-lite/test/st/scripts/run_net_train.sh index b6fb7f1f9..ec47b5d2e 100755 --- a/mindspore-lite/test/st/scripts/run_net_train.sh +++ b/mindspore-lite/test/st/scripts/run_net_train.sh @@ -360,13 +360,6 @@ function Run_arm() { cd ${arm_path} || exit 1 tar -zxf mindspore-lite-${version_arm}-android-${process_unit}.tar.gz || exit 1 - # If build with minddata, copy the minddata related libs - cd ${benchmark_train_test_path} || exit 1 - if [ -f ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/lib/libminddata-lite.so ]; then - cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/third_party/libjpeg-turbo/lib/libjpeg.so* ${benchmark_train_test_path}/ || exit 1 - cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/third_party/libjpeg-turbo/lib/libturbojpeg.so* ${benchmark_train_test_path}/ || exit 1 - cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/lib/libminddata-lite.so ${benchmark_train_test_path}/libminddata-lite.so || exit 1 - fi cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/third_party/hiai_ddk/lib/libhiai.so ${benchmark_train_test_path}/libhiai.so || exit 1 cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/third_party/hiai_ddk/lib/libhiai_ir.so ${benchmark_train_test_path}/libhiai_ir.so || exit 1 cp -a ${arm_path}/mindspore-lite-${version_arm}-android-${process_unit}/runtime/third_party/hiai_ddk/lib/libhiai_ir_build.so ${benchmark_train_test_path}/libhiai_ir_build.so || exit 1 diff --git a/mindspore-lite/test/ut/src/dataset/eager_test.cc b/mindspore-lite/test/ut/src/dataset/eager_test.cc deleted file mode 100644 index ddb915410..000000000 --- a/mindspore-lite/test/ut/src/dataset/eager_test.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * 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. - */ -#include -#include "common/common_test.h" -#include "gtest/gtest.h" -#include "include/securec.h" -#include "minddata/dataset/include/dataset/tensor.h" -#include "minddata/dataset/include/dataset/datasets.h" -#include "minddata/dataset/include/dataset/transforms.h" -#include "minddata/dataset/include/dataset/vision.h" -#include "minddata/dataset/include/dataset/execute.h" -#include "minddata/dataset/util/path.h" -#include "src/common/log_adapter.h" -#include "include/api/types.h" - -using MSTensor = mindspore::lite::Tensor; -using DETensor = mindspore::tensor::DETensor; -using mindspore::dataset::vision::Decode; -using mindspore::dataset::vision::Normalize; -using mindspore::dataset::vision::Resize; -using Execute = mindspore::dataset::Execute; -using Path = mindspore::dataset::Path; - -class MindDataTestEager : public mindspore::CommonTest { - public: - MindDataTestEager() {} -}; - -TEST_F(MindDataTestEager, Test1) { -#if defined(ENABLE_ARM64) || defined(ENABLE_ARM32) - std::string in_dir = "/sdcard/data/testPK/data/class1"; -#else - std::string in_dir = "data/testPK/data/class1"; -#endif - Path base_dir = Path(in_dir); - MS_LOG(WARNING) << base_dir.toString() << "."; - if (!base_dir.IsDirectory() || !base_dir.Exists()) { - MS_LOG(INFO) << "Input dir is not a directory or doesn't exist" - << "."; - } - auto t_start = std::chrono::high_resolution_clock::now(); - // check if output_dir exists and create it if it does not exist - - // iterate over in dir and create json for all images - auto dir_it = Path::DirIterator::OpenDirectory(&base_dir); - while (dir_it->hasNext()) { - Path v = dir_it->next(); - // MS_LOG(WARNING) << v.toString() << "."; - std::shared_ptr de_tensor; - mindspore::dataset::Tensor::CreateFromFile(v.toString(), &de_tensor); - auto image = mindspore::MSTensor(std::make_shared(de_tensor)); - - (void)Execute(Decode())(image, &image); - EXPECT_TRUE(image != nullptr); - (void)Execute(Normalize({121.0, 115.0, 100.0}, {70.0, 68.0, 71.0}))(image, &image); - EXPECT_TRUE(image != nullptr); - (void)Execute(Resize({224, 224}))(image, &image); - EXPECT_TRUE(image != nullptr); - EXPECT_EQ(image.Shape()[0], 224); - EXPECT_EQ(image.Shape()[1], 224); - } - auto t_end = std::chrono::high_resolution_clock::now(); - double elapsed_time_ms = std::chrono::duration(t_end - t_start).count(); - MS_LOG(INFO) << "duration: " << elapsed_time_ms << " ms\n"; -} diff --git a/mindspore-lite/tools/converter/adapter/acl/CMakeLists.txt b/mindspore-lite/tools/converter/adapter/acl/CMakeLists.txt index 0e96d12e8..e32daaec7 100644 --- a/mindspore-lite/tools/converter/adapter/acl/CMakeLists.txt +++ b/mindspore-lite/tools/converter/adapter/acl/CMakeLists.txt @@ -1,5 +1,4 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${TOP_DIR}/mindspore-lite/minddata/dataset) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-rpath,$ORIGIN/") set(ASCEND_VERSION_FILE "${ASCEND_PATH}/latest/compiler/version.info") diff --git a/mindspore-lite/tools/converter/adapter/acl/cxx_api_lite/cxx_api/serialization.cc b/mindspore-lite/tools/converter/adapter/acl/cxx_api_lite/cxx_api/serialization.cc index 55a3dc26f..cea33ecea 100644 --- a/mindspore-lite/tools/converter/adapter/acl/cxx_api_lite/cxx_api/serialization.cc +++ b/mindspore-lite/tools/converter/adapter/acl/cxx_api_lite/cxx_api/serialization.cc @@ -21,7 +21,6 @@ #include "load_mindir/load_model.h" #if !defined(_WIN32) && !defined(_WIN64) #include "cxx_api/dlutils.h" -#include "minddata/dataset/include/dataset/execute.h" #endif #include "utils/crypto.h" diff --git a/mindspore-lite/tools/dataset/cropper/.gitignore b/mindspore-lite/tools/dataset/cropper/.gitignore deleted file mode 100644 index eb8ea4d2c..000000000 --- a/mindspore-lite/tools/dataset/cropper/.gitignore +++ /dev/null @@ -1 +0,0 @@ -debug.txt diff --git a/mindspore-lite/tools/dataset/cropper/CMakeLists.txt b/mindspore-lite/tools/dataset/cropper/CMakeLists.txt deleted file mode 100644 index 217896854..000000000 --- a/mindspore-lite/tools/dataset/cropper/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -cmake_minimum_required(VERSION 3.14.0) -project(MinddataCropper) - -add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fPIE -fPIC -Wl,--allow-shlib-undefined -s") - -file(GLOB minddata_OBJ CONFIGURE_DEPENDS "tmp/*.o") - -if(NOT minddata_OBJ) - message(FATAL_ERROR "Your code is not using any MindData functionality.\n \ - ... libminddata-lite_min.so is not needed\n... Terminating crop.sh") -endif() - -message(STATUS ${CMAKE_CXX_COMPILER}) - -add_custom_command( - OUTPUT libminddata-lite_min.so - PRE_BUILD - COMMAND ${CMAKE_CXX_COMPILER} - -shared - -o libminddata-lite_min.so - ${minddata_OBJ} - ${EXTERNAL_DEPS} - -pthread - -std=c++17 - -fPIE -fPIC - -s -) - -add_custom_target( - minddata-lite ALL - DEPENDS libminddata-lite_min.so -) diff --git a/mindspore-lite/tools/dataset/cropper/README.md b/mindspore-lite/tools/dataset/cropper/README.md deleted file mode 100644 index 167d6595c..000000000 --- a/mindspore-lite/tools/dataset/cropper/README.md +++ /dev/null @@ -1,71 +0,0 @@ - -# Objective - -The goal of this tool is to allow the user to reduce the size of MindData lite package they ship with their code. - -# How to run - -This tool has two parts: the first part only needs to be run once, when the source code for mindspore is changed -while the second part should be run every time the user code changes. - -Note that you need to run this tool on the server side if you are planning to use your code on an edge device. - -## Step 1: Configure the cropper tool - -You need to have mindspore installed on your system to run this python script. -Additionally, you need to have the mindspore source code present in your system -as this script processes mindspore's source code. - -To execute the first part simply run: - -```console -python cropper_configure.py -``` - -## Step 2: Crop the MindData lite package - -The second part needs to be run every time the user adds or removes one of MD operators in their code. - -For the second part, you need to run: - -```console -./crop.sh -p -``` - -Note that you need to provide the name of all files that are using any of the MindData functionalities. - -`ANDROID_NDK` environment variable needs to be set as well if the target device is android. - -Example: `./crop.sh -p ~/mindspore/ foo.cc foo.h bar.cc bar.h` - -This code will create the __libminddata-lite_min.so__ library specific to your code and will also print for you a list of -shared objects that your code depends on (including __libminddata-lite\_min.so__). -Note that you need to copy these files to your target device and set the linker flag accordingly. - -# How it works - -The first step (configuration) creates a few of files that are needed in the second step. -These files include _dependencies.txt_, _associations.txt_, and _debug.txt_. -While the third file (_debug.txt_) is only for debugging purposes (debugging cropper tool), -the other two files are used in the second part. -_associations.txt_ contains the entry points (IR level source files) for ops that the user may use in their code. -The other file, _dependencies.txt_, contains all dependencies for all those entry points. - -When the user runs the crop script, _parser.py_ will be run on their code to find the ops they have used. -Afterwards, the text files will be used to keep the needed object files -(by removing unnecessary object files from the static library containing all of them). -Finally, the remaining object files will be used to create a new shared object file (_libminddata-lite\_min.so_). - -# Requirements - -Step 1: - -* Python3 -* mindspore -* mindspore source code - -Step 2: - -* Python3 -* cmake -* Android NDK (if target device is android) \ No newline at end of file diff --git a/mindspore-lite/tools/dataset/cropper/associations.txt b/mindspore-lite/tools/dataset/cropper/associations.txt deleted file mode 100644 index bf05b87ce..000000000 --- a/mindspore-lite/tools/dataset/cropper/associations.txt +++ /dev/null @@ -1 +0,0 @@ -{"root": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/root_node.cc", "concat": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/concat_node.cc", "zip": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/zip_node.cc", "bucketbatchbylength": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc", "dataset": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/dataset_node.cc", "mappablesource": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/dataset_node.cc", "nonmappablesource": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/dataset_node.cc", "project": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/project_node.cc", "shuffle": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/shuffle_node.cc", "skip": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/skip_node.cc", "rename": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/rename_node.cc", "cache": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/cache_node.cc", "repeat": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/repeat_node.cc", "transfer": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/data_queue_node.cc", "epochctrl": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc", "map": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/map_node.cc", "syncwait": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/sync_wait_node.cc", "take": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/take_node.cc", "cachelookup": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/cache_lookup_node.cc", "cachemerge": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/cache_merge_node.cc", "buildsentencevocab": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/build_sentence_piece_vocab_node.cc", "filter": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/filter_node.cc", "batch": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/batch_node.cc", "buildvocab": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc", "random": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/random_node.cc", "cmuarctic": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/cmu_arctic_node.cc", "speechcommands": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/speech_commands_node.cc", "squad": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/squad_node.cc", "udpos": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/udpos_node.cc", "album": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/album_node.cc", "cifar100": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/cifar100_node.cc", "phototour": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/photo_tour_node.cc", "sogounews": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/sogou_news_node.cc", "stl10": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/stl10_node.cc", "imagefolder": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/image_folder_node.cc", "flickr": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/flickr_node.cc", "voc": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/voc_node.cc", "lsun": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/lsun_node.cc", "csv": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/csv_node.cc", "gtzan": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/gtzan_node.cc", "div2k": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/div2k_node.cc", "widerface": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/wider_face_node.cc", "ljspeech": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/lj_speech_node.cc", "minddata": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/minddata_node.cc", "qmnist": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/qmnist_node.cc", "kitti": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/kitti_node.cc", "yelpreview": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/yelp_review_node.cc", "emnist": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/emnist_node.cc", "mnist": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc", "omniglot": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/omniglot_node.cc", "lfw": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/lfw_node.cc", "celeba": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/celeba_node.cc", "multi30k": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/multi30k_node.cc", "enwik9": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/en_wik9_node.cc", "iwslt2017": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/iwslt2017_node.cc", "conll2000": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/conll2000_node.cc", "sbu": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/sbu_node.cc", "usps": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/usps_node.cc", "places365": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/places365_node.cc", "yahooanswers": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/yahoo_answers_node.cc", "tfrecord": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc", "yesno": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/yes_no_node.cc", "textfile": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/text_file_node.cc", "penntreebank": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/penn_treebank_node.cc", "cifar10": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/cifar10_node.cc", "iwslt2016": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/iwslt2016_node.cc", "fakeimage": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/fake_image_node.cc", "dbpedia": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/dbpedia_node.cc", "tedlium": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/tedlium_node.cc", "caltech256": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/caltech256_node.cc", "generator": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/generator_node.cc", "manifest": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/manifest_node.cc", "cityscapes": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/cityscapes_node.cc", "fashionmnist": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/fashion_mnist_node.cc", "semeion": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/semeion_node.cc", "agnews": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc", "libritts": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/libri_tts_node.cc", "wikitext": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/wiki_text_node.cc", "imdb": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/imdb_node.cc", "coco": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/coco_node.cc", "kmnist": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/kmnist_node.cc", "clue": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/clue_node.cc", "amazonreview": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/amazon_review_node.cc", "pksampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/pk_sampler_ir.cc", "mindrecordsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/mindrecord_sampler_ir.cc", "subsetsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/subset_sampler_ir.cc", "subsetrandomsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/subset_random_sampler_ir.cc", "randomsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/random_sampler_ir.cc", "prebuiltsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc", "weightedrandomsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/weighted_random_sampler_ir.cc", "sequentialsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc", "distributedsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/distributed_sampler_ir.cc", "skipfirstepochsampler": "mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc", "normalize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/normalize_ir.cc", "randomcolor": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_color_ir.cc", "randomadjustsharpness": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_adjust_sharpness_ir.cc", "randomselectsubpolicy": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_select_subpolicy_ir.cc", "pad": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/pad_ir.cc", "randomequalize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_equalize_ir.cc", "rotate": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.cc", "randomsolarize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_solarize_ir.cc", "randomhorizontalflip": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_horizontal_flip_ir.cc", "rescale": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rescale_ir.cc", "randomcropwithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_crop_with_bbox_ir.cc", "boundingboxaugment": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.cc", "verticalflip": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/vertical_flip_ir.cc", "randomverticalflip": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_vertical_flip_ir.cc", "randomresizedcropwithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_resized_crop_with_bbox_ir.cc", "randomresizewithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_resize_with_bbox_ir.cc", "rgbtogray": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rgb_to_gray_ir.cc", "randomresize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_resize_ir.cc", "totensor": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/to_tensor_ir.cc", "randomposterize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_posterize_ir.cc", "cutout": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/cutout_ir.cc", "invert": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/invert_ir.cc", "rgbtobgr": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rgb_to_bgr_ir.cc", "randomaffine": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_affine_ir.cc", "decode": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/decode_ir.cc", "randomautocontrast": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_auto_contrast_ir.cc", "resizewithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.cc", "rgbatorgb": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rgba_to_rgb_ir.cc", "uniformaug": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/uniform_aug_ir.cc", "adjustgamma": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc", "centercrop": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/center_crop_ir.cc", "randomlighting": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_lighting_ir.cc", "autocontrast": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/auto_contrast_ir.cc", "affine": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/affine_ir.cc", "dvppcropjpeg": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppdecoderesize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppdecoderesizecrop": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppdecodejpeg": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppdecodevideo": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppdecodepng": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppnormalize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "dvppresizejpeg": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/ascend_vision_ir.cc", "randomcropdecoderesize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_crop_decode_resize_ir.cc", "slicepatches": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/slice_patches_ir.cc", "crop": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/crop_ir.cc", "rgbatobgr": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rgba_to_bgr_ir.cc", "autoaugment": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/auto_augment_ir.cc", "equalize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/equalize_ir.cc", "gaussianblur": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/gaussian_blur_ir.cc", "randomverticalflipwithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_vertical_flip_with_bbox_ir.cc", "randomcrop": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_crop_ir.cc", "resizepreservear": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/resize_preserve_ar_ir.cc", "cutmixbatch": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/cutmix_batch_ir.cc", "padtosize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/pad_to_size_ir.cc", "swapredblue": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/swap_red_blue_ir.cc", "normalizepad": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/normalize_pad_ir.cc", "horizontalflip": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/horizontal_flip_ir.cc", "randomsharpness": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_sharpness_ir.cc", "convertcolor": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/convert_color_ir.cc", "randomcoloradjust": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_color_adjust_ir.cc", "randomresizedcrop": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_resized_crop_ir.cc", "hwctochw": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/hwc_to_chw_ir.cc", "randomhorizontalflipwithbbox": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_horizontal_flip_with_bbox_ir.cc", "randomrotation": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_rotation_ir.cc", "randominvert": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/random_invert_ir.cc", "mixupbatch": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/mixup_batch_ir.cc", "resize": "mindspore/ccsrc/minddata/dataset/kernels/ir/vision/resize_ir.cc", "compose": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "concatenate": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "duplicate": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "fill": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "mask": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "onehot": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "padend": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "prebuilt": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "randomapply": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "randomchoice": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "slice": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "typecast": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "unique": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "plugin": "mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "basictokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "berttokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "casefold": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "filterwikipediaxml": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "jiebatokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "lookup": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "ngram": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "normalizeutf8": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "regexreplace": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "regextokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "sentencepiecetokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "slidingwindow": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "tonumber": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "tovectors": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "truncatesequencepair": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "unicodechartokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "wordpiecetokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "unicodescripttokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "whitespacetokenizer": "mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc"} \ No newline at end of file diff --git a/mindspore-lite/tools/dataset/cropper/build_lib.py b/mindspore-lite/tools/dataset/cropper/build_lib.py deleted file mode 100644 index bc506a03c..000000000 --- a/mindspore-lite/tools/dataset/cropper/build_lib.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2021 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ -""" build MindData lite minimum library """ - -import glob -import itertools -import json -from operator import itemgetter -import os -from pprint import pprint -import sys -import warnings - -import parser - -DEPENDENCY_FILENAME = 'dependencies.txt' -ASSOCIATION_FILENAME = 'associations.txt' -ALL_DEPS_FILENAME = 'needed_dependencies.txt' -OBJECTS_DIR = 'tmp/' - -ESSENTIAL_OBJECTS = [ - # 'types.cc.o', - # 'tensor_impl.cc.o', - 'random_sampler.cc.o', # default value for datasets (may not exist in their code) - 'random_sampler_ir.cc.o', # default value for datasets (may not exist in their code) -] - - -def load_dependencies(): - """ - Read dependencies.txt and load it into a dict. - - :return: a dict containing list of dependencies for almost any file in MindData lite - """ - depend_file_name = os.path.realpath(DEPENDENCY_FILENAME) - if not os.path.isfile(depend_file_name): - raise FileNotFoundError("dependency file ({}) does not exist.\n" - "Please run cropper_configure.py first.".format(DEPENDENCY_FILENAME)) - with open(depend_file_name) as f: - dep_dict = json.load(f) - return dep_dict - - -def load_associations(): - """ - Read associations.txt and load it into a dict. - - :return: a dict containing entry point (a filename) for each op - """ - ass_file_name = os.path.realpath(ASSOCIATION_FILENAME) - if not os.path.isfile(ass_file_name): - raise FileNotFoundError("association file ({}) does not exist.\n" - "Please run cropper_configure.py first.".format(ASSOCIATION_FILENAME)) - with open(ass_file_name) as f: - dict_ = json.load(f) - return dict_ - - -def get_unique_dependencies(dependencies_dict, associations_dict, user_ops): - """ - Find which dependencies we need to include according to the ops found in the user code. - - :param dependencies_dict: a dict containing list of dependencies for almost any file in MindData lite - :param associations_dict: a dcit containing entry point (a filename) for each op - :param user_ops: a list of ops found in the user code - :return: a list of dependencies needed based on the user code - """ - selected_entries = [] # itemgetter(*user_ops)(associations_dict) - for op in user_ops: - print('{} --> {}'.format(op, associations_dict[op])) - selected_entries.append(associations_dict[op]) - selected_files = itemgetter(*selected_entries)(dependencies_dict) - selected_files = list(itertools.chain(*selected_files)) - return sorted(list(set().union(selected_files))) - - -def remove_unused_objects(final_deps, essentials, all_object_files): - """ - Remove object files that are determined to be NOT needed to run user code - as they are not in the dependencies of user code. - - :param final_deps: a list of dependencies needed based on the user code - :param essentials: essential objects that should not be removed from final lib - :param all_object_files: a lsit of all objects available in our static library - :return: None - """ - # find objects which are not part of any dependency (lstrip is needed for remove '_' added in crop.sh) - to_be_removed = [x for x in all_object_files if not any(x.lstrip('_')[:-5] in y for y in final_deps)] - # keep the ones that are not an essential object file. (lstrip is needed for remove '_' added in crop.sh) - to_be_removed = [x for x in to_be_removed if not any(x.lstrip('_') in y for y in essentials)] - - print('Removing:', len(to_be_removed), 'unused objects.') - pprint(sorted(to_be_removed)) - for filename in to_be_removed: - os.remove(os.path.join(OBJECTS_DIR, filename)) - - -def main(): - # load tables created using cropper.py - dependencies_dict = load_dependencies() - associations_dict = load_associations() - - # get all objects filename - all_object_files = [os.path.basename(x) for x in glob.glob('{}*.o'.format(OBJECTS_DIR))] - print("All Obj files: {}".format(len(all_object_files))) - - # find ops in user code - my_parser = parser.SimpleParser() - temp = [my_parser.parse(x) for x in user_code_filenames] - user_ops = set(itertools.chain(*temp)) - print('user ops: {}'.format(user_ops)) - - # user is not using any MindData op - if not user_ops: - warnings.warn('No MindData Ops detected in your code...') - remove_unused_objects([], [], all_object_files) - deps_path = os.path.realpath(os.path.join(OBJECTS_DIR, ALL_DEPS_FILENAME)) - with os.fdopen(os.open(deps_path, os.O_WRONLY | os.O_CREAT, 0o660), - "w+") as _: - pass - exit(0) - - # find dependencies required (based on user ops) - unique_deps = get_unique_dependencies(dependencies_dict, associations_dict, user_ops) - print('Unique Deps (.h): {}'.format(len(unique_deps))) - print('Unique Deps (.cc): {}'.format(len(list(filter(lambda x: x[-2:] == 'cc', unique_deps))))) - - # add essential files to dependency files - final_deps = set(unique_deps + dependencies_dict['ESSENTIAL']) - print('Total Deps (.h): {}'.format(len(final_deps))) - - # delete the rest of the object files from directory. - remove_unused_objects(final_deps, ESSENTIAL_OBJECTS, all_object_files) - - # write all dependencies to the file (for extracting external ones) - deps_path = os.path.realpath(os.path.join(OBJECTS_DIR, ALL_DEPS_FILENAME)) - with os.fdopen(os.open(deps_path, os.O_WRONLY | os.O_CREAT, 0o660), - "w+") as fout: - fout.write("\n".join(unique_deps) + '\n') - - -if __name__ == "__main__": - # get user code filename(s) as argument(s) to code - if len(sys.argv) <= 1: - print("usage: python build_lib.py []") - exit(1) - user_code_filenames = sys.argv[1:] - main() diff --git a/mindspore-lite/tools/dataset/cropper/crop.sh b/mindspore-lite/tools/dataset/cropper/crop.sh deleted file mode 100755 index 2c4dcc333..000000000 --- a/mindspore-lite/tools/dataset/cropper/crop.sh +++ /dev/null @@ -1,201 +0,0 @@ -#!/bin/bash -# Copyright 2025 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ - -usage() -{ - echo "Usage:" - echo "bash crop.sh -p [] \\" - echo "bash crop.sh -h \\" - echo "" - echo "Options:" - echo " -p path to mindspore directory" - echo " -h print usage" - -} - -# check and set options -checkopts() -{ - while getopts ':p:h' opt - do - case "${opt}" in - p) - MINDSPORE_PATH="$(cd "${OPTARG}" &> /dev/null && pwd )" - ;; - h) - usage - exit 1 - ;; - *) - echo "Unknown option: \"${OPTARG}\"" - usage - exit 1 - esac - done -} - -checkopts "$@" - -# exit if less than 3 args are given by user -if [ $# -lt 3 ]; then - usage - exit 1 -fi - -# exit if mindspore path is not given by user -if [ -z "${MINDSPORE_PATH}" ]; then - echo -e "\e[31mPlease set MINDSPORE_PATH using -p flag.\e[0m" - exit 1 -fi - -ORIGINAL_PATH="$PWD" -FILE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -# getting absolute paths for user provided filenames -USER_CODES="" -for i in "${@:OPTIND}"; -do - USER_CODES+="$(cd "$(dirname "${i}" )" &> /dev/null && pwd )/$(basename "${i}") " -done -# exit if user has not given any argument as their code -if [ -z "${USER_CODES}" ]; then - echo -e "\e[31mPlease provide your file names as arguments.\e[0m" - exit 1 -fi -echo "Provided files: $USER_CODES" - -echo "MS PATH: $MINDSPORE_PATH" -echo "CWD: $ORIGINAL_PATH" -echo "File PATH: $FILE_PATH" - - -cd $FILE_PATH || exit - -MD_LIB_FILENAME="libminddata-lite.a" - -# locate original MindData lite library -MD_LIB_PATH=`find $MINDSPORE_PATH -name "${MD_LIB_FILENAME}" | head -n 1` -if [ -z "${MD_LIB_PATH}" ]; then - echo -e "\e[31mMindData lite static library could not be found.\e[0m" - cd $ORIGINAL_PATH || exit - exit 1 -fi - - -# extract all objects of static lib to tmp/ -mkdir -p tmp -cp $MD_LIB_PATH tmp -cd tmp || exit -# extract objects with identical names by prepending (one or more) '_' to their names -# (this scruipt supports more than 2 duplicate filenames) -DUPLICATES=`ar t "${MD_LIB_FILENAME}" | sort | uniq -d` -for dup in $DUPLICATES; -do - i=0 - prepend_var="_" - while : - do - i=$((i + 1)) - # check if more duplicates are available (break otherwise) - error_output=$(ar xN $i "${MD_LIB_FILENAME}" $dup 2>&1) - if [ -n "$error_output" ]; then - break - fi - mv $dup "${prepend_var}${dup}" - prepend_var="${prepend_var}_" - done -done - -# extract unique files from static library -UNIQUES=`ar t "${MD_LIB_FILENAME}" | sort | uniq -u` -ar x "${MD_LIB_FILENAME}" ${UNIQUES} -cd .. - -# remove unused object files -# write needed depsendencies to tmp/needed_dependencies.txt -python build_lib.py ${USER_CODES} -retVal=$? -if [ $retVal -ne 0 ]; then - cd $ORIGINAL_PATH || exit - exit 1 -fi - -LD_SEP='\n' -EX_SEP=$';' -LD_PATHS="" -EXTERNAL_DEPS="" - -# locate external dependencies for MindData lite -LIBJPEG_PATH=`find $MINDSPORE_PATH -name "libjpeg.so*" | head -n 1` -LIBTURBOJPEG_PATH=`find $MINDSPORE_PATH -name "libturbojpeg.so*" | head -n 1` -LIBSECUREC_PATH=`find $MINDSPORE_PATH -name libsecurec.a | head -n 1` - -# resolve symbolc links -if [ "$(uname)" == "Darwin" ]; then - c=$(file -b "$(readlink $LIBJPEG_PATH)") -elif [ "$(expr substr "$(uname -s)" 1 5)" == "Linux" ]; then - c=$(file -b "$(readlink -f $LIBJPEG_PATH)") -fi -# detect system architecture -IFS="," read -r -a array <<< "$c" -TARGET_ARCHITECTURE=${array[1]##* } -echo "Architecture: $TARGET_ARCHITECTURE" - -# exit if $ANDROID_NDK is not set by user for ARM32 or ARM64 -if [ "$TARGET_ARCHITECTURE" == "ARM64" ]; then - if [ -z "${ANDROID_NDK}" ]; then - echo -e "\e[31mPlease set ANDROID_NDK environment variable.\e[0m" - cd $ORIGINAL_PATH || exit - exit 1 - fi -elif [ "$TARGET_ARCHITECTURE" == "ARM32" ]; then - if [ -z "${ANDROID_NDK}" ]; then - echo -e "\e[31mPlease set ANDROID_NDK environment variable.\e[0m" - cd $ORIGINAL_PATH || exit - exit 1 - fi - # add LIBCLANG_RT_LIB for ARM32 - LIBCLANG_RT_LIB=`find $ANDROID_NDK -name libclang_rt.builtins-arm-android.a | head -n 1` - EXTERNAL_DEPS=${EXTERNAL_DEPS}${LIBCLANG_RT_LIB}${EX_SEP} -else - echo "No need for ANDROID_NDK" -fi -# Note: add .a files only to EXTERNAL_DEPS. -if grep -q 'jpeg' "tmp/needed_dependencies.txt"; then - LD_PATHS=${LD_PATHS}${LIBJPEG_PATH}${LD_SEP} - LD_PATHS=${LD_PATHS}${LIBTURBOJPEG_PATH}${LD_SEP} - EXTERNAL_DEPS=${EXTERNAL_DEPS}${LIBJPEG_PATH}${EX_SEP} - EXTERNAL_DEPS=${EXTERNAL_DEPS}${LIBTURBOJPEG_PATH}${EX_SEP} -fi -# we always need securec library -EXTERNAL_DEPS=${EXTERNAL_DEPS}${LIBSECUREC_PATH}${EX_SEP} - -# create .so lib from remaining object files -cmake -S . -B . \ - -DEXTERNAL_DEPS="${EXTERNAL_DEPS}" \ - -DARCHITECTURE=$TARGET_ARCHITECTURE - -# no dependencies to MindData lite -retVal=$? -if [ $retVal -eq 0 ]; then - make - echo -e "\e[32mLibrary was built successfully, The new list of MindData-related dependencies is as follows:\e[0m" - echo -e "\e[36m$LD_PATHS$PWD/libminddata-lite_min.so\e[0m" -fi - -rm -rf tmp/ - -cd $ORIGINAL_PATH || exit diff --git a/mindspore-lite/tools/dataset/cropper/cropper_configure.py b/mindspore-lite/tools/dataset/cropper/cropper_configure.py deleted file mode 100644 index 2916c12f2..000000000 --- a/mindspore-lite/tools/dataset/cropper/cropper_configure.py +++ /dev/null @@ -1,396 +0,0 @@ -# Copyright 2021-2024 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ -""" configure cropper tool """ - -from functools import lru_cache -import glob -import json -import os -import queue -import re -import shlex -import subprocess - -from mindspore import log as logger - -DEFINE_STR = "-DENABLE_ANDROID -DENABLE_ARM -DENABLE_ARM64 -DENABLE_NEON -DNO_DLIB -DUSE_ANDROID_LOG -DANDROID" - -ASSOCIATIONS_FILENAME = 'associations.txt' -DEPENDENCIES_FILENAME = 'dependencies.txt' -ERRORS_FILENAME = 'debug.txt' -OUTPUT_LOCATION = "mindspore-lite/tools/dataset/cropper" -MINDSPORE_LOCATION = "mindspore" - -# needed for gcc command for include directories -MANUAL_HEADERS = [ - ".", - f"{MINDSPORE_LOCATION}/mindspore", - f"{MINDSPORE_LOCATION}/mindspore/ccsrc", - f"{MINDSPORE_LOCATION}/mindspore/ccsrc/minddata/dataset", - f"{MINDSPORE_LOCATION}/mindspore/ccsrc/minddata/dataset/kernels/image", - f"{MINDSPORE_LOCATION}/mindspore/core", - f"{MINDSPORE_LOCATION}/mindspore/core/include", - "mindspore-lite", -] - -# To stop gcc command once reaching these external headers -# (not all of them may be used now in MindData lite) -EXTERNAL_DEPS = [ - "third_party", - "build/_deps/pybind11-src/include", - "build/_deps/tinyxml2-src", - "build/_deps/jpeg_turbo-src", - "build/_deps/jpeg_turbo-src/_build", - "build/_deps/icu4c-src/icu4c/source/i18n", - "build/_deps/icu4c-src/icu4c/source/common", - "build/_deps/tinyxml2-src", - "build/_deps/jpeg_turbo-src", - "build/_deps/jpeg_turbo-src/_build", - "build/_deps/nlohmann_json-src", - "build/_deps/securec-src/include", -] - -# API files which the corresponding objects and all objects for their dependencies must always be included. -ESSENTIAL_FILES_1 = [ - "api/data_helper.cc", - "api/datasets.cc", - "api/execute.cc", - "api/iterator.cc", -] - -# API files which the corresponding objects must always be included. -# (corresponding IR files will be included according to user ops) -ESSENTIAL_FILES_2 = [ - "api/text.cc", - "api/transforms.cc", - "api/samplers.cc", - "api/vision.cc", -] - -DATASET_PATH = f"{MINDSPORE_LOCATION}/mindspore/ccsrc/minddata/dataset" - -OPS_DIRS = [ - "engine/ir/datasetops", - "engine/ir/datasetops/source", - "engine/ir/datasetops/source/samplers", - "kernels/ir/vision", - "kernels/ir/data", - "text/ir/kernels", -] - - -def extract_classname_samplers(header_content): - """ - Use regex to find class names in header files of samplers - - :param header_content: string containing header of a sampler IR file - :return: list of sampler classes found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Obj : )", header_content) - - -def extract_classname_source_node(header_content): - """ - Use regex to find class names in header files of source nodes - - :param header_content: string containing header of a source node IR file - :return: list of source node classes found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Node : )", header_content) - - -def extract_classname_nonsource_node(header_content): - """ - Use regex to find class names in header files of non-source nodes - - :param header_content: string containing header of a non-source IR file - :return: list of non-source node classes found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Node : )", header_content) - - -def extract_classname_vision(header_content): - """ - Use regex to find class names in header files of vision ops - - :param header_content: string containing header of a vision op IR file - :return: list of vision ops found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Operation : )", header_content) - - -def extract_classname_data(header_content): - """ - Use regex to find class names in header files of data ops - - :param header_content: string containing header of a data op IR file - :return: list of data ops found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Operation : )", header_content) - - -def extract_classname_text(header_content): - """ - Use regex to find class names in header files of text ops - - :param header_content: string containing header of a text op IR file - :return: list of text ops found - """ - return re.findall(r"(?<=class )[\w\d_]+(?=Operation : )", header_content) - - -# For each op type (directory) store the corresponding function which extracts op name -registered_functions = { - os.path.join(DATASET_PATH, 'engine/ir/datasetops/source/samplers'): extract_classname_samplers, - os.path.join(DATASET_PATH, 'engine/ir/datasetops/source'): extract_classname_source_node, - os.path.join(DATASET_PATH, 'engine/ir/datasetops'): extract_classname_nonsource_node, - os.path.join(DATASET_PATH, 'kernels/ir/vision'): extract_classname_vision, - os.path.join(DATASET_PATH, 'kernels/ir/data'): extract_classname_data, - os.path.join(DATASET_PATH, 'text/ir/kernels'): extract_classname_text, -} - - -def get_headers(): - """ - Get the headers flag: "-Ixx/yy -Ixx/zz ..." - - :return: a string to be passed to compiler - """ - headers_paths = MANUAL_HEADERS + EXTERNAL_DEPS - - output = "-I{}/".format("/ -I".join(headers_paths)) - - return output - - -@lru_cache(maxsize=1024) -def get_dependencies_of_file(headers_flag, filename): - """ - Create dependency list for a file (file0.cc): - file0.cc.o: file1.h, file2.h, ... - - :param headers_flag: string containing headers include paths with -I prepended to them. - :param filename: a string containing path of a file. - :return: a list of file names [file0.cc, file1.h, file2.h, file3.h] and error string - """ - command = 'gcc -MM -MG {0} {1} {2}'.format(filename, DEFINE_STR, headers_flag) - command_split = shlex.split(command) - stdout, stderr = subprocess.Popen(command_split, shell=False, stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate() - deps = re.split(r'[\s\\]+', stdout.decode('utf-8').strip(), flags=re.MULTILINE)[1:] - - return deps, stderr.decode('utf-8') - - -def needs_processing(dep_cc, processed_cc, queue_cc_set): - """ - Determine if a file's dependencies need to be processed. - - :param dep_cc: the candidate file to be processed by gcc - :param processed_cc: set of files that have been already processed. - :param queue_cc_set: files currently in the queue (to be processed) - :return: boolean, whether the file should be further processed by gcc. - """ - # don't add the file to the queue if already processed - if dep_cc in processed_cc: - return False - # don't add the file to the queue if it is already there - if dep_cc in queue_cc_set: - return False - # if file doesn't exist, don't process as it will cause error (may happen for cache) - if not os.path.isfile(dep_cc): - return False - return True - - -def build_source_file_path(dep_h): - """ - Given the path to a header file, find the path for the associated source file. - - if an external dependency, return "EXTERNAL" - - if not found, keep the header file's path - - :param dep_h: a string containing path to the header file - :return: dep_cc: a string containing path to the source file - """ - for x in EXTERNAL_DEPS: - if x in dep_h: - dep_cc = "EXTERNAL" - return dep_cc - if 'include/api/types.h' in dep_h: - dep_cc = "mindspore/ccsrc/minddata/dataset/core/types.cc" - return dep_cc - dep_cc = dep_h.replace('.hpp', '.cc').replace('.h', '.cc') - if not os.path.isfile(dep_cc): - dep_cc = dep_h - return dep_cc - - -def get_all_dependencies_of_file(headers_flag, filename): - """ - Create dependency list for a file (incl. all source files needed). - - :param headers_flag: string containing headers include paths with -I prepended to them. - :param filename: a string containing path of a file. - :return: all dependencies of that file and the error string - """ - errors = [] - # a queue to process files - queue_cc = queue.SimpleQueue() - # a set of items that have ever been in queue_cc (faster access time) - queue_cc_set = set() - # store processed files - processed_cc = set() - - # add the source file to the queue - queue_cc.put(filename) - queue_cc_set.add(filename) - - while not queue_cc.empty(): - # process the first item in the queue - curr_cc = queue_cc.get() - deps, error = get_dependencies_of_file(headers_flag, curr_cc) - errors.append(error) - processed_cc.add(curr_cc) - # prepare its dependencies for processing - for dep_h in deps: - dep_cc = build_source_file_path(dep_h) - # ignore if marked as an external dependency - if dep_cc == "EXTERNAL": - processed_cc.add(dep_h) - continue - # add to queue if needs processing - if needs_processing(dep_cc, processed_cc, queue_cc_set): - queue_cc.put(dep_cc) - queue_cc_set.add(dep_cc) - logger.debug('file: {} | deps: {}'.format(os.path.basename(filename), len(processed_cc))) - - return list(processed_cc), "".join(errors) - - -def get_deps_essential(headers_flag): - """ - Return dependencies required for any run (essential). - - :param headers_flag: string containing headers include paths with -I prepended to them. - :return: a list of essential files, and the error string - """ - essentials = [] - errors = [] - - # find dependencies for ESSENTIAL_FILES_1 as we need them too. - for filename in [os.path.join(DATASET_PATH, x) for x in ESSENTIAL_FILES_1]: - deps, err = get_all_dependencies_of_file(headers_flag, filename) - errors.append(err) - essentials.extend(deps) - essentials.append(filename) - # we only need ESSENTIAL_FILES_2 themselves (IR files are split) - for filename in [os.path.join(DATASET_PATH, x) for x in ESSENTIAL_FILES_2]: - essentials.append(filename) - essentials = list(set(essentials)) - - return essentials, "".join(errors) - - -def get_deps_non_essential(headers_flag): - """ - Find the entry points (IR Level) for each op and write them in associations dict. - Starting from these entry point, recursively find the dependencies for each file and write in a dict. - - :param headers_flag: string containing headers include paths with -I prepended to them. - :return: dependencies dict, associations dict, the error string - """ - dependencies = dict() # what files each file imports - associations = dict() # what file each op is defined in (IR level) - errors = [] - for dirname in [os.path.join(DATASET_PATH, x) for x in OPS_DIRS]: - # Get the proper regex function for this directory - if dirname not in registered_functions: - raise ValueError("Directory has no registered regex function:", dirname) - extract_classname = registered_functions[dirname] - # iterate over source files in the directory - for src_filename in glob.glob("{}/*.cc".format(dirname)): - # get the dependencies of source file - deps, err = get_all_dependencies_of_file(headers_flag, src_filename) - dependencies[src_filename] = deps - errors.append(err) - # locate the corresponding header file and read it - header_filename = src_filename.replace('.cc', '.h') - if not os.path.isfile(header_filename): - raise ValueError("Header file doesn't exist!") - with open(os.path.realpath(header_filename), 'r') as f: - content = f.read().strip() - # extract ops from header file - ops = extract_classname(content) - # add the op to associations table - for raw_op in ops: - op = raw_op.lower().replace('_', '') - associations[op] = src_filename - return dependencies, associations, "".join(errors) - - -def main(): - """ - Configure the cropper tool by creating associations.txt and dependencies.txt - """ - errors = "" - dependencies = {} - - # convert to a single string with '-I' prepended to each dir name - headers_flag = get_headers() - - # get dependencies for essential files - all_deps, err = get_deps_essential(headers_flag) - dependencies['ESSENTIAL'] = all_deps - errors += err - logger.debug('len(ESSENTIAL): {}'.format(len(dependencies['ESSENTIAL']))) - - # get dependencies for other files (non-essentials) - other_dependencies, all_associations, err = get_deps_non_essential(headers_flag) - dependencies.update(other_dependencies) - errors += err - - deps_file_name = os.path.realpath(os.path.join(OUTPUT_LOCATION, DEPENDENCIES_FILENAME)) - with os.fdopen(os.open(deps_file_name, os.O_WRONLY | os.O_CREAT, 0o600), - "w+") as f: - json.dump(dependencies, f) - - ass_file_name = os.path.realpath(os.path.join(OUTPUT_LOCATION, ASSOCIATIONS_FILENAME)) - with os.fdopen(os.open(ass_file_name, os.O_WRONLY | os.O_CREAT, 0o600), - "w+") as f: - json.dump(all_associations, f) - - err_file_name = os.path.realpath(os.path.join(OUTPUT_LOCATION, ERRORS_FILENAME)) - with os.fdopen(os.open(err_file_name, os.O_WRONLY | os.O_CREAT, 0o600), "w+") as f: - f.write(errors) - - -if __name__ == "__main__": - - logger.info('STARTING: cropper_configure.py ') - - original_path = os.getcwd() - script_path = os.path.dirname(os.path.realpath(__file__)) - - try: - # change directory to mindspore directory - os.chdir(os.path.join(script_path, "../../../..")) - main() - except (OSError, IndexError, KeyError): - logger.critical('FAILED: cropper_configure.py!') - raise - else: - logger.info('SUCCESS: cropper_configure.py ') - finally: - os.chdir(original_path) diff --git a/mindspore-lite/tools/dataset/cropper/dependencies.txt b/mindspore-lite/tools/dataset/cropper/dependencies.txt deleted file mode 100644 index 7154f239f..000000000 --- a/mindspore-lite/tools/dataset/cropper/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -{"ESSENTIAL": ["mindspore/mindspore/core/include/mindapi/base/format.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/json_helper.cc", "mindspore/mindspore/core/utils/parallel_node_check.cc", "mindspore/mindspore/core/ir/meta_func_graph.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/cache/pre_built_dataset_cache.cc", "mindspore/mindspore/core/utils/log_adapter.cc", "mindspore/mindspore/core/ir/func_graph_base.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/text.h", "mindspore/mindspore/core/ir/tensor.cc", "mindspore/mindspore/core/include/ir/param_info.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/sampler/skip_first_epoch_sampler.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/sampler/distributed_sampler.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/random.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/bucket_batch_by_length_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/epoch_ctrl_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/btree_iterator.tpp", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/random_apply_op.cc", "mindspore/mindspore/core/utils/info.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/btree_impl.tpp", "mindspore/mindspore/ccsrc/minddata/dataset/util/validators.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/album_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/bucket_batch_by_length_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/cond_var.cc", "mindspore/mindspore/core/utils/ms_exception.cc", "mindspore/mindspore/core/utils/profile.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/execute.h", "mindspore/mindspore/core/include/base/effect_info.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/samplers_ir.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/parallel_op.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/map_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/intrp_resource.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/dataset_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/map_op/cpu_map_job.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/cache/cache_request.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/intrp_service.cc", "mindspore/mindspore/ccsrc/minddata/dataset/callback/callback_param.h", "mindspore/mindspore/ccsrc/minddata/dataset/api/python/pybind_conversion.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/zip_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/project_node.cc", "mindspore/mindspore/core/ir/scope.cc", "mindspore/mindspore/core/include/base/float16.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/cache_merge_op.cc", "mindspore/mindspore/core/ir/dtype/monad_type.cc", "mindspore/mindspore/core/utils/misc.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/type_cast_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/pipeline_op.cc", "mindspore/mindspore/core/include/utils/overload.h", "mindspore/mindspore/core/include/utils/system/env.h", "mindspore/mindspore/core/include/utils/hash_map.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/operator_connector.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/auto_index.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/project_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/concat_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/services.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/datasets.cc", "mindspore/mindspore/core/include/utils/hash_set.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/skip_pushdown_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/list.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/arena.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/concat_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/vision.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/tensor.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/cache/dataset_cache_impl.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/cpu_sampler.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/device_tensor.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/config_manager.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/python/python_mp.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/shuffle_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/sequential_sampler_ir.cc", "include/api/format.h", "mindspore/mindspore/core/include/utils/hashing.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/mnist_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/tensor_row.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/cache_validation_pass.cc", "mindspore/mindspore/core/utils/trace_base.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/bit.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/status.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/cache/stub/cache_grpc_client.h", "mindspore/mindspore/core/ir/dtype/number.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/btree.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/ir/tensor_operation.h", "mindspore/mindspore/core/utils/label.cc", "mindspore/mindspore/core/ir/func_graph_cloner.cc", "mindspore/mindspore/core/utils/anf_utils.cc", "mindspore/mindspore/ccsrc/include/utils/utils.h", "mindspore/mindspore/ccsrc/include/utils/convert_utils.h", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/compose_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/cache_lookup_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/build_vocab_op.cc", "mindspore/mindspore/core/include/utils/signal.h", "build/_deps/jpeg_turbo-src/./jerror.h", "mindspore/mindspore/ccsrc/include/utils/common.h", "mindspore/mindspore/ccsrc/minddata/dataset/api/transforms.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/path.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/data_queue_op.cc", "build/_deps/jpeg_turbo-src/./jpeglib.h", "mindspore/mindspore/ccsrc/minddata/dataset/api/text.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/execute.cc", "mindspore/mindspore/core/include/ir/kernel_info_dev.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/add_skip_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/tf_record_node.cc", "mindspore-lite/src/common/log_adapter.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/filter_op.cc", "build/_deps/jpeg_turbo-src/_build/jconfig.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/cache_base_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/take_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/callback/ds_callback.h", "mindspore/mindspore/core/abstract/dshape.cc", "mindspore/mindspore/core/include/ir/dtype/type_id.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/service.cc", "mindspore/mindspore/core/include/base/user_data.h", "mindspore/mindspore/core/ir/meta_tensor.cc", "mindspore/mindspore/core/include/utils/system/base.h", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/py_func_op.cc", "mindspore/mindspore/core/utils/file_utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/task.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/data_queue_node.cc", "third_party/securec/include/securectype.h", "mindspore/mindspore/core/include/utils/compact_set.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/connector.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/system_pool.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/memory_pool.cc", "mindspore/mindspore/core/abstract/param_validator.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/tree_adapter.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/mappable_leaf_op.cc", "mindspore/mindspore/core/include/ir/scalar.h", "mindspore/mindspore/core/ir/primitive.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/data_utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/tf_reader_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/sampler/sequential_sampler.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/datasets.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/batch_node.cc", "mindspore/mindspore/core/include/utils/visible.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/tree_modifier.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/epoch_ctrl_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/ag_news_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/random_data_op.cc", "mindspore/mindspore/core/include/utils/counter.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/map_op/map_job.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/random_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/device_queue_tracing.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/execution_tree.cc", "third_party/securec/include/./securectype.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/post/auto_worker_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/transforms.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/task_manager.cc", "mindspore/mindspore/core/ir/manager.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/iterator.h", "third_party/securec/include/securec.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/cache/dataset_cache.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/cache_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/random_choice_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/samplers.cc", "mindspore/mindspore/core/ir/dtype/ref.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/csv_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/map_op/map_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/monitor.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/csv_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/shuffle_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/cache/cache_common.h", "mindspore/mindspore/core/include/ir/signature.h", "third_party/securec/include/./securec.h", "mindspore/mindspore/ccsrc/include/utils/visible.h", "mindspore/mindspore/ccsrc/minddata/dataset/core/type_id.h", "mindspore/mindspore/core/utils/ms_utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/profiling.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/runtime_context.cc", "mindspore/mindspore/core/include/utils/convert_utils_base.h", "include/api/status.h", "mindspore/mindspore/core/abstract/utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/constants.h", "mindspore/mindspore/ccsrc/include/utils/comm_manager.h", "mindspore/mindspore/core/utils/system/file_system.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/global_context.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/consumers/tree_consumer.cc", "build/_deps/jpeg_turbo-src/./jmorecfg.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/queue_map.h", "mindspore/mindspore/ccsrc/minddata/dataset/util/log_adapter.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/dataset_op.cc", "mindspore/mindspore/core/base/base_ref.cc", "mindspore/mindspore/core/abstract/analysis_context.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/input_validation_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/skip_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/tensor_shape.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.cc", "mindspore/mindspore/core/utils/any.cc", "mindspore/mindspore/ccsrc/include/utils/contract.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/repeat_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/consumers/pull_based_tree_consumer.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/auto_tune.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/nonmappable_leaf_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/data_helper.h", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/duplicate_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/build_vocab_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/de_tensor.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/circular_pool.cc", "mindspore/mindspore/core/utils/ms_context.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/tree_adapter_lite.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/cache/cache_client.cc", "mindspore/mindspore/core/include/utils/shape_utils.h", "mindspore/mindspore/core/include/ir/dtype/empty.h", "mindspore/mindspore/core/include/utils/symbolic.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/cache/cache_fbb.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/client.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/image_process.cc", "mindspore/mindspore/ccsrc/minddata/dataset/callback/callback_manager.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/repeat_op.cc", "include/api/context.h", "mindspore/mindspore/core/include/utils/flags.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/jagged_connector.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/take_op.cc", "include/api/data_type.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/deep_copy_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/lite_mat.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/zip_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/getter_pass.cc", "mindspore/mindspore/core/ir/dtype/container.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/data_schema.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/slice.cc", "mindspore/mindspore/core/utils/trace_info.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/mnist_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/ag_news_op.cc", "mindspore/mindspore/core/ir/primal_debug_info.cc", "mindspore/mindspore/core/abstract/abstract_function.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/ir/data/transforms_ir.cc", "mindspore/mindspore/core/abstract/abstract_value.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/wait_post.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/tensor_helpers.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/allocator.h", "mindspore/mindspore/core/utils/system/crc32c.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/sampler/sampler.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/c_func_op.cc", "mindspore/mindspore/core/ir/dtype/tensor_type.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/epoch_ctrl_pass.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/opt/pre/node_removal_pass.cc", "mindspore/mindspore/core/ir/dtype/type.cc", "include/api/dual_abi_helper.h", "mindspore/mindspore/core/ir/anf.cc", "mindspore/mindspore/ccsrc/minddata/dataset/api/python/pybind_register.cc", "mindspore/ops/op_def/core_ops.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/rename_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/filter_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/skip_first_epoch_sampler_ir.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/connector_size.cc", "mindspore/mindspore/core/include/base/complex_storage.h", "mindspore/mindspore/core/ir/graph_utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/text/kernels/whitespace_tokenizer_op.cc", "mindspore/mindspore/core/include/mindapi/base/type_id.h", "mindspore/mindspore/core/base/base.cc", "mindspore-lite/src/common/log.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/treap.h", "mindspore/mindspore/core/ir/value.cc", "mindspore/mindspore/ccsrc/minddata/dataset/text/kernels/truncate_sequence_pair_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/image/lite_image_utils.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/samplers.h", "mindspore/mindspore/core/include/utils/ms_utils_secure.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/skip_op.cc", "mindspore/mindspore/core/utils/ordered_map.h", "mindspore/mindspore/core/ir/func_graph_transform.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/queue.h", "mindspore/mindspore/ccsrc/minddata/dataset/kernels/data/one_hot_op.cc", "mindspore/mindspore/core/include/utils/ordered_set.h", "mindspore/mindspore/ccsrc/minddata/dataset/core/types.cc", "mindspore/mindspore/core/ir/primal_attr.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/semaphore.cc", "mindspore/mindspore/core/ir/device_event.h", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/source/samplers/prebuilt_sampler_ir.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/perf/dataset_iterator_tracing.cc", "mindspore/mindspore/core/ir/dtype.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/batch_op.cc", "mindspore/mindspore/core/ir/func_graph.cc", "mindspore/mindspore/ccsrc/minddata/dataset/text/ir/kernels/text_ir.cc", "mindspore/mindspore/ccsrc/minddata/dataset/util/lock.cc", "mindspore/mindspore/ccsrc/minddata/dataset/text/kernels/jieba_tokenizer_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/dataset_iterator.cc", "mindspore/mindspore/ccsrc/minddata/dataset/text/kernels/basic_tokenizer_op.cc", "mindspore/mindspore/ccsrc/minddata/dataset/include/dataset/json_fwd.hpp", "mindspore/mindspore/ccsrc/minddata/dataset/engine/datasetops/source/io_block.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/rename_node.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/device_resource.cc", "mindspore/mindspore/ccsrc/minddata/dataset/core/cv_tensor.cc", "mindspore/mindspore/core/include/mindapi/base/shape_vector.h", "mindspore/mindspore/ccsrc/minddata/dataset/core/data_type.cc", "mindspore/mindspore/core/ir/named.cc", "mindspore/mindspore/ccsrc/minddata/dataset/engine/ir/datasetops/root_node.cc", "include/api/context.h"]} diff --git a/mindspore-lite/tools/dataset/cropper/parser.py b/mindspore-lite/tools/dataset/cropper/parser.py deleted file mode 100644 index 602e05f46..000000000 --- a/mindspore-lite/tools/dataset/cropper/parser.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2021 Huawei Technologies Co., Ltd -# -# 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. -# ============================================================================ -""" extract ops from user code """ - -from abc import ABC, abstractmethod -from functools import lru_cache -import json -import os - -ASSOCIATION_FILENAME = 'associations.txt' - - -@lru_cache(maxsize=1) -def _load_ops_names(): - """ - Get the name of all ops available in MindData lite. - - :return: a list of all available ops in MindData lite - """ - with open(os.path.expanduser(ASSOCIATION_FILENAME), 'r') as f: - _dict = json.load(f) - return _dict.keys() - - -class Parser(ABC): - """ - Abstract Base Class for parsers for looking up ops in user code. - """ - - def __init__(self): - self._all_ops = _load_ops_names() - - @abstractmethod - def parse(self, user_filename): - """ - finds ops detected in the user code - - :param user_filename: string, name of file containing user code - :return: list of ops found in the user code - """ - - -class SimpleParser(Parser): - """ - A simple parser that works by string matching: - Code uses an op if it is found anywhere in the text. - """ - - def parse(self, user_filename): - """ - Find and return ops in the user file. - - :param user_filename: filename of user code - :return: a list of ops present in the file - """ - user_filename = os.path.realpath(user_filename) - if not os.path.isfile(user_filename): - raise FileNotFoundError("file does not exist: {}".format(user_filename)) - with open(user_filename) as f: - data = f.read().strip() - user_ops = self._simple_string_match(data) - return user_ops - - def _simple_string_match(self, user_text): - """ - Find and return ops in the user code (provided as a string). - - :param user_text: string containing user code - :return: a list of ops found in the user_text - """ - processed_user_text = user_text.strip().lower() - user_ops = [op for op in self._all_ops if op in processed_user_text] - return user_ops -- Gitee

      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 @YU-ZKAOx{Sjt@fTb zZ(mEwlYO17bZ%&<9yxaAR(dC10+llHOu&H4h)N67LTq)epF44MABcSS>{q{N3ZmbL zn79Ob`*9@4w{l~Yx(weNqFc+Q6(EoF8t>mbYHDK~LfBf-#e@kg{RG|H%bH`MU zXS`#A5A(5naORkbs>W&adh)-J#%0^br;mUBv$HNcD!|KB zOB3xkRZeK7p@@taS!h%2|MT;|{-dKhDcFZ+0_K^3OR+=nOuztzFl4jb{_5l?Do_dloPR13KnaP=zVl4L zu(DE~3AnGlHa{l7;>oS68tP~K(&0byOu#sPAO?;GL47Tm-Ufyiwzkd=CdL-lATkHl zxt~8BIx(ZWw+l8zP*#?o91-B{?d^j;0YSkbAtCf+QAGgNsWpr10I|+Yif2VQk&!6j zh$06MOW^TC`yhhwltF<(!Od8hP}9>w6KLRa03?wNr#4|ZAZcT5fRj*QKrSYN_Sghy zf$C~vfGfz$&cp|pk_>DSfh=2zcA~LKOwLF9-MU9K|+RSFTw4*CCV zMs^m`2naaP{sC`PBxJn@(EbZ@a{u#k*ek>G)F%XZeRfXZnSgmF;3ax4uI`>bet`I* z;4?x`_x7EOtCp-?Hbqfs%ov3UGuG+bI*@@Q1Jv4<{pk8dwQURM&7Yw#dhE!N3X^8; z&^5NSb8>ZKg-J~!n;RF^wyc~#U3s*^n2{rurp!I|^aUn3yRou!adX}SwY_WR&Yh{G zFcxF-?6ub(>KIwrIJi*d0uEZu(VCk#E}J)X;shl{r781QoX~po0$Ob605P6#JlnxX zRHwX{V{y^%Vo6YGzVXS#3 z;PZR7Y+5pH@`Ulz_QW?(padu+hF))%#XfZ~zNT^bw{;69E00x}Fl}dkT@|7=r2;0G z)!17Hm^?VRck|-e6O|OkC{3QB4=zwpeNmSf1QSz@U2;5b?ApG1;Y?-4v17)Lop`jk zf>C)wjv}-Wi`YhYEtOqs=S-QPGP``jo_f2sNF z&I1QkE?qcv`qDL^1E1>Z=^KE^6-{|49o|@*os^ZwGXb*>qdXIEHE7wWnLFy4=>g%H zfP1@Igq6j4sj&fW&W`rBW|n?IK_Q``B5^}^$D6-@>hF*>*OUq};v;=rT$~*2tnA@E z1Ox?%z%0<#{rmeqnY6L2BqupK)Y~1Z3XH0QHAzr*gt^a{UV1Fu?fgf zU=x`Ec#*|e zpVfS7pYayleYg0~*;#DE-3z1-~~YHDhzDJ{q=ssP1LO%1&O zEKhPQKw?p4R$91^qpi7T4kdu;^^qJCaATecI5y1F#Ps>yE9W$hs~iGFv5A{r~9h}k0vL0`tATi9tM)&!>o0ra? zJ9GZrF^$W29_yJ{+Bz~h&b2wAE*6H*weQ@xaqa4*(`T;SdHDRLiG`&#wZg1UCz8>2P$MlP|YXi2f=G^;x z7yUG4!dQioBgf9TTEsH}@4fX<*VxPoy�bkSnTcOUF+hH)iCB(O@ASqdaB7_7j)x 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

      wjRzWA4WB$pu7VqfDk8%}ER46)zxBYy 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č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�&dE=Yy<}kq;GEz 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%0A|5mXe_ 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 z }Leo^tsX@v4q8ReIh=41Kt%#qWM*1AtNsczr0;q=)DUSZL3NvXK*HPJ5F$u7n( zcJI}EaZl&`@-=H$EmD8>^8Tyf=(t3bK&+fJQ(e1mpZM_m5YG-xpVQ54YzTcW8du@vpA8W5{KYODK zrw;DfdRoK8-Ol`hX;4T6?#J?2cY};n4@;9sFFWIF8e3I2Yh1Yc#*AkI&fuAVN&oRb zZBvxsCn<_bXz%QH(A0j;k*$4U0&fu)*L8Nc6@{7=)*HU9>FUIaNGX2Y;P&b&WBaPQ z)C_^sS+(^qd!!i67SS54R}x86Q&~ukZ``iy;aA%L1CNp`@!sviI3G_# zV_j>T{8Y26&(+rG>uAQ6l$S7q6XZM8#v498cg5Ay!qn0JfrarMjYF?IE#LWNGo0Oa zNpoJb#kn6J*n~Qnyu5Pq&Eq>i9`=d1c=0$YAt5$V-gaKqNaHz+IuSB_@_=9z$@Iyl9$of|(&fVV}F1Ly}C z$IQJA?cz3nIJbBvV4ewh(Gs2sm}deOSKz2HWW$qf%BpK%qxIjowaVu=nST%68 zz`22)L71HC9=PEhz3=;bS}UrGMP;?*z97s6&V#~+_kRBT)BBFb>e?bt1$IZj-`lzfO(qFg?SrP*&ImHCI4qx>faySgw62R1-d=TI2KMjZe zV*zLeL1b-n>%ZwghLa86)KF2B>>cFg8!xJF#`rJ#-;Cv@UD8-tFV2YY53)16qiq>p ziW~{bVJ7{@@b=!W`hudOwD2HDcUSF8=k6K@WeW>X2}Klyn7#Yw_ma|rw8)s0uwVzn zcV^EXKhyQkU_qb)A%=hWxjM|=J0v0`EIKJQ+}q}z&YeqVtRmx+QqwcD+uA$2s{K7& z9lS!K;}a7iJz}GLw6$K`zxU2JI5Ivlxvi^MFC@jwMqk&&CLl3AGukUO(f_61i)(vt zyLktMMRlgHTc>ZRb^GS6JDQITJ(CJD!i{~so!^{2vtP^E&C{1>0w&it-9MBaG33wY znSd!#hi3vVFQ>agA`Y-}Ft~m0#EGq|=PWyED+C?o`nd}i&Kx+SeqiZ>MKfm~ zc>M`48g59Q30NZS z8|>?9Yp5#CO$ZC{_HcJ|cXctGo=U}#XKuw6Y62g-^IbFxws zqC*3GeY`w9+>2#Q**p_)wB74RH#ANi-n09hNmec8a8Z<&<+H!xJGZS{vwH33w^+UbxWeYIDoBeD46x99eCxvT-5XWcu3EWz%~tyg$n(o-f{Lt+ zgb)uKy~nrD9NoHpK#SFT>KmR^Ddj^vd^nYnopE|&UQmofdOwIpA)ev3^}Zgv*2 zt5+6ei-a*=mM`yX96Pjg{pwYauUxHq56S3Bpk@|}bMr-|NjwwqC7uZwM88y^z%v0? zRss$Ycqah!Bn7XFKd7uUMPc;t5hHp;`p#2nNCPl?*YN~5i&Q(^P zI_kUc{_-8<-+e!7jQozv*Y9cJ8Wt6m+HY93YTlgbN(#gO0yzyIGmd8hwz09X2E%uL zA%MXE>y?u&5D+*J(1T;6Lw#TzxVgHBg!w!ZFc?6oefae8(+3%(3qV}}InJ1hIpN^P z&mEQ3rL{ePj6u41Z!faJaWHEqj2IvU4Sc9}xp8p!=AFkM3EqDmfEY&Op#)7OzEbp{ zR6CZ1%QFGPqstNVf@+*Kj^~$h=JAi59y}8;ic1FCEiRwiwQ<9eMauJ(Cr+54sIbzv z9Z8&2%}KY`V4A_rll!))Zl0&SZ~^2KCr+3j3rJXsfg+UE*X8;8!HFHaaj=-KGFeeU zVZ!9e3dcpL@Wkc{d4G$Q{_E4*w{2cDPi6ANiHZuCVB*5ij7&jxPA-j?8a#Y(Vy)`x z*)x?UPNMlIO`5pOFDiy&AYfqa9kBfI%BFoQ=ggZsaq^_e=rU=t!hY|-$k_O#WTIg1 zt9^L+z~<$1Ri;myI%O)lOqww3v7=8YLMBP*EKRdDt3FbKA5C~nD zx8B2(+jp*Cv0P=^Li4Fx&q-{-A`N?74&aRP_j0q4- zzo^rdF#$?|I@VmA9O7*B?9MfvP@V}`!!T1IfB}$$>)O!{thUoJzKkUy*GT`V{fnIK@KkQ_#Y`dVqUEdpqsFJTp`p30vxgviXbLPKJQFak zMQ3SadCLcvGuxIgn4vgs^k}&W3KQquN=r*i&&bTg{@zuPmt0d}aA?DP~-~Aikne}P+1`^XE4WI-JgE_b&UjynAP0ib?|=K_??1oq>uW7e^fY^}bx-5GRwiX2 z6y{4&)7(G!`=5XRC#VQ|8grxF&7W!BId}ScWC51=YymKU0HpcXfBf_R{_SJGv??#! z-<)Rxe(>Db!UooXx3@2R%{&vZEF{4*0rO12OJ?y*z!z^kdiq-5*xb^_)}C#xRJKxE zUzr)7o|zEn0(4_@D_eUVAoS)7Cl8!X=}#b zjLl74MNp-L`oZ>Rt`FCNVAd)~UQ&#KLmN0YX{ofW8Shb7O-QJyfgt&ixT~`kOyEg{ z4XrKpM7suFBqZ?-$)%k+hNrfwE?>Ftx_t@H1UzA$en?PAXjmAbzqB-ad*{@oKiju% z;j{_zW5H#*1(k_q&jn#)O z9>~zZ;sQ4YCnqr_Z{Rx9Qced06bO=BNW+p~p}K&_H^*$_eFnSdLcq@ADs{O$AL zKznTs&jg&A5DxwJaCZmGravT&q6ue78fjkp1@C>P; z=!pX;$Dx`COPP;RgeH?h4e2Lr+)z$R&qohvFOxG3#{;@h(hGGUkaDgAWBQfpKL>rt zbztg`8JL_YIO)IaGm!qHLlN{JZ~9gL;T0g=r#7Vj7%!8gwFQV^>@a&Q@BUWO9tIt;op;^K>xO(Ybo=$5*+Ofm~RSmq+u%@!4G8P@0nz;^J!h zRP!1bzO)hwaK^zR{ZM z;y70;{g+Rk=xA%+x%)u-*~>Tj#%AW0Lvq{}JQFa_1dKyYT`lu1GW$SS*aWkK)Bl!6 zVVtjLKwN$cm~L>u0p}|8w~^_8Lw$2?_}wk*7tEe9Z_WMKni?uWV){+?A8eB59PI;( zXDLk@FE@Jh`02L_Ylq})cwKYa^&{ICEtxDoe$=qhqesadNUvf%#QAw7uWuARK7IY* zlIc??D9Mi;F>2&EIn^LAy=G*jr;)s_&gR8~iw74gO_?xBe$=QDC}9{qQ7<(iJ}xef z-oCv^*W6C~(3+(`$jOZ!F=F_L5o5=Hza=~*EIc9t;zFC(Z@q)H4^E#mY1BAWNI*Vv z^zcPC&Tc#tuvspn3oa}?Cas`=;Y`Sn491G`EURD z`_CWV_je*BT2)yrEXc@+^z(K>{iuVDO=M>O=imSF=Pw@zx*O^maVHD21?kC={vHTY z*;`v!2PF3W^3Q+#@$37(j)J0^^17O$d_hWlSb&=Yn69nNt^H&AKmYH4|MOSSAXnFt zV@Ft&lN1r`<%qd$tSoJPL;45${^#$%eCPueFKnH1p&&Irj7nTBtx)3X;O5iMGXeMX z_90WVn_wy_76<^}GEjAc28habh=%|P@;%5AM$&703rs~S1unzkl=XpX$&mQkDJd2( zz%V!K-~glj2S~&WSC?F87~GEe$wLReB7(_aCt%J)CX-W69XldZ1p{Y@Gt^&-GjeD^ z3`h#8H!Lnmnac3(Q8v!kMqNqF%9@+N6wZNrk^Xa?C^XMA0T-cUo7|N6b|v+t!u0s) zsL*hKCp*)(FSM>*xpeuVO>_aUq>DN_B~*N#k`Nsh78c-aZJ_^F=gw6Pjf)qrXxZl! z=67}?fLv9Onj9Yz5f$KUYha+Oee2rA3+K;W`0>ZrDc~I8nSdLMGGlz4?aj;#-#*v5 zfBV*rD;F%DxTt$FX>t=o4sA3oJJFtf5n0ug$& zRb)hXJK9>AnHau({qhyy0gTKnZ5;s9L@AQc9zX!pmW%ST(h_4MLW6?>{QVHW4@MRR z3oiq}gWTg}IF?bTq{R66__%lwg##v;8q#Sg3!t_didlvre|8pZx3sj>)Kq9K8Z?J$ z1K>pj`W+6BMM5kO*#dzei#+7qo(i2K?-rer0WnV2C+h$5nD3n_9uWu(b)X*H36mR3 z9Y_v^W)qM}!}-v7CSdTu^z_yzSviB^JXSKbHrCq=lqU(;#j4mi}Z&QGl`q3l% zc5Ym?YRQTS>_2dL-_9L7wyavdeA&`9CoFT|CSdyC)syl1 zipI&KhmRdSwtvU=wJVk`m^)|of^82xGT{yub#@oN)4tC$0rO12&`s>zH9&O{ll~*W zBL@or-G|hGqKB=Y))+W5X!mEw%R-(B_)BIXEwiMy+)9c#B+mrwYvKO734o`dE%_P79-r;|y@f>CD_vPXTUn z5TO%zd~xS;nXf=-r9(9~X}Fl-L&E+5HVqbj;1&Tujp0;{I~#S%GAjV~IL`!3HWu2y z>wjyqpnNES;A;1!1v@9<9D{(@ki4pLXn$t!0bc>+vid`I*s1)gpG3{YaBLQ1|c_!eMi`Jca>=+!E zl9`gUAODxq4Sro-PF9IzH{ZWB{QbZ+;IJI zn>5Yp_O6{L)sG)OxO?y6)5lL7+qYx&n&tCmD9@OG;O3Lo_Ez7gSI=KKe|i7GBU^WE z-Me$+$}KCFsZ3LzHGktdt(Vxnnj;<`xpa8{hHXc7tX;3VY2oZSvt~@0GIPm>Q}>_g zQWa@y-n$=nZCtfy-Qs0ymM&0HnmJ>t%EHyVFK9k{^#*qo98Wc&I>&eKQeCuq`J#FA z=FOh7c>NCbYY(2i);EO|NYO2=waK>kj&5JSeE!@8i&t+yeD;RcGd&Y4M>l{}^Gv{` z;G{rgh@dfzCwCst1l-g}7lmg69_a7+(9>2H=V`A0 z8m8_(g5vr3`9r5TKh?|n&67ul9zoIZ$tkI+>5zAHVS>KCfx&^n?y8Ik57a7~d4$AZ zd`fDjfaKjU;QI#p`vwMjnniJ5HdgMz(Eum`ty4}O z)o&lU4qOnT`5QS~o45i%Zh|J_@){aRj@*up)=V7K*n431W*nzva&K%8(#|0{*&>*q21`47lS8sH-EJM&Y1%4f@6Um`K+eQG z6Y!f=JQJ|&#$c&tio(XvFBnA0#W5>($ zOu&VC*>La)1lif-e`oiY?2(nZG!V!?LU3plD3Te^=tlb=wpBVI^Gv|3dYNYemPo}3 z_Imf9xH_2_*_de?zP@+;%ySo8vvAiysQlA}Tf8(Z(mA}2|v%A-yXx=z`(lyLl z|8-_gPHsV=w6!8N)YbOo+hkv>=jz9g>^gR6(^^e8>onJ0$8TObdE3O= z#XBer3fw46aPsywx_|T0!@GCx-@f$Yi)YWCzj*yt zPft(Z=+(oQHm=@&!6b*;H{|C<*;slySzFr@1^|lxkv3MI^ZY(lb&nrSCTi>bLKh>AFdH;82TU~B8N@T@Go-O+qok` zd*$^8=IWD;qu05W77H$Z?upUq;N&Esizt&elY^qCHFbZyl*u6?DV@^E3&=A8^Gv|& zHe9-TLG98LT_X!S7pQkeh^wWozr&-G7cQRLyMOQAU0V-dK6~i=O?#f zFjR-4n9Ti$pLr%=at>hmt3`N2+Fns3@=Mb>X&rif{)`dwGu9lv$uj}-Ou(=7cqU+; z3AnwhHKeGyC^If9A-$|w+SyXmB&o~DN(?Y^LK;aza(DgNy{@57*5>Axc5dN$O+6hg z#f|lq>E3Q8o?#J@k#Ppv$q`;&fzgTJCQMDqC~F^V?Wk@PmWqNceL_P+Uz>*ng~w)! zDp0rx(kgLTx%AWL*2b>tvXoFu_u!BhHa<}aIXn|E9lo(vbFGIW0-ry>FSIfs19&K~ zC3}V`iOLL~37CoicqU+;37DAd7~M0A=^#U@wz?%RvM|I{ZPkT0RKSd24z!3ns<8tj za@4Yd3;Zq}IdQj%gphLQHuPcr8d~ZSVq%}3RKG|eb|yyw4Ee`cgPTm+ED*TdJAH;5 zL1#R4kcnxM=BjdWwAQH;Y%ongZ!t8QI@B~a<~>zcqZ20^Mm;z=y+A3mRb{qnN9og1 zpRWz4S^C7y^_9Y?<2xu&MSX@EI}Fnw)m&el9=UTZoposh>w`x+9wSW;r`v|=%sX4& z(C>6uX3yL&I2WVU*Hx8fW#vLN)Ho^S_@N=k1}Hb8e53?&)ikUZ{YJ-XF2jar0_K^3 z=Wf?~YhdYDi|+yWsfpBulox>+3Y-9gDe>*?;}@3lOu#%7uz*U9ih$`+ zQUZ52zB6Nxz zYD@udXCCB4bxEzVx&hdn6{12>d6k&nLnuU~269eZBLVqVTSFyqnxa#SD_ISGL9U>r zlnt+FM2bm&kEB5?%1R9N4agurs3<>MKuoOoOfA3s`qKwc?KX%D1xaE4UY>EN!Y?Yw z&gMqf|MuH2KYe=N*Ioy**u)Ufw7N$_UYMKBF^vxX@!QWoeH`d%t1r(1ORKMkn~Q6F z3CS4~VB_z9{q5&Z?+3cuYD&^0VnTeq+?-uJ^NVnO=r6D7{`>F0{q$j=ud_)k5+p^2 z_<6azxH`ubz}L$&0XHL62HM6X&t*>w5nSh}k zNZEyhzZf@l7ARul!b1WB{C#~y#U(NY!ChWiQ37T_L^sn@lH<8p1OeheX{nV`oSI` zt|IA*m8;h8H3cPhX<0>OdAN_gjrpqwx6U8mwQ=pTWy_XBzFKvUu8oZyhF4ZZIN6%L zd2;uX#_?^ctClTUvTXUvm1{Qbd}?ZHf%mTvr`X$C8ED_Ts&RP7hLuYfFJ8KQ#j5q2 z4nNV=H=-M_qR89EME}Wyn->pnU$=5O#xGm3X2TYZM^9e9VRvLjvXzN}&V#Ezs%=}l zV);_cze;uE-s|@sK7CQb-LgIgZy(*_nSg-|!!rSoU#XP>nmnEfI3y|}9M)Gw(SwJ! z9?v(-nlSQvqT>Ay?fYTFN6oYK^YKF(LPgG{!{=`7-8fxg42l1Xbs0Wm}$4)=^G-8p|nV2)v`sil_!lE!{pTG`{5(U zPrZzYIT?TDrptIH;B4d*C3>4Y)za2A(0l$!^SKOWwrtz9Wbqv3>B`fl zsi;g>QH{^e%`X&+a8U<8I^4Ls`}l?>%U8^pHGR6Win7X#A5O)k2y*g;BD(PJn_k{k z->bTG-h#QaX3mv=FFTqbB4;a>Faddf?`v% zcqZT~VtQo_jF*(Yj+$XAm80|;@)Oo|BUbUP9Je&?YffGdZ(&y#xLVPOag~bnkWhpU zT&W)9ja^j0%vu-Akzqy4cn2z^BYAfh8sp%k9C}K2=bk8^hCHeqlP|G!a*d@4u#Vv6 z1K751{zsAdHMxI90xi6 z$6eRem>uZi;29nn>hI&@kNWUaytc5306FDB;_HDMvC2d5uj^o}vaa4@bS5#zQd4Zv*=#U#x|BTp-(q77+F`RxX|ixZV8HnH{R@7S2VwGvUZxV#*(?7l&s3Am@t z_w1&H3+BvTyz$tL2ajLAGqrYb^?;sZ`RwiOfr586)<>lp4BCTxi zgqnAddb1LEEDP zs9uU9#j-x%$~hrz`D6fa9sWg5U&5DEEm;TTOnhYmg%Fv^t?eCM91}PzUHHNTidkFh zOEO~uTwTJ-xV0K3!dOc2p=f4Qig(l&ri41ay?0G3pt%EUIa{g$V@l$#!NERhZ9!^; z^V{1RSFYJM0N0U?Z=m`DN$01ZKJ-Z{5j%8#eM4RS)MZynJHh-7oc#T-zy31NQk)za zVE6R$X|>ZAZl_2Xnn`^R;hwA~M<_=%Nh#^|!zOqf?v|(_p zO<87COeLs=1+={IOu*UK5&4`mSphr>BSy+inYrxf^?TZ{^o^{nkPC?HrgoqG8`dmR zo~)oKH&#w*#?svvZ)-h!V_WPfm}dgMevM}WrtCga{~W=Pev>_nwSrueTn?U?3eI>Y zV79(UJ7Qkg8{IyCY?tc7sfy#}CMoa9tF9n0b`g_zRN7hj8$UjBV9S!(lNIE~DNLQN zS1Tr>VnT_Lv`bTsoU`5U?%lC=(F{fT@#DsipL{|HGMIwA9LQT++k?$x8r&YL?Oi`d zX_CVDF>-Qpsu6j_^Or{FjJ8&9--1@Vho|?iQl30PVcb~x@$!ouQxf9h<6_}aZfR}y zipcK}K0Uf+@wCa45R1 zz0iHDr!Omxz~qKIiP+4K$LF1Zmv0p>elv+yeh%V~+8T@pei+{pHigcl{vasxD4X2y%0Fwsnlp%gzMV4Ca1^sON9L|MKolx3HzA zEGH$_-`&aH&e}6IIXNjg8Q~_L37CA+5(yaQD~fm~V2(nFQf$adUnSP1xcBljCE-9~E|Jg{U-1JIL^ve zgELN&4VWQy5^ zJ+b7ZL^_1if5aOC&4ubR0x`fFfGAE+O<@_#lz z8PSi8YX5B>)pSnd;ihnXD?qFoAOM+tSBFxd*tC`jlcmc+OB_>DPCCAQNeCuB$CA%1Mohh%2baYDW=So(Z`1fB*f{+dfH0eQje+X+cs{ zl)saUlZ~aNm6ff%vnLn^{`S{j-gb-I8fq#_3JNo#0^D4jkTht8^S2Gj-@N<$@r_I* ztgkLBEXhfY4EA$(c5$?`wX?Nx^6){9!nH74xOx7h8w>Q<*w~AykRtKCQJY;%63Yd16xJi(m0tRe*ThHWLG2ubE z@r@`iim}Zl=}GaCL7o;S&z?ThcgYe;fG1~|3t|@#*Olca#>R&EdpOz~zkG84=52!z zo(Wj>nA#;nOPMrF+}T*19vR|n?_qCY^zhcL3mTdy)s7!KcKqaReN(BdPS{pc5FZiX z=45B`+VJtsOWIl|PpGS_scBr))i)z>e`7^13MaVOTN#?YcyR5~g)>@OnkTiiF5G=; zWPu;Ii)R8RR31g&FrkMI(Hs;A9%#&T*0|^#&jgHADO!g}$pIzkmhnu$Q5JWv>ulV9 zVE5tu+c&RU`QzgGvq97~WA?0hmz|~60SRfYx;HNF-G5Z|$ibhtty#Hr!R+ZXAfGko zhkF*1W~-PmXZ@Sns(TNr9^LmdhA*8z8<~DHXU(3oL-)B@;_dHc`}mrc+OFM)c5dCW zcGb!y^XAQ&gJi+kOEhjh7R&NHY@XlN;hBK38}UrQ6!79e1mq4Rf1n&dMB6{%_Haap zPh!X&&{Y4m{7VP8#i>hsR$et{Tnw2H>=H<`VDR0R@`sMaO#a2ZU=cg?LOisi`Tl0B zcxGTwf){IyG7(k^D$=!!`yFnJp0fp?4n0g>2h#u(I zll08j#|M_F7&ghU5rhsT$B=GI-C#doVMbhqP(%qRL*E1myL$Tv8k23@e0|#nK8dTc zi{brfMks(jgG3_h=^J<}%1`5&fc*pUZShRNsN+WU3kjXm+(apgKmws=4_strG-H|7 zEjJ-3S~|?b31=OIG^S-;1Xh|g^ypolC+dbiG_aH06>KYin_G5m8&nXG#1+GmhifkKP54J^!px}CyYGo5uS7VOr0Q5P2Y=+LS~Tkd%! zW)%o3Y8qOEZOz4g1{XIkoH=dfjc1~QB%}R5ZReSQg{^gYQ7%SL9zTC&WMXa!?;#}; zhf$Urw4wt)DeUvc+VY|t_=4hCi%&>MV801cO(3NWhH>tvXVaO=^0c(m1{U{ zGKMf%wTy~~5O^ReWkrfs(@MXyIKyDj3+*xBgIJfi4O!U?^QDoki+^+beR~@x z=Q@!MO2#QVQU|grC`s^(ZAt^V9(aMT1dW5>vWv5aFCEAu@h_M_H63Ry7MG~xbBqxiyyGPX1 zUhQ`Jj5A%ElZ)}QlbB}$=9z#|ZGc)2mU9uQDw6nFSxd|n=3quO0km$m(vx?X+}EUt zm`t<*z)#%G#5D03&Ycz%s3NHoo7w0^l|o3)+{+|KlgoymXgG+)z#PVP7#6S>G^;UV zp*vVh$U)vTB&T^!Qi!3u*u$uV5a7O~-_)3skbA&lf`$cy{^Nw~QGN=*QpI}EgIvzt zm2M!v5L+5pum7R{+?{Cbzt(?NTZl4jxowbrZuY?b5}pYdIf82=v+@cGa; zo04{k&CN`&Y?z@m=DRUt$IUX0h>A~2Nlr=2$YgR!cg<6~5Zz^p3ZqATH+t-NZNGp} zDv=SNm@I~e0?TT>yWNGOa}-C99yRK_vEz2wyQ4!?bWCgIiU!JikvK~~cL@xhY|H*kq1~6TLWn6Sa*(9)Tcl@-U2Ulov;P5`guw9G6{E@{`k_~6pUnJS9o6ciL^ zJoEGkj$}NO2}~}Ql^%ZKrMp5|MG^T23d+ZA9NoSBgAn14BK?<&q?LxoXAkjAz+jjL z46U|;X9A|hf(|`Rq_1?qz$!+nEH^Xo9$7gsBbc1}u=;~!p0GaRP65vZOr_7^&*zzd zP4C|_w0v~G*x&xS+Traxw{1Na6J~FHj`aB zzy)rb1&2oBcv%(aVVIfbX=NJaZEtcxd+Ykm+NaOIFgLUG@CyOcudpQ4*)Sx=!{+=e zPn#=hyVkDRc=pnHO`Zujmm2dxK!3UHKAs6U%)GeS=w(BfjJyX}->`0B#S+!mnK;xn zrDf(gpHyAqk5yN{zJDveyb4qaD6$XzZ%iysTdN=dwDx zB5_+mjHTwO+qPlOrcciu2gc>pLEi++Cl8_%6H_vB#GMUUq0TnX4RV8R^^Wh{wnz2Q zmgN_G%yl1yMaRU%C5uHBiGi;9Ugin*Mz=LIE@<&gz&sOhW;#_k#DA%&oY$D?KUX$` zxS|N4n3kXOk{_EvxEsUfFp;s)$(W?+ziqEpltbw|eJ?BhVk zm}McBbT(2k5VwIqB6c};tE0Qe>f%j3I3Vc{z&H|1+p7f)U46C2 z>y<-V44*glbkjM7>>EfWGI34K3#mRe?U?W9@Jzrw6EIczfnR}{|Lh0=-R1NKVFs$=!Q>PJfliZqkWLZs zU9vC)W&6NI!VQ=9zUlAjsIDs$R5p_PLS94&9u&5K^?vy9@r|^#zOl3{Qcj41qfTx+Eh_pofPC1mRbP%iE<(xgQ|&Hc@L!d46VYLVQ9@ zdO=|k&etV^4w2~7f4^%iscvj(YzBr#SX-Kt5E0-UpPreWgDtwVQ}+9Z`bt4z1$f9h zI)ybI?e&TAsS#mOC^L;dJQFattYaO;GLN-f#ysU8KA~7+hO50besbCnU?(vTH$34) z)7v-j+i$(SH8BoWG&!&}a2;p|lF68_{q4v1?>ge_E!ezbUTeApYf^TFx1WFMOSUs( z5jM6bu}g40cqU-z7*~pzbJmfsj<#Ez*eR7Bu>sh&6h`EkfO#fho(Y&|0*3L<0R6R)md(J6U;!q|I>K@s8*?{wMI}} zTHj7-QeW@?FxpXop*=Iu;`t@jlUg_I(waJeAVdI#i=Yr@MqCqU?Pz#a^VqSitL82{ zZdXEy#+1{H{h!25Nv`@YpIp&8vG2gHrL(5Y*<+Ye1QHtoQIKL+=&Z~1esTAl#);F% zj_=vDe#4S^3x2jtNyFw1qM8mA6%;t0-M;(qv7<-Tway$ruw~VvSyN|T@sEs4Oi9n` z6pJ#QPwd~hW%pq<&C{nf_Gzf?`(feYS#yrq`2TISxo6*jBkHFw>73Ypc-Q9TQzj|xG_kgKxiNEh zfcc% zFqC#09e$a^0nsid1DlYj+b9L5Q3`(`I!aReqKFt6xkH){3-81SRXX-c-JOu zUNmwd&3`l;Y0&{;IGC@H()_Qj_SnHQ0rO12j7gYh0)98pD`^+j6{N<72Kacox;QyG z**dzpxz?cuL)+l5A3(D#>u9YmOp6Kj^Y%cJE-Dq-*gHDcLf#?$0Gi|;S$kteesW}p zua}3b3wl^uSX$eXoM!^AZJ=5eSaeXjqBt)*4Q$hae!kvbo*rOgWMtJm6EM#N%rgP2 z?%V(#S@@$*+u>#vMR>dPxi93DQnct-v3&Q0r9uUff&%hqlCv~(`t)O}P| zCO}E6vho5a-7BY09Nf8i{kk>lH*NiCue$d6YquXfMxhx2%3+}>R?ssr4e!~pb=$V> zKkq$!Li^&i+q#dQKul0wG(ncfLmkzM3PC1EUZB+h^n100$#xG9o*|U>p z0v0ycRiOGZMSo~#EiEmjN!5o**WCq26)^$I%V1L}T5>V5cm|uaU5G;ydY?H62;xh{ z^{Ux?WwRP(peWePAh3jzCBo3!S`x7au3p0TQy`4oEig;YHV0>w;15odr7)-^)m5dHt4hHpO%&nA-K_N%{d*~M+O7MPmDWX?|oUo0< z;(@oF6;&lQ(t!bVp_`D`r=zWhQAzgpzp1f1y=~))^?RXJQFaN|HGy%f%wS}$qM$c=}vUQ&VAVc^Z1H-o+Oh2hVmUikCcFpQHAz7vuD%LWJKH(~2Tt6bdw zw#Me+^G%Nyj0HB{;1xabCKF{_ppEX5g+~^S_ z$0;g|pL@X3-OI<f z`ry*BRkJ3b#=wYgz8$NmIC>Fy;h(>B>pqx%Ep3oc-rgbMnSe3X3D(=d+>*%i0Sjn062&>DaHP9Y=}aK= zSYZ-!T$WSZBR?nW&vYUOevdVBS-}U&xhAPj5IQnBgIALifJ|yCfknPcPEI*CLyg(w z%!`V{tsDb5WT4~RPcT6|b+9WC%8$wPQsfB(;a{I9>f>*tw(c_v^Y*#R36LYq7jFwX>>h{GJx zwBn-5n+siUXdca!u7k4U%WPfDJTTVTu0RDt?L%go-$Eo{CMT*OI97yxqSORsC-}J z>Ik2(#gR#_GJxa4$!r`}falp5mE+c_!ev*yyOp zNO<(9*c9h&1I2b}34xB`x=T+@PK=L>B{^%fp^ zx3Jeeu=LBXzw`;w-9KaQgi&Kh@Jzrw6EIZ_ zrW(M6cP872fC220y&{hip_r*oD9;4UGXeMg*WZ7A+XpIM*g91uIcW*u-mXseR@OE) z){gGJ{e3(WFwX=`aYSw*8CpV!(1xlJQRs0f1c>LMURZAkfWtEZ%VfYZ)D@+rB%pGC zpo^WMp}y{w3ujJiX`ViH>UnCmOwtJWLw;UnOh`zui;Ly!=lAYkIIVqBLqp@_$&(I= zvbNgp&g$aaWM3~oXBTI4gXenpuj!o90N3vE<7x&WQfY^*vo0?^?zOqQgO|IN$xFSv zSI%pxsUAOmOjX^$2k`Cg?xyUt1XoWa@4H%98r;8j@wB?y(W9!W$4{BK$i#&`;)ddk zm;hItryVR_Ji2=YcUMzA$}<6jC_IVgH>Ne>FjV~?mH&~(Ujhz?%#8GO6zfdGj7Ot5 z4Yt!3&V^Q00hCVx$b@oovY{`eIOu>amtgxuE`mckoj#FPT)?!7!|ZW&ZCMBE!JR<4 zvD86c*qf76aG!PI!1-K~0GBC11^bvDQdyvOFlaMGKV=0l^uL5f>~ULsY+k459n8@ z18eL;!2ME=OT(((w&D-4UEjK?VV{>vx(kZm6w_j z8SL-l>EY?+h5x*L{Q`nQD3Jv7jq&oJa8_-lAU`uDF+MgnE)K`9_ymTVq2fj4ho|C| zVBajx&tZjnQ&Uq@9tE1)4c_Zt8W&&qf znvCa6=6}H4#zxXqC7~qCFQ*_b9RoMIyU)eerX|cSTF@#*wv!N~wQL2%6YeUEGte=% zwXJXJ$n>@dDyylgu4@1pI1sU@Y$)k$$+3QY{T9yzj9-HprZ_|;1-aS2x_Vms{LM#t zkDk6ZvvG9u=9z$L(lT@|&0)OydiTXqgZ5uKqL7)7vKwN?;YF|jc2#p{QVVuY zRM(ep-wY)@w12aEAS?gN_RU>@=@(NzX14!--2WSf>;tBs>peE|8!jK=)wIvcON`)^w^O-J65e;K7Z!4nG5z^e%K-E@OyM#>$H~6-u;KR z{=9YfPa9Wk`El9wsncdJ*r<8uDG>f`kq-`?J-BznwnIDCtXscn(VV%nXHJ?lYv~5{ zn|k^ks50MC@aojh8&~dHyJXqw9~MrZGHd4K>5EqFJbg>=*$d1lESL>p_mA%UdHv#5 z%NNg|KYz~LCF^#mUAV3H{IwaRsNUY*(U@X)Ek>!RWBLn%d2@zI7@ew(ob|t?BvQJ<}(E1>yN+kW| zM&>5>tem_&;FW`%6^sQD35F91?4ZEg+$SO=z`@4Cr3Upy+9+oOuq(12An)t8%j~Ny z$VhOtHZlxHnOmG~s;R;#8{gAAm}62fAgw8gb~o35Y*ScXfrF$#fC_gc$8d?eKrCsi zO7*vX{osK?Tuv^r2vXz557gRszwZ_1M7bI3-O+vIk&>2`lbe@cP>=`o3`mj(gc0xG zbk$}0+8I2&d-tA2bRsyBa&mHWbJ_Slo(Wi94T5I^rged60>J{zGXcx>A37}SDl;=S zR-LLedEN^Nof2B1-Y}C$e~2EVx7g#}zU>QVDUDa0pVZmf+EfpaG3hrsLurMvatuGa za=|<$1qGEWu;z&^UQk+AM&lvw&C%4hIQiquiQ{=D;Bks-`c`g!fq~!&jK^k0PB!?9 z@s+nWr###=WAf(57Opt^heX6CrO{?aVss#8%GRdBQ17s?@W`l`_@uPV>|FBip+Qdm zZgSHDEL&MxTv$+0P*_-03`aNppWJ-%(02CZ{xRda{EZG?-@srtf-ai-#|OX96bo5jezZVg=rINg1|}bWZL*^vWl=1#SXF zAQ~GX?uoE4aNfF59}y`b^T z%*e>dM9X4MVODyyxe-9&YJv}-Vt8I24sX=3sL|TUvin&GvW84ch8`#=KpHuOv~rR` zFMnqLV08mX|CwdO?0t&P$s6@XMn1XOzF0oY0wPK!bb>LK$5D^{K~C#5-LRuWRC}?7 zh5Fbi_95|S(rOKB41IcH?@Kv38v-qpO9?X|?&(O|n;>Cw7J$V-7|CP@mPRLvWZjOp zbRVd)^;)vRZ42`Q zY>Z>Qtn^PD-L!7g8Eu`Xubw#!JayYu=^ue1(qU&k{6^Gv`z6YyLU z7jJ^7$cnaqbFuLFjA{SzZ{I99v}fwbk(0)cQ~7?BlAe{FJLzwCz{fuG0y}nZo00#U0vnhRkrQgf8>~&+6m3m`&aM0dY@+k zCLkepdv1$nJ1r}H4YL8RFL@Op7y~3BU?CkXcqU+Jds%C9ZHAA#sTcfEQSpYlDUsgZ zK`}|-CQM7stP~A)Nb6fmDg+@`zF}cu&n-fOBjU0I)yVWhDp*ZrmE`@0j@GXF%G59` zkC4zOw!YDcc{R<5YLZWuBCz6Lx>}{(4dr3h7T$s3G1&zrHLY!QkYNq*0O`j;aZ^WQ zl;LY<&x)G*CZrk5qZ;Vc1wdu@r-9DFwqoa7)=n_v+VR^V;=v7L|fAvrB0 zE4Nc5?Wzy(baV6$g~uo{$}=w7SNG17n>Sv;zm$-a(%DsJ5Sr?3`&!@BHZUn8E5jVexw^XTJ!^00Z&6rQ#WMl(Ou#%7FwX>xj0)83!M0PAYJNlG%I4Wh-z~X8RelIn$ei%x zMnZwc`nsf-SC5|lVS<9vKA>yJz{CCz3qV8>G{oT=>Y`tsQQJOML178m222k74;CN+ z#1MvStoD9&NpqpH!uQDCU~*<4a%Cv7do?xXKDvE#(|84C$hiyP7Ub#29dH4j37FP; zKtxbQ6D~CQH;9+;^s%x%Y%8d)sjcSxajXM*<^WouBAz6!B_^Zle`mFPK!}7!rGbbW ziJm}zR#ZgPg!C^&qRWUbkv-uBxRmWhHs(2~i<_ zUhb|g&W?`d9M3P$1YFlpmEmh)_`<+GrmV5Px(XFhsM;yPXsFed^Gv`z6EM#N%rgP= zOu&CaA+y7>8;a8 zcKx*b$Azm_%$zZE`t+&GQ`%9$6=4=I{SNl*mL>$BS=I0j{ z%BNiqt25ETGXZloI*9WavkQ>~XftEZh0;?49G!b2^(E&5Q+Fcils8%$s1L0Wa`}*m zU58s5r~`KaZafwk))=oTcNez8h*6XS?{UJfdN5o(uD~+^)51HLZg~0lo-OM)&!4tv zp^CDyii+}rxF+BWQE-`45W=x-`ghJU9CyqZVKq+T9Yas7$w|@Qn z#P)5Q7tfzQQAI^*JT9QJC@hnu0nvDg;oaNE)~sJO2TVW{==u{Ts4Vl3j-?m~t^EU5 zr_OEKvtsW2c`6epOhlIn6UXoM35tqKNKPRN=Dx<3Q1 zVF;Ndvmcdb0w%6qP9rG1N9Z}Cfdyi3NO^cBVEQJwWkW1tO9az@GAEz{JQMJ)jceA= z`F{S~@k+`{N{Vx$>MAQJ4UkN$E+2!t$G88q?#JcRr>cxs1W(rlg#-D>V#&`hfPA3S z*7ViMt(!M4oHG^L51KC}C8fE(iHXU{sp&LcYWzS~ZPnTpv!+kR#mD0g%1Vo!{DQ+G zW8=wq8?@9qw{iFK*>mP7Pf%7t7iATN?Y7R|L1B?GWYG;Y-#xo$)3P~Jr%j>tK}kh< z{PZhEcJBUR5s?u0_Ps5Fp`)dJ(t%z6LS!aIDzRD^MsOs zk+{-|s8m@_Ot8P7uaB3PH=2*1e*onsQWw;oX=$oM>4w70?V@RD76@c{`0fb;i5}UNkSq}0>$m+@IHY4009NPL`fk$d6^lRY3M{@eKbG=DJcz! zXI%MDpv4fr&q`0@q=X!#+B|gTK;(>$hJxZEI0bVs|K%W!Asm7Hnf@>grtF)4k_>mC zYkw_fD3C9mx$#5)CLz~Yk`LbggPeq%V*xMZcf{p|=!aWCiX5MgmX@|oSr3)$p&o>H zfSg^IX99NB*tUG(Or^2keWySc_7zl_&B)Bk!nfL0RFKlZGXbw&JZ<85C515xQ)d3K z^UT#ddM^x3tgVsvE2Nw?`)w;1&zdrElCtuQ1uOQRy`lT)*-Jw+8(S(N1m$dRk2t?) z*RGwb=PzBi`|RC^s6PanGizHrsvtxadRyC@N(5!a$$oB5F0L+4PEO9wPR_1w?v(J( zfJ@DY|JS4PZc%1pbYvu80-=F{pi*app_~9Vfu)y93zQUOr==t%CML#5MMXuE4l>je za8IQF@cC4hAz3gZJuN*sAu)+Q8afa%82ZmM0prU{&2OUgz@|nlcGXp&qNZg>BIz0E z?US}P)#k-{x+mAS!2?6`N+y@cx(7b~`te<_w6i`v(m~(AwX6-xaV;=btjuv&_xn$O z{q2|c1Kl04vrQiAJ+Z8AC2F;rO5&iBbq)OGFMs{?%bNjdV@bH}%ZK-F-wqbi`K_v) zRru)r74pyT`uimfnXX2953cK+GpwS7&9YL+Q9_C`0@P{=wh=_V<5$80_!inSc%MUOav3?8V2XNC5Qm@e6>j86}to2L`&Ua#H@x!KqLeYH#1O4q;-meWUY;Bz#OpGm1;g_h+{roXAVBvJ3 z3={ScL3w#WN<;wLy91z|At50&d9eAwK?eY1Yds*=S;+~Y=!pVJ4^lXysAx8M!x1in zR~->6B|?E2bJS4Va2+{y5PO*v7D1P=Vr^y7*oNp5Mf0x#oN7Y}b*Fhym& z%6R3?@d6lV`FYH~>mJ2)5>HmK~FC2 zTfcna_e$ec6qOfQ#>d8^5IsqGCSXcSOM7v0%Z_~qj_^#tJQHvsfbP{;t9d41(6L~B z@Ue|_x4E`)@!YB7$Bg-IjKcV-OYWJNnp@gBU}HfNTQJWA+=?0-^{C;W86O_#>*eL? z>ETX|5cdFTfb6lc0W+$!v^Xz4F*Yh9JS;RMI4HoM@)^)z-ZwX*?i<)&ONw$?p^uoT z$cTt=CMP1vmS(aiQGg6?+v0qV=Px!Uiorl=Z-@C0tT0SUY#TVe6O{mfJP8;HA+2wi z(lxZ@RdFo7=)p4qV}r#PLs<`81E(j&USS>3-d~OgTJeya&QO#>OTPpi4r)MWj30~1 zaRExhM0#hZ5T$>Jwv)~Vj0qIKeiO=Xi$v1CH@)5MO_hS|+^QCy3E0Zs6XtnP5NN!` zojt$4@0W^O%1d)oqC>si0d04(x3zcmMhDjTdO+|bz27?~^UsLe|Z za4|P{cK0e+z|@W(KXB3t9&iuu`o^lFh^)@$qS$C3J7dH9*Dq=vS65e8J95s{($>z= zxxS&PGCrwFkQE!|X=3{9&Sf1f4fPWzH8rk3GlG5LR8JFFkS|D&@^O0g^#1ir+NU)& zPMy-=nSgmFV9Gxf3b6yQ9bBH=C@-&CRfT{HI>3P9#6UD+FCQXGR<0sEvx_;;Wqo*%llgAAmkWyAvm=xyYWM!z&GXdjB z2um0SG0qn9c-?TKF&DU_kR#`IM0k#Zd=vv6YUFgHs0pY%sZKT|2iTGEqxeNmfRBa!z%J1pZ!% zGSu@-z&sN$(qQ`gSe8L2`S7WVcwP=FdS+#1W}&K556=WlO8NBy0cZiw1YAT%F{w=4 z)>u=Vnivxv9vGxR7T8?rLqU$c+v4bFsHFG<)&j+NBF; zw6ru&YH3}#`_#w+>vvacX;!SSi-Wnj(aXp8Z(hA}>D-yKXU|=|_w=>7HAMiWO?gqC zPS$27CI(NR=-#?<Va(= zR<8VU@%*{7=gwdF!;VLpg?X|LUwd=CYiG4j9yzXd;HNFCRxMvLZ!X~Ya~3XId@4Ro zR_YM!^Zfet^Xf;A9@)8b^UCE5mduKeE9@JOn<{2zPbR<1nlO^GXe8VzyJ|( z>I>aKl`n*_ni`p3qZ+{g!UqB4NFp5SNp%TKhzJ*|4dSTuvmP%?W!}B5ojO_v)Uz=H4-?(~L_s*G+EaBE;tDYnxY(uISvpbzJr0)hl=OO)PByp#lU?+*A}F9p&}R*47wD0Bx=FcW&Hz zWB?qXBPJ#Gc!`8(0%lMFS{tYYAdmtT6_gAh-`?>RQ35Vjf`7gLV;MjP=n&5Y+{rTm z^Gv{C>H${|j`>`bdsMh@1FD?(|7r>1OX2rSLTF|X#FK07*nH6iWIT0@iigs4@(RS@ zA*MdG`(k^>-V6n$>Hq{#L8(oSpAhbJ@~G1e&h5O=U!)5l(hrq{(sGtNYTKhIMErNnXXA6iDv@F_X3PK&jj4_ zwx_c)-pk_k!<*Od7>2|orle=*=H%qz`|0ZI9~gM^uBW9SA=ugUrS7$xFa0B8lhGk3 zCl~PR9+D4!d@m}^$qaQce|-0@kxwWtkeZ&Aoh=s2x><>+cOTx$Y6{c5ZC*UQXXF_i zlaP{{mX-lId?;|R_YI&LQg>Zuq^Gt1BXiHtSd32vpE1e1QOW>h#Q^&EvKAs7f)&*LNaIB#J*y1+4e!#$noE;@t46u#(C;rgb!ooVxdM0f9gOL4$ zy8@k8D9ZRn&ekR_0g$_c5R2|GlPCrHq|%P8ZdfUF5Bh`@WaP$^bwhzbnVkVlQY@-W zP75N*Nh*=HN`%$krw$uRWJ7X_gAR*(L6?=~er4ZJW*riCf3BDdwsG|V6K_;>41K@!1&hUv`irN}P#iO6^q6tmURt6ANKhz~aHdjy!S2o+jWij{i=>fkAUQCkveMWTWX<|M*Ef)g$XPMW1lmzw(cInFnH^+T z(Qaqn)Y~hev`ffouteP0aC!r~Hg zGzl_2LPK6&(Y~Uisd-vU{kZx`jiW~{K7MUs=L~s=s3X@WIMn3!wX0XJ-MDr8_MLkV zZe6^gZ$cO$l6Q1A7Z$~vy>zsCiP9j(Xy5>`w6SLv4CU~nM_Xe_L0VLhzkh(QryJ^n zIJ>%g`uO?>g^*6*G%9RqtS!yUK=Gf%*ho-?qW}Tb2#ShfCv0>;et&IM84`E6^gt>Q zghC-nNgPEW?d_mnq7pxXg4`^g37BUB=9z#A18CMMDL*jgKmKjj7Ue(v85NQy%$lq) za)iPpg>M$0f!xpn`k!l|@!hd2lmG3T*^_6l+p^(@1uK61e)P!A7j8d!WnvBWVI`2Jl-oIMnjL^u|* z9_;*ykOJD<&%g8~+nF(=k{vz2bdXSofe#^F3wb7B zo(UMc7TMs#4SsNPXSFrgmP8-jL4hiAZw)ohhfd+G=K74NpVq*m&V{X7?3BM& zpLK1^3;H(~t75O{ALN81H(Xssc6L5_x#f*@pfEj&DbQbCAt;15oq58!KJ+hgt8sCD z)QJ+XcfNXHS7z>iX6~2Do$+iKjYOlQ{vnu88GDP01jvQBGK(FsYGFVTaEl^VGpt_( z6dDw(qLCA6{-YrP$rOOL zFH$MY|JrJg9c!OK&NXNc*BHtMYF1ZOo|jW7cTb}axzTC;!2E{?o@z{@oL>p9%6!mt z5oTU<3j}KZAbGj6s)FRy=sI}fyzWEm1J49pkXunviNye|LEPKh)!9;uI!!TYWwi_- zg<2`)6^u%?x)l(U{vL5ljUYQI%r7vLU|51eFc~u@-|F^XKYe^V*duAFDb7g_5AgPi z2L!9MC^wfI-Te9UuOHvP=@T{86lW!cf~M6Y26B{I0Ua~i;9o!g^6}k3PiJ#gULqj1 zp6;%03FRcOBBs~Y-+ur6%lkJ2-JK2P8Iee6^LBS}^(rg{)iUI@4c&kL?eoXC1AQ`K zjUXpED%9WG!`00tz9=_43*!2ww}1Qn^M`l%c&*@?jSUO-^Y(Ogb_$?+(Y2tFZTa}u z&%c3-w@1`iTap({rBHN<@=^b(vEbyl&BDYTppEdoqU4=18eG=A^-f#hd2G*Vqv`?BOxpZ zA0M}Oa&Yzb^uTbQ2^f1g>`7>zG$*T_HLeE`$mk#+0x^{< z0x}U8blUlG3AtQ8h-U(}dU{j)$bp~Mty;MPZPof4S!pTBs9sT1lV2#PNOm{knSgmF zV4ew>X9E5c8qWmGGXZb8dG`)oz;6dMIC-9dUMI5Nfw%Rpm-g@6{L|5UId47;Kn`kIxg6aG`x37lJhE;1 ziY4=AFT0)E`-aKk*lx#gh?^a>RgZ1lvV7tEc{65Coqe);kY@spOUus9FJMf){e#7? zPXD-S{o*-uP`_Z|ma_&9K4EbHK;-0b^4?xi$hkeMS1n(*X6I==Q(IU6@R-EZ%$(d@ zChzOznSepLN=bSQiO0}ya5TX0*98^?uCXFmH0LSc!ZQH_F-YJG#xqKEl>=`(E2>Ir zq(~%T?8GP;2A5}>oCLl9O^w~@Z5vmt-*Y{^?@bRe8bgEOvVk0n$zX5P*&Vyq&YM4D z%A`4$<7IsuaRmG|#I)Nt*kpce&)yZQ=gpWlb;1OtiAT!%F&GKkO^uXXG9a?l(foPi zhNX+A&7TG)UZwFX{6yp)wROqj`SX6*taejR-`K|4%O5!gG4c4V z=;xwZK)FG#j$RS)Z}|EK1cyh(CM2b%rPJIe>UxCm#6Yb!*OnDB5-nr_VfDz%8)Cv{ zS^+XHcM6%cAb&_G_-PsFP8u?330rJ)`qrp}^H$ayB<4C70xJQMJyMGNQ7S+eoS zrP~jlzB03Mbn}FsWBG)w1_kdD=7+dD`$k5F__(`ydiw>2ghxil#$o+Mupe?t1{4E| zS6)EXu~R6+5Ij3+#B`1VkzFi|gdDp~-X99-*oo51OOAF5g{HCipGuYY2 zSXbxRp~Hs`q5e-mFjcwanSg;7A`k%21k8{KUp+8H6wd^V-9=JUR9X=9L~F;o<#Q&D zR~R>G*4j`h;W;P=5#kO(aYc#SP4z9Smdu!_IBuNEta%sf=#W@nS5KQ$b76t7&-d!V z^~;w|S5X+Npr|}`dsH2Zff7Uuj>+ooU%b!nT(fl1_v6QoR#2HRX_;|Z30iRx3?o@Z zYgPMOo(XvF#PN#bMvWM$FlpAZ!xwMpK6`C!4Gcda{zSfeH}FirC;$YuVSxV-b}y&n z3%J7m%*<`Fuh|RK-bbw>)EJ;({1C>Eut$V~rUME~tR)B|b)X9oHfgA#2lFk$^$0Hs zCm%*QQI;=t5OqK;I1D6(`q{O)+e5~x^b^bq>VZ!R_yCO>YMuCW4Nxg$LUToz{~#9$ zTWU*rCg1>z$2YHNsh{x!k!*TK1}KnG;O6(g|KlHj`Owo|lN;k@q<8iFsgq~jkwS<> zGKrMJA)o*HpMUxMUff)jAK{>XT}NB%)CJeDu<%G209_>i_!$VP-locu6hE^^7fzvF zb@2}l4GjwuQ;D7aw*W}>bk+*8qFi3yJ9|>|l-3#(^e(qpEaYZUIgez26)xb{*`4GaHL70&MKpm*eS~bbb%f8zSqzU{6K$~OjV2ejpH7zcLA*S(63<)tVqZ^8#vjjns z^Gv|4tsSDCzx@u3f48uurYt8V*5BR9-p<-HH90vcIT`SEiR}H~K7SY-5H&Uk3eywA zyj(zuV`&>66&W29i+I)B-uHj~^tP|Ny|Jn!KP@)E&Dqi3*38l`2uwJk$QJ97z4^;8 z12Sn_ZJ8i5A=1ai#mT{rX9A9nj*5zmB;si7nnENBVrrsv7XplhxtTz3Cndzi#l|)^ zV$+3@LjOUIsSAF~5>)cYNKHWs2jpA_mYdW>8#rV7<(Ys<{h|NhLH@BjUePoP1r zZzRV~NoihkWQezuqphv2wUwP;X#W7u1k5u5Q;`C|8(E`j2W_a}&+4HxlZOz1K0^L- zOAQ_gJis#n$M8(RxAje>vN~Z~O+kD_fSZ$@$!o*MH!o>xojjqguBN7OQCHuL!2LWE zFsT;jLXg+@sVv9gwy3bMfN2%eJFdV8mpMXy1m+;4DkGd)0+PZoDeM9a1;Dxx?w%0$ z!;Q-7aN0^`f!e#IR0W82U@xQ+=90&4IhF4MNivJu(>uUQLC&&_xX1y|1k5u5d;5FY zKE9@Bm(5{+Ar#j-pPo9DN6G>;uPuxsK~RaU9@1)qD4!8{7L)z!{?Z1;=nss zP9NI!)0SdFO%a<-&v3~!VYxkeLMEpq78Ts(^!CgDHZ{N0Q^VSXP)~#R5 zGXWDz2zm2I&;~uUH?y6aT#YzZHj+~VJqRQF#}1SR!8*w0fankRAmAI%1WXBKSPc6k zUOIbsf>MC$sL@2F#(+>nEidXxdgklnBW|h{%aCw56zD_;f}CPVx20~dpRX_@u0klH z1QZUfjQI>nSgmFU{1B5BV_I{>@8cQcqU*I8}Zw>?J2KsJo1Ro zE)-POBAkS}ssM5dcqU+^5qkT#!8|65m&(*u<|c)>yTQBf;sSI8BPxqVnOYS1!Vbv_ zTsGAs2#VrMNvOe3Eh&jgigLC72_1_Xn?&;oa7}Iw0D5R&N{rIHM0gqocrDfUEGaHT znqmG>8B$gukj1a51KIB^6S)WoW>!9wJ5UpPX6OjOMY!1e6hmam$FwzbasUMpg)b|m z5~T=iao`;mN0*Q1nSh&fHH>p;-<5O|zaVS0gdi0hA=?&V`DUfZ{v?-n4ej4-h2fci zc_v_<3785VVDTZIk}tL?9R!&9N-w}YhBgf08`7}h4&;&e7fhh?D_}g9u7Ax0N|(SL z+W(0OR3_^ZWukZwL`*Ic;Rf7oVNU}}-rL`2s%JoD=UFm>NCMt-l9NT<-6QI0uXa0q z#+k-)a*)DuOrSgya6iuk+((UP0*1ZAj1_r#bh5e0K;rZa8zl2LvZa9)P5&SIKiuK} zqW@HT2MtTd|5g8~2aWzG{fDEHX9E6i%-C_WOe3P=lTwmX(lRo!y1{+hUGvl~M0c5@ z!st;T>K(7`7Z6G%GU5}H#qdx_Bwh9Hb{CG$Q5-#b)Tr;qj@x1Hjt=1Qjg8~R!>=22 zbUozXjT$v-^w`yA4xUtmCz_akv0h?%l?iz!U^a2t`IGE?4lT=02ee0W)0&ir4gy>; zez?%s7(AfPIQNn!_7|exe`5M&^B(dq9frma52oRe(@_KHq~Y-_u+Mg6mJ=aI!|_bO zZ2zYahqy^xmUw@`!doJl2)_l~n8buEX95QiLU)A0eSMv6%jVy2>lAh%k&$rWtt}YO zx%VXAKkq&`XPko4#QUAXwhrQDtH&%L{UMfxUf+G2<}aQxU0F$SRveg48gN3ZL?h!v z3WG(w^Q7~RxwB?Z7(ZU+LOCc*N(%D}P{5Im7m2$ACTuV{v~=cVWySI1R|O=d10tH7 zoR*oz$tCUD7av^OI8#M&oPvVljAx!c!I4nT*!Tn{7t2Zyzwpvsp{$}fcHB4x<>NMv z?%rUb3yX*%{g;ZQm4?P=56zn}9z@*Z6csnVFsB+jfuN(K@#NtZh8|u&f3mW|xH01t z6n}hZVCm?JB0NFCkW+0IWHLWrvuN&A6$LcK<@cVO*f_a*;vV>Z$mfaH@NDIx1(TJO z#wm>7c;~6HwS%*px3?cjX#YSHMjoC&YohXaMWt;w9~;^5Ou$qCo9(Y(J@8DxbQtHE zfNfqFr03-3{Xt}MT2^)zm_SnlQkx^K zt@W>Kp0@Nexx7R5==NRe=WqE(C#0m4flwRmpPcS%^;F}~2`3xd*Y`2jY@v0hgCCys7fx9NKj|yL*AMh>SkpZ;CS!&=`)(U_wL^PGtUG} z;fbs)0+BOLL$boi5~a{Hdksz^iX7k=0rOC9;8G8gQx{QtyNKj1&3K1;P=lF^OHel) z;)S_5z@r1%ztr$H`PGO~4Bi58dua<@I2_1;q@9h3 zmWzAZlYUB&GC4CHF;F6fKq7WIcB`Yi$Lb=_1k5u5^Gv|W$;r?sii%TpA7tR7Y)@4s z=%=!gJ%WZhsQ<==D9JxTJ_4q+6bXrSR?-fw=7Vu?y!7FOe)MOB{PK3 zu$+eDLtxdwO*%y=B`_qXd|{x0(WJd^`g=O6>&gU`jpV+N7ZE}_F{3)~{qW)A8)<8O zV`)irTzXMGi;}Wz%g|28&f& zo2a#=JU=rxAwD4{y`ZoN=j##y@Drc@`(0y6bz@6ob6claSX-Kt5E0-U52C=F-2B4M zPTB7t>MI3>74#GDs*k^^pc5WLZRYhYyLei4j>atQM7cwp2!1Yi- zY7nE;MnxI>CxyxgDnPD5DzU}HTq$CDhZP=X+c7}|$Z>^)`Njq)P-QvBQ5#l&w(FDr z%k`HoOdpE0^k3;eye{^Y(K(r;QnXgF=|JQMJ>TMvx9l8Z7UO#FOYUYyj}d&kAy z%g=0&TBL`up_PY!U|@isw{K)>Nko#XzqNz)mBV_ju5NqJ+S@sKMP?QWD8n>AJ2k1e zp)o2j!_!Ics;a)Tv*tZhcfYventCKd6S$+MqNubr%|9vI+y3&A6Xy0j6EJ2iP2k}M zJ;5716EJd)c_v_<3Ahv-jg2j>&x)>_KI&MJ2NrV)7Id@RgJ@2*KJ%)M84}( zGe9^~K=Rtsto(vVSF6`|bdDeRY10~#uUxmqwiK0KX-%yy$`zEvdh<-cr&PDC`EmIV zOP4KQxqjpBi#P5*dQy%J0@Kac@a4TLXHW7>z&sN$&jbu2V4ewhJkJEoGXcY{15>P= z>X&B%UbSq&%xTl6d_Qg4^yw=Sxf30jir;iwKGM}d4*k+4bEko;bn5i!Gp4UkK==a@ zl7WGN!FP_AuJ1g$Vd?T8XU?86W7_m-(`SCK9-o?%S6CwG?;jX=BYb*YZTI>g<}aLw zw-a^$MDbRoBe8Zj0!szH|j!Z8-|Ou!u-_*&#& za4#(c{p^b(`bB^h;P2s?fI+1xF}!>G*qZgL=FFO+G677)DibECEc1_!O-N45z|`s; zusU^a)1DP`=g(7_IAJ2XOqe)+uTM}^Ttae6H)uTi8t>}t+q`_<^chnpPnwJ_6O?B^ zaPkd{Nk~d&Oq%_Lm(=$xUOWf#$&)8dP@cG0$H?9rFy%Nf`4UraU;g7`3zjUOHD$__ zNt0);I(qY^rL%V+Aj&Z$1(7Gu1Ppu*Ffc5hk0={kA1Kp+7)GfPl!o9kT%3-{DU#m^ z{_mkiVv5339G`^>NlyAeG^cDZgVM;a#;(;#OuS$*Wh6i}9#|U=(?hpI-U4Ilg`7Se z_W>A=hdvPc;?vT~gpbG-8!p>iyU0X`3c0#K=*q4nX5 zMk33-r5Q=?Oy2Y-3hkp2JP!?_dhP|3S%f$#2hbrg?oIm4Ub3zXT=YdwG5{G^A3RYP zO6Ow$9~wqdT!b2K!aCp%L*uzSaJQx&Kn&3*;F*AHX~pe%|K`*0_31vgrmt^l9XotP zm1hFxnSfD3BsV)dhXHsv>>f6JKtQT0D}n5x_*pS3{pK<`z(G`;i+DJQfd^iC#Mpo- zfrpw52GULrAu;>{nv0k)8p+U@_^X#>MzF@cN2b6R{g-$DTFwcX`ZJI(UGEQawm7hb zh0}lP0Cs93p8K#{`b?^k>n9*=0o?&E;9Kfo`thl^kn3h65@k7bDAG z+E|<#=JfK$g*$<5QZj^?8N`k58XWACG!~^ry1cxqeeQx?iVWhqgC_K$Q8us&>(*g2J(a68d5W{_^Sfzl%%b{M>AxT{>~(sOn+W zGv+xtIk{*MOM5@Q`}9GW7wK+i@$kI*;Uh;69Xa(pigJLHl1bkE_ASo@9Oh~BQuoHq zv&WCAoxbtt`72XPTcjy^5LyU70nY?1SM)DWoC3?O0`~UM{x9#tIt=$GA=d~GNbL`D z64KEE-!$9*sRI^>FZvIRe@9D8Tc@mtO7_qtuz(EfzeHS}>hACk1lT3$j|E*&6+ZK&ayj=5^g-F|C08i ztOyUo^E#4D!8hNI z96e5H#!8)gdM}JjtsL-vj2xplaqiYjx9)@K*U|>nx!OA< zxfl0tUBfd0qjV=AG{E_n3m8u*xP;gyJG}q%Ou$XGd2ycZ$@Oi-Mpz3vF_ywEk#!Gz z{Pp9zUTJ52dZdHCfomDiMkv=-Q^m?0cXhx2^w-~hc|XwIQ4{ZO@=))IWpyhO`D1azl=*%$szUSU^pH;g%@P6Mpzxz9X-TTL`VLG8#?doRls#>d- zJkPy5cLEy0c8DhdhY7fFe}(+>`<@FG^X3GE%-y}don=^lFerdC!CwuY}w zEo>ZI+&#U#eMq^p!h{a!5J7QqPGYF9hlhu!w=ZEJ1Of#CjT~60Q)?1dBgHzMXnewn z^(03Nb3(o&Mp5)-h! zF~x{FVWjF~fC}Oh0t7^6I;fnJ5|LX(7J3WIO>7imQu0i|P2@YH8U-vNIv-HunSdMo zA79?HdeNMpMvYYBnSgmFU?k{bm*Enc;p)eV;hBJWCg9PE!-fx2P#DWI0qZ<|{!&*D zG;54!7B4&#u&nNvrSTKXE;WiH$Qq|~S?w*)1dOu*ak1nRdvU$6sjW@Y{kE&4xvo@@ zkyX}Ei?cx)Iq351;dd3b0Jz}eZy&nFP1U7YiGh9z6;&0LWkko84?wqu<~C9H=ihMv zXltkvq$K&dxC9pyoHP|dpkA}FMb!EG@4vn8>u9N|5~RikI6FFcz9}Rsx2#M6JU6w9 zfBW;dPw#sqjWy-@DY5>}4h~j!F(~y$oj07IJQFY+LSj*U(VLXG$jFcYH%l{1D;qm| zM`u@ek9ri%G9Z1i7!30;@8d$j^y%j63YJeg2oO(fBRT+<77pF2^1?T1i7`>(p}~QH zAp7(WU_7%Z(uG49XG#Eg7v*KAC4uEPif00*{ew&o)Tg4+FW?@*EusxZP8H5ciN%|` zz^rE-2&9}G+%l$L#?^~1%!=VU$V!cPCSYPZhM!YdQ<4$o=VEX8@Xq<;M^79%>5{_E zpt!&FjZI>4Q-vTUz}wdB>D?P=j;pJyAJU8j%x7dIJsd%$MBG@M6YB1$uY3R2`4c=7 zu%5nwp|PpCrL`@Q`0`A^L{v@zElN5-(Gw2dh%#b*7J{jl8bVzxErFt1=zujYU%w<} z7h;xDKSn=9CwD3Y#QLUWBxq2!{=rs2*aKu?z%>V+1Clq&EHo}-0X|L+UKL${XCe?l ze=-9O84YX)<9rT3F!n#`gBjIMj zKM?x4k-i+kDhQi}1u@?4elfYtP2@XdVOHpG3<-ICQ*G#t4Xfr%pE_&#t>_wnd=ilZ zt|v|-}%PzV8N^NHZL~g?X7t zk)hE!)!6OGUEAK-_37`=pZdDmrEQ|7>Wbpr%=ln`Pj|n#yfTCsN;^B>|LgDHKlXG= zVPiJel)>qr6dCO8?&jp|6IWC$==)nt>%af?*KcpTC3%H4Wpy=$xtU3^A$~4));2a4rk1`@Jsu=wVobfRZglFP z*+?eZ(-qa16r{#RMg#*~%*OcT zW6g`_&uQGX;+cSt^Gv|WShZ+aUMyvd`PeALR{)lsr*YEndD&Gpqs1p7eiR4T%K? z=a1~(vwzptEn7A$U9@Q7&&!XPXA2wHgW{Qhc_v`YN7$NJyoAGvV`=`I%rt`FL`&q- z1DIhzr)WU?RRYNk>mT_Y+1LOuRoM;!6g`Z9DOUrAUef;Tc$uHa<~q%1EC#4$q8fx3 zO&;qjDQ3F|larEXa~5tip#1Snzzq$>5AE_<0s%`rWRHUEC9-9(wY1c0tv8S1auekp zSoH$ivyr;i6Q*Y3&EzoAV4R`RvyvNQJQJ|Bp}BK=&)avOx;wf$d#cJBipz@XDg>gw-Q3XD)l)3(?&*E^p{u1L zr@166r!XTiDl9(I#?s5(+yrbUo;(vUB@uBeoU-Dp<2kapjg!9(J!e!TUJ!B(btK$ZM zGq0+WOK_rj`?Z;xm>2UkyuNax&7au|9Vv2R z4-kSNyS}rlFVirmS5lD^;bQXqk!5ai3B@-B09YkCE*HB95FjW^^0Cl+@IW^@GmBUR zsd3l$R@!uY=xWRicYdv-d0*Q#F*!XmEBj4OPBv;7;H2nn4E^x7y(-PiTKD1IyZ1~Z z;*!%cGpUM@UEkfyGXcYikH#|rlbO$g0Q4d&5~gJ@SFn(iJCkJeDofGl#CYW*4%7jE z{#Q)k1Ra1z=av5*6S(}af=)7~Z^#4M~d~%p_>?Qqg zGrL0ELAC{TpaGgNf?Uq6|K`X}SqGB0_K(k`B1yaC*16lQEWVEq5VjzWN!OEG*Up{NxOnpDu?MF1?taAd+a_*Kb@SwzfT4Be zjm&#!SMs9C?HajxLnaUb2nn;HebM1RA zNQjFH3!>tpz~InO98(w!7dilQ0##$c_DD}jN{EYzj)_73VO)GXM-j*l6*zK~mlg|h zveH?CK{87+U~();gw0rLdpQt7Sb`$Y1dN+rU&maQQc*?43-juR1i!~~#;lrgr(25C zAa*bK=n&ozx0Tljd{VSjErT!3o;pZz>hgnEDv%6XUV&5#WM(!PY+G;k?Ed^8#~9t8 zJw{Ps;`Z6Y%nM6Uz))0D+9r}l98?~;VE^f9!>6xavq*K}sPQv59A3KeRZ2=akhVpl zoGbIj4jMad<$?J>uNXUPsp5bi#%SD`ICP#*SXgvoilp9ikJ7-oBNX*EDh?VnYV0>? zL+7m>HF%kOKu}Ofq`1{=?!fO}uT(xe;`?vEoiu0cfFa|QhAR&Fo@WB)nSgmFV4ev$ zBr+qXpaO-$TxbUV5pk>JQ=h1=r8Zn&&%v#vqPh-c#$OVbfVwT|_^r3KuPNW*wuL>+ zI0CYP--RMOXdUo_cl3PfZt1J{v$U{iYN3Hy5QzLJ$i!rxa-Ino>HL-1Svjzkaf`k* z=zE;>nVXPj0)~qecL#Sw)Ywp7nCKbc;T2>2&kxF&qY&i=3$9`@Q6NtEsB2 zB2rWR2+3s~SgJhNA029h!W?Zi6)L}HhfF$L;&;{vmA0v&s?=KLAWMIfzt4VB@Cny6 z)mIio9Nxl}zI^FGQ7criz)xRaof^J%1^z~nEIQQ6Ltm69hi$Q;I{o^F7u21LRk7db zFF5w1flIU`BjXJ^${Xu|Q!$fM9Zq?PAQvfjTr~~rMSr7XHJ4!rqHh6v=c^YsGOLDX z=6)d?jmhEC#+7IYEXzt^5hiADQ4On1h>=N3#r9N;Uxck44H##LlmKy&s}Ukx3nUv; z41;r!b4771yg_y`T$reEftbmW8brGq4TP{ivzz4$nB=4#M3OdPg&@DMx|zzPSOy_g z`(ph=v$VE~o74PEpIufte)6Vua$O7gN7?XL;WR5m6@C_W`qxezIkIutjD@P!1ypD( zTmK}ki+6hd^6}M^NB8X8u|Rpk^j-Ric_6V7(E7(0YpqK2cyagqv7@JtsP0;~dd>Wq zv$vTgCa0ujfT#u_ugwCxbDMV_JaYK3`pL7Z`!+0_t2}Y)RiCivxWtrnFhHg`9NoKp z!_I?hCr+O}w&$4Io}cH;Q=Wdv+A|S(E4?ow(r`r?~wZG%Nj>FAKbBi(S&iMwi{a5INqGP)7M1v&J!~` z2YXv9GlNH$ub$A@yJ!8H`9F<5WNd0NbF=PCeRHq)tX$B5rzXZmcp5%_a#H2=+C`Jc zk2_~*yyNskQ@fxyrIl2yUP-6BG#~x5`%j))Id_c0tOt5#_CZncxmAc-;i2KqW7ZYN z8C+N2wQb3`VTwl{=kQFxH2Z1yV2x)2E~MIfV*2HofD7}ovKS3E9fBEpX z7lla0sbNt;-X1QFPVTvdU`mF(vZmvo-#>qP*V`>^tPo@-ga`R}xH>sI#^hyXq(fX? z_wMgMK7V}Q*C`g33o@dD1H3)loE+?ZGg6Wgc_v_<37A5OPykg`FpP@?z!*(WPK*f+ z^7r%g_ClIiDaomo1EdrPqlj(-r!*EwMsPI}gaRx9Y9-XTpT$7wVH-g{#FWVZTbo{p zWPmyY#fJm&D8SWB6%zw6eFQc1AcAi~`5-yHL(`nBcGkEaTnE`D!~_lvhN^)YxH{Q4 z;$TAL*Hl#1l%;x^>c7yni7cwErq3uQm=L+VsyNc-*}cnW)c5b$e!?iDmS8vtri)vj z6;%Qs6N4ApH%_Y^*s*c-s>4o|l?3*c@`@x|YYYARH!qyozh%vmpXbm2dC}sftJdv*_+0NbO}z3#qBwbY=gQgr zn^!Jbv|z!)g^QQ3*>L9G!zVA;j4V&IFw)n$bAe|9PD@URi;fBprV=V=CnrZoDCz>z z*ZEM_Z?ZGt{7)o!f~YV*A8#*DPfyb1oIE!V>V`TbAW_yhBp_ z=wJWkUv((L=0Md-wEk;|r=+^Jp$_cp6w9Ci3Q5jdbwjbw9koLTPU!dwkY7=W(-Ba5 z`sKB`x|=5{4E}M@Af5^Md$b=04p|Wt5f+L=PoRcQkV!grHnxSurDbo_HZ5GdY|7{n0|$JE z#>oc_op|k)o&ka!C52~}E}S=g(wLz`nVk9z82ICe@fwdFKf@WUsLXib@>Pq*k5o{g z@$m_e4;rE{>*AgJ+L$JVMJXz)moJ$)Y0~(?-+zzczy0p}0fUDsZqc}OQ?rm-iS3%D zOJ~iPGGXLE_VKuU=e@e$!sMT zGmB>s5ij^>kOoLJeB^{r>#ZT4&bt{~hn0~>Pj1EMH%=H6vDPTV!r_5}~ z<&S`80-iN_tdi2Gk)xHAl;#Gfu`(dsJe~;{^M$33gPaw)w=gXW3kve7732e1hRr8d zB8%K~a-Ab&-qddN$z+?gv2u z_Bp`w_>b{vLC8j+ZHNK^a;8u6<5Ma!g+buPXV;UnAJBL5Mq*a1K=DT!pX9V;@ENq4 zAm#Gm@o!~=kxLY7if01u?(TV4VR?Mr%Ac2SyPDk9!@&vSW;Qio`JnZ6hMn2CZJF}) z$>T;%xfm(xpuh(M3gpDKuMchCv2gLUi4%SrJ$i)F0iFr?p`N9qyH7w6L&)pudGqkl z%(*{L0a5taF_RYVzHslEp{2cxn}-kG->%Mfo(ULq+H8M7Y?EgKrj3lUQoyZ*5Fabg z5Vtl~=SITet7tf1GCm=9z$56|sE% zgI1hp0_K^3ub(}%bN2z2Gq)eTG&HxdcXmU$33gCPeQJcC!>jw(Z=6@zdr;-%wFiU( z1g2leTWBwrl<-WzTn>c%m9nCc_=MD+FZmI28T-k^ps@KOrhNJ@f1{H~Ka)5uQ>Lfs zm?7))A9511Vq7|j$U5M1o(ULtrLwxb;}?$$+gB`@`_stbLlu<9j9d7+h+;qadAM8B z5@A{MJEvot7R{MDYWR>L3Zq9V&AOVBl9Gx#58T!Eyqv_Ea{Ya4W>5M_VaShz6-FtJ znxXC&6dW266;1M-_~azdr@I#^Pf!{$bkL6@6ct9!*k?yXnLd6ngrr3=!A5#oTjx$t zQXD>H;Gp4(BgahNV+JZsS2s5t8YOi;Mm!U6b6tU;C_lm5+1}9!AnW!H4)zXC&MpM^ zLPhO_&R0_nc8k2UxQMW@(2$TIKR;1|GRE}FtmxjZZi$d* z0(Ljhxpv{y@pCSbka1@fm5sgxJYZQ0!GGQ>JKswPO1$; z)elPtr0iLah7(2t73|0w2i(ifAS7qC0s^^^ezFdfDkw_<#O41XryM`j?;w>BG|4+eAgodL1yTShhnUH!e1#&Gpg@8ZLm1{9Qy_Jq>zSC6H|nSZfuOVO4VFaM zFJ?oq5y-VJWBL>1%jI;20x}`RMp5B}&%i%|m;gzR@xk>_G$I0eLfQRI#+FA}>8DCOj~}&)e1Lm7%#6{CgbJFMR;d1Wc8G z$Voy1Z!t2T3b^AzY6>lU*1#{q#6T~j?0f3M)~`&AAF%o9dMsY#NN}rP#`H^cvhohJ zc4_gl)yp#h)2*RHn6S1gFE!HN+4{NGmFqS|)m#cmK|Uv^Eu}m=EyUf2*Nn@{My*F$DsDSAHJ%ALE6CAQ|EboU z8#k_9y>#a6l{*igzA`j5v%sWhM84HUG0qlxPaZzhx_|rnjXU>sp1jaAG%+=o%b^lV za#CYLyqzqK4fJ>>V3Y%Zi&h|@0%tf7*b8(kQk!@tV4ey1#~}kIJTo#y46UkOaB1uc zO@kXNXN?{^?8gBpE*S6wGV}+|(|K-SWNJ}WT_2^k?$F`g%az9rA2odFj{^q`95e(m zggv(&K7Va&4nI$AZQvDEwM9zfM-Lw|c-WvHhYTMzVeXdWmoy*g8Cz7L9;C9KX96ah z7lto%H>=M@2p~BzE;=#-_m8WwDW}pJvYSCNT7ds^GSgFdCg29137BUB?tA~fS5#M7 zEXd2tN=}Li3l8w{@^EuN{NK~t4>V)Fh%a=CnrcuXo0kKY-`JSQ5Pu&ZhQt@vg$`Vu z0M7)>d7B9o1!e)W33w)8A|Mr26=lUmMFsn~+S$B*`B>}L9o;~l30UQb+GTw+sU%(0 zTAQB|7U*E(YGZ2f@b>MC$4(qqQ$2D-m1hD@=9z%0FiJ`+>Tu)W510J|=uKA6(A-E- zL1`!GSEvJPtPApqkiyF|0VhifZ38@?-MDc<{m|h<+qbV@x@h+NsZ*y-Rh~9|=B&9d zBBiOWsR1wUX`bJ+=isiL+c&IS3W%Z^)0C%7L6;dEWh#Z)mLDyl>~hy_?ss zTDo}NtZ9=cPMR`JdFB-dNx5%qveW&Wmv-+xta51owoNORE|@)S@)XFGr~iDYbDC!YmaS|g2~d^+#T8h*K+fcc zrOhpU#*>0H1d@}>u|IQA*5RuG{$H6uo71z)x%>k8_Q8Edix7;daLe&bz}9Zkzy9a% zt&KG)5m8xr<<)iY*GoHldwbi;vqCLQ%*~xU`u?xKrKnV>5M-t1RpnO;TO_?5;yS^b zbZ-+2Q&Y#zo?rjzEUuDD+Zw7Xt83`i)>h@_XT$|N+4D@m+1br<_*5u5G4aP!hzS8G zsx&0<4)FM5?z5UZ=rk0yunZaU#Us}b9yq`c%Cl^MwTjtKa;j6t5IF5a&NBgXhQU`G z1kVKA+K_3XcjGqC1dKx~L#X7LfU$9)QT`s(DjbRosEcO;rrj6#4YEcC0!qPAh279t zynfUpAKINc!~yET?1v`0jR@*h9iDO1klVdkfhNh>_3)0MM^n!FgAa8Pn?{8RS7S{h zph}xF2OC88a~{2VC2lB^bkX^TGgRm!$V6&vYARNrZD#PYUVFps;Oh1^7)_LgfILeQ zHa1uKYOGzLrS~%Z)`Md^_UNVom#M6>2Ejmb%``V=8EdRsGJB`ai>hEh?E}l^Zn)JDbR1}t_rKN!yOGCKYI4cz|h3Z8u%i{2Y0&dcw z4-3^1`~ceX$N}>_KFAN2kADrb~MbKS9+A1pgk-i;uVUrVv2pY{}8CpPd z6DQ|7Q93Wzfo3|sF_MN!B2V*W7jix5LmBSzmkeL-6XZh>X*j-f6Zh^{ud)ufo-Knf z687C_rLrD)ZT>Gzpi*gjTUsah%BjK^FKuo30PcD$L8Ov*^>iEQU`b-JF*!1NI3_?w z#@p7}T<(1OtOH%k$*GJV*NY`mfQFw+7>!?Z#bF+A^ZQ>Rbh3k{RXH4=A zf?DY9>F(|AY!bwHSXsCRM#d&2g3K5TOavD#$$KzH4?IbYCArxd3r*aMWi; zgv^lOnSkMCfg1$|PFq2Ym%D-Cb4#n-WRnYzRF>;$-Hs_P164vvX&KPUYGVzwPn>sl zH#4@ky<=v0{mj0n?&hy}CSaZkm}dfRAbSE1a+nFE_DRFT5@>Df4%d`2{g1au_2WM# zZLOtbpw8y_t+5i$zToTyn86T;MUF>qw{&!xU%I6O2OVu-%n=ALP;~Hj`Vx9^3`DUd71Coqc8YUo_;~ zK~v{U7%=Du0QnCb_S5uv3%2W7*t>|t!lw%cZ`eHHn{O0u{ygB@Z-LQ2Xz)ml4Wq{n z++}WI2M3zv^dG+;HtDC~n;aJmg#5c7z8^eQYya@!gXbHXSip`s9M1&IGXe8Vz&sN$s3N&zYya`JP1Fwf z^NzROb$T}_0N0@SIYRa>AqldJNf$v6kp|wpdh>Wo?B&wIFpqOU@Rts}|t#9t?;|Jb053jJK zg3x#;9}8QHs|R(QoSb)`v$3{!4@=7f;|+qw8Asc!Z<*HoT6IGng=%gGh7f zT}*sBY2sJv)zDlQ7Zt6os&r4*UE_#rV`tVG^2=wzzz&sN$&jc)y`G85cBJ=}9 zOzcs0S=x7QtsAK@8Yq9Xu~2KH;1Heqkmyod=c;+<;OG&fpov+`i1nZsjDQGLeQl}U z3Dudy6y(RtemS@ssMS{IT;I0*$H8P9@JztK{6Hhn58_6mAILt+W6Xi{H$LPqkP#p3?U%+v>>x!gVMK%F&A$TY5>&el75SM7 zA-*2&F-XDUnSlNLDyjjq10Lg#Z+kjKjn#tG*kFGU8H0$}-qy*(%@ri$4NbrP_RIU; zE^%{XRZdb=kgum3L&CMP16gGiVVLy&`Vlu*+9E8^O^yVQv8$t_gM)*WrH!3KCFCuV zkDy8Jls4Cvyh#WP^m2Cv#igTz89+R&NZu^!djGDsT`a6F$&3vN@N#o;1lF$YYeQob z^U7MD378fD&jidf0YA8=uC{yQie*a{FIlo=>B{2~F;P)q;;kr8O3ulPHhZFZ?#Q7X z>sBmZym&FXtlIAp5*$q1Ygr61!5uBMuWI1()k{gbc*)XLJB{7l-AhW#E6YN?Y^_Y6 z-nn}6@V2!p7A{=42=ZmCcRaVUvcct**apUa4PQxf7L1HGK>tSrn-O-;?r zIV>({V`$SiSs6SNFwX@1{ebTQw)5k#LjZ!0DJ9d&+{i6R_rSvOBYqh4{r3=~@l3!g z538NJcuhcPb_Ie|wOMl~O;92lgdsx)4;?yUJ6|3=Aq?e zXXQk{*tcZHxp_G-m9w(qJ&m+A??2bqeRS{kt=o4r zHSayhmpjh{Ol)DuF7K1IcOTw=cqfB&>Fx$EE5xFH@%xV; z+$gE-1Y}H45BOb?4Gw2-8)3xs_4f3>t9H7)cl-LShwo*+{n!gR3U_33bnokqySo3- zrbSEU&z!dKPEyxfCZ~cPTn=%)?HQFLYd0*KGi&CQsS~FiFYm+XsB#cC(BM5Ho2%#d zZ{N0Z`JBbGCQq6)al)Da761jtC4G8dn$hjkhjwh;xp>a9B~zzNojiHsqQqvvp4L`Z z(bwzr(S2}Y&;D&2*3O+hZPJ8^la!}UI1Yw)!fA(`X96ZJLHO_~y`N_SrYHw-9;2uU zt`hKk%9u{!Ye8HC9u@Y3ZWsA$iHWzLtrWiyNhtN9V2MmF7jqq%h&pfsaM!oDgK!j$ z4aI#51xm2K*n6Dts~&L5;19$+6ELbz`cm|-sP5XZdi|_PbLS|H29fXR+0k```c_s( zOup^z&+Z)AvVHCHB~vDk9W`>~=&@r*9u_bt)5+Qlb0Gz{Xd?svHtRd>vtGi}mO zW5d}>B2lpR5d_YCR3a%%Fa|D#IBI%a&b_s&*Ug_LFueyKV!9)8G zoiRwyga*vcCV87wBC5^xLv`()vq$#s-M@d|fwQ{tsKHH1O=I%5=IYXHFGFq3tEUd{ z-o1DK0hM#Q5i#+JiHS)h7YVCM-bC5IxO3&4+Wzf3_UzlQdObz-H%EHY}PkYUtphg9iOLOcDP1 z%eS?j>b*9zL_v9Ti#Y2NBA+wHj#L~mc+igutDVxKt^uAHp0(=wZp*5%IPY3Yej-s4S1mdsDv~z zcKug!AZ1k+@=UgrzgZlMS!I{ zEG!JKU@4;^ptKNQXsD?yF9wx&YEoiCJlMQrW1^d>12sCW3hOcF2__7|yp$xg!~_EI zWMW(m>^b0jRsjD65D;MLOHNKoY@{4&Qpe;IXcX2Gg&hIJ5Rn%VK>_oPN_0774k0ZH zK#1{7z#PUfkWvW*5>3(;^ys|ig(IqJ`_=ZUm=y_$ikP5QIk~vi*UZVt>B+-KXV$J? zJ!8)C6k)%d9<3zNG&9o4#^Cw0i_2$Eo1i@Ba6ug^fVu0jZPj^u=Ep=@nLfLGcEhYm z(Wkin}P>XnYZEcc#r-0G~Cq4c98Y(MijE4_w!qGwo1Y5xJ=#ZBUGYq-&!6Ab7yik)zs%w`hj~%6;#4`ck)qa6hY;8-m>Qvxf zA8>dD&jd_SA%FwJ@xy4ovCB}tGrF>6m6lf4&St*95oUomy6q_wO|5I$Zu$Au4lFZ>RtMoeo@GqZJ@q!^`a%a zF4-2h)RJvc#WMkiga9cK3c}MfyCzj<*UGsQc_v_<3Am()0LBG*JQFZBLBOUmB4H|$ z7G_o<;b8z0gH`&j>%(8az3c91t}QEglN{yi>|keOWo*VX0SEZ| z2Y`o_Av6O%4>3Eaoua(#w50gh=%@&s62iiWIGSR5a1uasR-^8<81{c!GENAw(a}*+ zwY5;X%!nZuKk2&WL;OVWc6}%CognQb*dZKmX@|n{oj-5Jn?EI~VFO4j09WZ-qaE}G? z!LF9ipWeH9>Ac3-^BPA_UcRIA($LJxj>&6lt9T}0tn5X{;NeS_BG}Y;U z;tfH{0Z6>zJIKvN$}n?6BvKTOHJbYf5uh7-6H5TBZ&(SOn6x8yAXM-3Ou)I#JQFa_ z1Z-_>V`FRY;8;a2J5nf>aOmJnos*sz6CUL6>+9p~?d|2|RRvrID$zqPoV=;F7C!3u z=!me;kl>)ezyRoHl2b!MX9==41gLCf76j`9HxnBGQl1HzTzT-v$=wZr@oFF(elBg1 z~*cjFA+Zyw}w4Dy?@7+&6_u^TfcG5s#U93uH3Nq_@!G9p6VL10F$IT*5dA&BM0~I z-MeSk&RqwOU)Fs1MAyIsc>)xHB8#)7zAQT_HY~u$)6Lb*-5vjXczOH!2NDDk#1tGu z;0rWhf;VZ2aNI>jN5`PWBI$+fQJx7{X0`)s9IjAuB{0ysubF;1yB_8RIXju07|r@K z2ibkZgZMH4&jjr3<<->to2V=!A2+mK*u>IJ#Gp>^?tRzxCfVD@$+P|6|MzdAjIhY; z!qSTBI$<*{h>q^JAG*pD{H<+yCSZa9C897Y>Ta&7C_q+CQVe1rSiv!I@$m`B?*PO! zYFH(-l$+s%FDcANYDRWuMn*asVT_V}O%*!C{)TOU+`sU%u^d~T30M}v|NmnBwT@)V-!M6)aP@Z}dO_NQ8p{t)dKn^<%Q}!IU~~zH>NK@IU8ZA0 za;l2vIxtm%!xCj(w=`&8Ft7m64tXg0KL84Y4pTToNqzR2SXzqf%Eax2Z7%=AL(Dp@uBowaBPYg zhu0VN4LJ-XZV2pxMDIy%Ik|fOqQBW^U~Yd=-&~fSeA>&@JGe&vPxOC{+&*CIi&+r# z35@Ct93}}F-1;Yu0M@7n9mTlypLOhYCVf871iX34yp_iu*ayZWrDrAjTk70YU$bE9 z*oliS>4+*KY&CZ6IkXqA6_np+Zc;)tawXI7Qf|N{o&7}ve;uMQ(+qSBz9p1lp z`_BDG4<9+SYs<3bi)KxoGqsbidC!E&7D4D+SGC5lozZ~zoqlM1yv#~Ij>G_Tf20} z%J~bI|2${%1m&sYC(m8B{q${Q;^T2*L#+wcI=p?`>Uql+&6_oA*7O}clY%6zJ1@>kP{o=VD$3-^;<7} zLZcGUAu}@z>DM6g<(YtSh5(=jz-^!`MW`l#IwlyW8d`5`)zLYFVe3HE63HIP(PXmz zA9T4G)ZV1y{=1wsF`fwUr6{EZ@;dp2c$d@U^h#LBfB=Fu(9Z-)R;jEp7e56A=zlUf zTbbcWjt*wLQf(j%SbCAkxlg4JkoRD6>P5Ed*G*>M(zO&v`0q^MJQFa_1ib5+iM_k8 ze?U-3IOY_l4|Eb$`xV?@GDdOa2t~E$=FZ-JegQ#YF(hZ!9rQS6eB#4(JQFb6TzDp6 zA!-I?Ov5<5;P@hGm)tscyOk9^(*{XaAMH&f6^rUbMR8iQ=iF|Swvj;yISvXWXX3Vw zP+hI(8k-i*(rRjLY(XI-DjBhbLe3rU#U9&s?w>va2$EW@II9c6dQy$T3wF7yXzb)E^>!O7LbhweYl zr=)tCTLs!HCygC9YSf4_pxH73hZwzrc{lSOA@H>WRiD-NrKk)4j%xU@9I5X`Ts~d?TW!riv(mhjo0a zmA1z5od;fd1~f3lIBc?zx8%eI1>0HcT3F>o=-+s#w#neJPEsY2t|HF@-nfc5Tisg^ zogIu`TbbN9cy{yBu}4nUCZQl!00?)Rs6H#o_VN`i3twAfog0@P-oAWX)j7mc?^$|w z_M5zXaZ7n}u(S1(mx%}txW3Mlah-;H} z*xkPWh?qXHfnj5(Gm)gZqNq;V(OMX6l3#D|vZh^1j(+TK6yk-7sjf1#t*T2-%XB!d zvg%2vnB__lQUSZVv9UBL+x%HzxZ$mRhu3a??&ee5Kv`5g6R;l)@Ul4fpl~&rp| zjV|n4vtjp^$?L)b&F&w%k2-tY{PHLleH|N@C-*A@?VcXovw7#{4VRLG%=LICV4ew> zX95=GT$wj^(AaS+56u60#n@R(6$ktl|1TyenW0iEa~9T zoM+{FTY2v72UjoO(7b!^?%n$j9zJ=lXJBk@Z3j6zv^AF}CdGI=J2*PoTbddgyfy|5 ziM^wYX7Gc5@TMg&3< z5g8d36~#`oaE?I#LnEQAUH}Y|lq6IKat#hg`T|6-R)^X)szfNv&E_%?Q-Ct^4e>bQ z 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

    2. t_#1cXa$ru*DXR8S5 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

    GA{YoLF6NE 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 z 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�Pt8(!b8 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%0DsBi|v`5Ax8pU`WWD+8V-d?b^C*;ru0= z?!?yD6O1%fN$^a-bxko!Kb)33vPpLOWa-Hh$Bh{^X6yt>sp&`WJkm8XwZ!M$&=7o0 zQEBb8SyLxZ7(Z$3xCxV`XD{D(PE-4-zNu9$N=j;)cqU*dMHMuI&hCi(PzWG3IUzPW z3eS%->#G10L*#yCCB;R>_?MrZnUDFI&8~>Lz<=~GDC=Fv@jooq>SN0bmS)@ zR}vbFDR7C3l<*{u*NMab=*{&Y2!F~|Y+(U?9Aih~daMT$9We|zi(0_c3kf;lzv@7P z@Jzskl}IJ6=&lMeOO};_8=BS|;cp`p3WjK=5Fo53%mH zy2|3>{FI1bUk_JDCr5|Cyn=yGzyIr>KfixB)Pu#Vwx+bWFe4+<&)dbx!NI}CCNgvA z({KO!?DXVFe-8wy?5!=V0}=;+`S-v6@%i0gcVS6=Wn+CwL3T=f zSb&=Yn69nNt^H$$KK=K9{rfX$kn0+nfkanal8Z83FGq}RV`XXU8!|LJ_<#QS%lpCZ z)_T}dmBrbq@nK#rj&_z-*49=IZazbUE&ugTo(Xtxu&)QHsgy(v0ADzNKpGA6C||RwiAyX%kgi+Q z-dJ1LB1F138vwCaGCgo5(y)6)je@LX%zHZ{K z=Pz`fGh4eD-~};(gJwchTbh#)6BFv^?qFy1`kBrhExll#30Pi1>8gPRXu?FD4Mk}Y z!A^GWcIJkU?%vfnt8z|BQGsUyPD@Qq#f(RzeV4-4WWM60QC?Dv& zz?O^oDmHML^;uX@kT0IgYygI9$vVIkU?*gr2^bY1q~U#oeVz4!{K8rk9dWQRq~tsk zuy=Q1U}mKGy<6%#_8vcU>e$|$TQ{s*v19>=y5=pAU3|@{yUIU4)#br$%_GO;z>!qxZ9yP7PcL!ET*sL3BWAuo6I04`s(WPz;g{Q0sA7VdlSQq<+; z=V`+;0R#OAg+X8dh5x-MFPj1Nrlh1Ki#Ih^U4)Y+Ostw(coK>Wkl&Gu1%ULQ8umks zfa5CjPf_rRj+aG+O#jh|X9C8P`2bQ{qCL9Xx_uENz_Ky!`_3ta&D29PWuC z2o?skf|3c52?TTsiUqROh15c9LNG5lbCi)SB4+w#X1TZnHwfoyFyd(= z=foE#Z?R=hb`Dz$M+O*~)ne^MRS|oeBk$nL^@Q01 zM3H75%P%JVB-%VWk+KeCzq3l@LgMckY0O(ra_Aiy_~Z>F8F|^oX?`>SlS8>#7>

  • ?&C>{Ypu03G4&UD^+vSC+C@f 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μ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μ3;yWM~@sWBR6Hy9V25?b89?_@`f7(RTo%*3gi zu4_HkH83`zg6s`V4Q3ju`_?a*JV9m@I*gW|Jo~51H_*WlbZiHWt+}@qx38Kth>uO97hz;J79 z3{>9m!}5g-7cW`5X6x1i>X&ZZ(SG#w*$am9M!R2Qn(pZxyAB*w(%_MROMo&Y5ELL{ zFl`7W6tITEWhe_oIz2_taxk3eH9$t6flU=>5TcWk0i@8c8tXt4kbjf{c>jY=7DyN| zE6Q;Rm?78<+;Hi)VA&0*kB7!CAsr8Z1SZDlXaXgM%^(FjVb^D5P=L9~)Dt>5urG-X z7V~ptHU*mje<`vE>5qU8G(e{_6f2a38K9tGZzm%ukbW^96HpCaSaRE%Q2K{pJ1dcY zK_SLG5^!T(cgO4Bf9~&SZ>}s6q{oGOIXOAl*;v@RLp=`&0P~95x_^1s*Dh`-EzVAg z3gE-T8gtn#u^4G6)!v7mDuNUuhvAZ*}e#5g#- zqtr)IQX+6tYq9J}jMibXg}9|pLeM*`-NfXfTQGTQ13q9eU*4E43IX`DQ&qN1XF z_?)r1wT-=FbxmDaY(lvpBRa(0$oProrL!lGs~kI_di>fG1LzkH1oT@g$QAHNz$|8{ z1a`TCysWh3#JHH~$cXT8aC{|>n%vHeyq99cCKZtBE;TtZJ~k$r=&a#jk31d9dl^Uv zz$6Lrpz%n+wD+@y-JFUaGSdQ&1PljuICQ2$>!&Zgg0y~`HhaQ|F+lnqI&9eRkwX_+ zJE4@FUv0h6VC;GwCmWT8@;nl7US?vrpNEU1oh^?9j2iu9SR~6nS@F0Z^cHqLSOVdC z;E{lNBw+8p-p2p>>*JeVNo#dwt*|&RAtJ)p!O6kO+}y%~M*_~y;*o&K!p=4P91tV0 zi}Ogpb~%Ol9UTaTs47TFiVF^p^mnq+*MFvU`NEmgCsj|MI`uR;vqMrV5m)5qq(=n> z1v)vI>pi`rec`m)iQ~tQpEz;CF219=va7A4FgwxP!^hFd(NyQ@1MRD4PaQv|qH^@8 zvJO0KtsQMuIjJ#vrml7#t`Rzg-607C2|GY=CqQrsA@1(( z?yeP2#oeW<;=#6epO$maf9^Nts$$c9&i`^>?#r6pRFWEV%~iYB95RP}MFZl-lB~EO zcMmTgCo6-e4{qS@TACU>5^xf7{G>Y@Dv=uXBCG_v6X*L})ca)u4U|U$rlf#gLc(Hl zK+69}OF{_}B|cJAuy;MRRAX&73()ZT8&jem%t@IWblbZtHH|d1U|b!#lTb zSiN%b{Mo8ARA$arn|Ix{r-nxYrcKEs0W;H{p_p*x1JZZ^EPtvFkTNbTe@Y^tY$Hbh z{}&0AM*?>D>-*3D^Sd}VIxfGg3JpKokh3X9C(n;R4c4TFI5;_Y_x|m_|I*#wm>C z%Dlp|+_d=UlsHE_e;*r5pqcmq_n2~!h6k|&oAN5k3eywA-JHEc-EH8(02&a|%6TMU z9toJU9ax1Dp{2C5BI!b1ivo7|fR*v*1Of=4M5gu{4VAZ%Qo3{kX#&b~BHu@c=+&BM zjzp)*X>I~j70dzB(caanciF`D-;uz{tiiw%y@5w8?d&AY)e=^o&2EInQ13wl(;ggh zv$t!FaEKMQ^$@062ffQKr?jZ{1Vdd@d;5mwu54eckaA#$)i(kR9B>X@NQ>`o71$cx zzQ-d0(^pAV060XYgm~D$yQy>j^4+J;p1w4)utSz&Aih+5Uu11|v{dD#gn4>+d3w0H zd3duLpXgZRKTr}Eo(k=W=7#Fhf}9L$w8KV93Vq*F3@_{{gwyKLJ&JPcsMS6OA?R>} zVRaBfSVJAMzYsj8GM)nRl+hf#5Ij9)>ri|eVK`QaT#N^by}}J_Iz;kFv9bw>bSm z@bIzid$#Z2y?NEPmCJF!n7v@Lw%*ImE>TDH<6{?(9^AC!*sgUOHf~uoXYTA-Q>Use z-E`{ivo};l+Ew`O$32@@?_0lQ`PyX*Ri~@Xnx?vF&0d{*&t4heaYA4=M(Ato-LrA= zniY%Z&!0bM?vf3=w65HL_S(n-RCJ2&>}pDLxO05xh7}9uEnKo@=h3s*^qv`-1IGZF z9Xt{+OqC6UT%#?i5h_8$2L;VifLv@8&N$$2L#*r;ZEP$Gz7KH5j zfx!;~v%>d1wS}>smT#Wh6`{E)bchgr&WVo8C7wdDq@y}L(AMbjW5WbNUSTnHu_!6w zu76+W*#FaDyCBBH^qJnHr(S6ptOZD6VLnP!0h0W_J?f{Aef8P?4u(%2Ja}jon+!}O zfgmq0k6l0Xet3XK0v^JED@r0lY>TqQpk?uV2>$?HC~8xIjue~+3Mz(lHEC)%xX3<) zdkUL?M*^1Fw~!t9Ka5&VZhZa98Nd)7FkSw4BygHRSCgqOyOO?R9tl_x3A__0shLN` zB&MXLre$zN`~dH$ed!SPXt})H__5>0PgFP`5QKO_d_rP!Dj|X5jNIVqa7AN|JTi30 zj+;1Xm!l^pup)Nie{5h$cbvvX(8rA(J9hlUwH8j^fnf~F4s`fvvA^35R;&G>I00Su z#!p;rWasV&Bwnn*o)73^anqZ{sx##$Oc+05(vG**XaRyE`sf&rjy1Zv+9M9moB=4( ziE;;CTe|oFW;8q!bY@~vORA2}(nqT%%PUNh*Lq{)5fF@O`)D2sSa!Z(KS?Fi;gmZ` zAo=eLk}x}G5S7j`ES-=}5{-?d-@hI>o8oE=EJBHiU(>kXyV&|PfsP&QJyQE@kp0Xd z@Uaz$E~RjEW;np2qw_a+z3lxzf3hXWi^$gh^L=KU@JPV&3JPn2k~5JanwpxCokRTD zBa(EUzxw#v=2^<}lK{j!^Od(>Xf(r_Ok#9#Z`pAJA07!9Dy2;r=Q_5OvYm+y?44g|E8GM;5-^Vh z%p(E+iva>>3Ug-iNWc}yn>>V zQjxf$FwR>0$NTmXuI4W&K)1N!BkO$0jGIWeLRHjXB}2cCQWdLhYZO+`Hp| z=CN%nuJ~C#dKwXnGX7Mts4_X&y}-vZ$e23Hh^IUDTQ$|C{axp)6Qx_95ZdgqOqjf1nB2Q+$DcS})mqQzTh zo3{q)@4U341!t-No^e_SS{dLvlDBO`I|0kk>12T+K7}77Zxd9bUEq;Wk zAL5aK?>>Y2hkejOK1pc!p3WL!W8YAn=|-inR^!)A1O4=!AzngMNw2uJ*1)ErH8uFf z!pR%v-XH2kgF?Exxe4b_iKwPg7?`Pl(k|laf?1>GXRSScy|%3pWq_!!Ld()tlRaCl zUq4zhX0rLC1(W6FX6#)s!KSPd6;$PwRU&b3>~S@PrAKvUPn@%H(~6VJ6{pSHrm=ec zyUffSNC32#yS{kJ=qXdzA6v3)-IV#OU%Xjy>>3|5;fRcrbXzYP^_}T@wF{HJ|Mpvzg;Pe1o2tws0Z$ya@#4*g z2=jGIDvnI}?i;mjO5cx|IBwizwP|u=M$1i=8@c!b=*CvCAM>ovj5~2-+BYL-Pn*4A z+ooj;R;~PD{Ftp*?!S0vW()QdH{Vv;c}n?PQ(JWJ@r`S@^&UKY@ZizoCokU^nOHzVfDZl^b=IV%CkA-9y1Bd9S(%xb0^-fl zh2o4vhtW^R4xu1DDKQ}~G6XsS1f9)zJ7uQ=;;thku6w5?7T>b#}ZL{m5I6~qcb-FX&M?djed5v)&U)a zd~RLzr*wdwanh%B03HdL`UUVvz*^B>rp7j2fx*E+0lxmx>7`LA?t!*Wwl|JHb9eVR zc+t_p#V0zu7+qVDS(2NcQqtHI6P)Gk^6aMO8&_BDhvuFE2_>~C^k!L?wUxzXZ5e?n zxxS9q)lXYGy7|PVW(gb6_?a6>Q>m+MDUI<7_rJAkpT1R5d39}lQya@=Der(?`~0$Q$kpm zx#vl5k}*u@GT>}9HGApZKdv-Mk%EhO`_DlMSTv!^Nc-fx334nwQ?>$|MHyW1;WrfC z+Ou}dSW@fI2m+hK z0tED*&)VHkA|O&kcqCvR2^d(rzx?fwzW|GOK-5%MnjI4!?C0a@=IrE^f;KXBb&W0U zfBf~2KYsZ**w@)uQ>`>Cy+nl-l82y+u-qaq_i zy{ygQ1+=iVwxu*K+=1C&ZLN(}ML5DIhWom^I6Bzd+1c7wVnQ7zP)NMB6;h?VG(R&b zCM>|m)7=g2k}4RGbzLL%vVzb-31LZoZboukL~ww=uaCDEoJ`yd>YIt=;c{V7UM{kf z6Qja`^BLfeG%+buHqoEwoi3pctn(&?b92&S_ijp zTus!Kt5$E=ZvjTAtg5N2j`DZ1w|aH|#yO2WJQDDZ9Xt0NJWeQ~_a8ldL7lRY34kHY z@p_`GdGO%=!zX^cbmi*JyL$RhpTGJ-M%(?O>}XFbV`D2jOQSb0FkZbjFk~>jbUENa z^U=5~Gc`Fb%-_S=-qza6ibn!2Bam=58d@s??`x?a(|#5tM1BR;QJ|3G3%#kxaHopg z*fFC=kD9bfFCG2Ts;SFg8F0$!^5&$&D$}Pbj2kt24Eo%RS!7z*P=kg^Wl|*Zri6n_ z7phE`8_y#FfA{V8-+lM}_oGIQQ+ej>=u}owRb8O9WBJN8GnFQd8u1+lM;|?Y#?5y| zCeS~XW#?BfUpz-;^7!$LPIE?#8Z&8{?(-L~k)>Q-ZLxgqh85EkK5HXb20#k;m@^;{Kn5gj`MSji(tj-5D(M*{XnHIuurl*1A(0_aCx z9s&qi=p6_w@Z^|KV1@hnem4vpHh_I1L75E#g~kgXBeospb(%N8vy15GNN<_ zrh!gia5T7Ikm-y?C?N-fkameoWtueDRLnqC4fCgpMeIF}_|*(l^#eK)XTok;UyAU{ z&0hKky(>1ToPPA%X-|0s4cHTdzs zfxT;%FPNpGGW`b?6;;($$zVJTqW4=reRO8qjx9@<%vAwa=?qoXnW`I;ScZZS1;HOa zJ72rKS7X!C6)R`Wo;g!RRYi5y52q5-1^Gp#LV{ra*#7di*8Yvl<}aK#TW$7C)tNJA zEHRHr%FZt=DP<4sQ<%^g}|lL z`Gx%cQ#T=j_Pl?ODYOE4eY!fNqHp+Pt%J^v&8s#ZxSculae(MR3Wv)Ebd-jC7>v2N zYv1~L^Jh+GXV8Db+Xcw#kwYrzDQF}NaP;J zP{H#P3zn=NUXxx2k?dlsG5fu|h4-1cDpgZu~_9ctw&s(-t3#EakcCJ2D zgb;`8sR<19O*x2OO(J z$J1d}h14j3!;>w*X4CpS60p_9GrKmfUo?OEydM-473JlY`*ty$r0QxaJo}JkdQEfR z=5-tA{4js60+4tW<>$oIBf%Ue95Su?{0twQ+_`(h$`z_JlojOV6(&!XJ5o?oh$8bs z(BF64o4-4|ee33hb7lYsTwV^hP@LC3p9tYO^uhYvrOo?!Bw*?U0KJX!Kk8CXP2h`*NPciKgee@%U?nn54Xn?6nUGWs z8(;!#c&MBXR@}l?K|^gBnc38gW4fsNhB^AF4B&s#c_iRoCR!kpMCgqn3!tDye|=F} zq@SBdOdTvKQ8$V;sR}?EUx7OAC{GJ_GktdJihe{_4`piLdA7H5;g`Ol0dY%7La2wa z{D%1R3ZC>9# zcT(f@g}YgOq)$*}KpiG}K79QAzO6XH+tK{VwbLimHFb1t+qhs3`s<(x!H4(1{@#)o z?rLqSe+dot)HQy5n%By;djOe70+v){#`@YkzjF4Z=F$DT_Z~cYR?pVO3mM8$aTH;b z^tNRLd)mIZu5;$pvHiRD9@f10%G%KrbXG)&uehf&z|rF6&GV_E{s?Ju@38zqczobi8Qd#P8_tdsEOJ+`ypEOBXZQj*-WQrh*qXCkw zr=_T{eaQdj(TyvXsw&F?2uo?kP8j$MjtQ6dAX?G?tM6qV37AI$CXg*237AI$);@hT zrWhhVPkxXq4j60gqX&A~BAWNa3j{EzAXoS1R(al2Oxx>>_T^X|;{uVbKR1 zSvuL7=@@CLXoJRVUn)sQ?>Qv-)K22K0P9dGihBFz-u)|Umv2}( zU0wlLos(y7G_-eeb@%Wj1GB4J@buOdt(}V(ESxDf0a&4OQ)lgcV`}Z-;_iVG0pLrw z*xkOOwSCP(RmBN%D8*BlKL7L!159xBz}`js%Z`G_T8GxppFa!c6UMYT8*V;%ZDM8T zTKJxa>0x#lNIC@rY~H5_Q6vFaIu3E<;=pkZD|SB;E{j{;0gvlk|u-< z=9@rj`mtT>*RGncHjhUFmYcHRiIWRzQ2o2wp+GwQ?4v#HZf#yXmq!8) zb+s}vGBhF_WE(qs2bRwduYYS>6H;s|%S*BoBZHA7?2QB)8kF$gf@BP2^*18Lwydlq zKQlQ#CMq%_JS;RMD3IzIFwnNAr3u++)ks?_&dW*%3MlfgqoN`i9qGR~D>hQD8el-N zMgdpqA0HQk_gm1F7N4NG5xK(D-j`4~O9%)Qi9AWT5=2m&QQL*A51bG<5MIoH1(60C zN5$o}3{McH2RsrmnNd6vaB50wD#A^Y-k<*d$Il<$i<%mRMVZMFK5i}!w$}EMG10Mc z@vuri4gT~uKza3dHlYc2MtqQmtFxoMg>^tkNLYAy3y%cMaH5edf~X$&50(1a#NY9; zDAkXSCg5m_>A?pFE^R;}eg*9RY#s?%3Kfo4xR70h;*G&toRbWzu9RVk6Pb#pm=vR+ z7{L{j8-&3X6B04#MCMAPWGgT@a|p^(tk03z9Inec%Z-!rKdzV3NtBYHZ3HGV?!oA# zCDPl|BW!O*{zySdRT~g5>gWSd5SV>>65#DE)w!9Ge$Mum-gy)NW{z0UF#)%3gZ?Pa ziVN{@c%y&)mScItztRa@ye2<8(#P3EU;nc9kFN@98Ki4rA+6uq+R@R{TA803?(Sjn z^xhR9eCZ_@=d%MK`|m@Xv#qf*H$K?Y#q7!bOJ`4OpE&24$Uwe1MO{-_~`B>?Ng^tXdc(~^bbU%&Bmt2rs%xh zuDbl>AU8|HR}XFi1x)MY$s=cNoLtc5rlE;P0T<#&4eKKYT>xJ8$2anp-wNWn;I%@J3v%m`4I8UnDtwV9#)FM;6d$ z0e)#R01NN)NWcv+_{d$0elx%P{f}Qh4EFV+<6lEOO$KhTiG}qUamlkJd#{~Mhqkh!c-aaO0_~*a;^Y6cXe&65P z+=iW8nkUFgiwW{}b8&I9v$hLP8T#!%|M|DyKMwU2mo-*5H8S3d|j}%y{(NyK=|)f7+auA>a{Bu&Yjafd+ywo2QN*mkWJp#R+ba*@8)D_ zY4Y~D{@t55u3frt@!}=w0%&PV5x}12{1|T+TMIKY!g9tqaCx?LjERrivz|rX(dLB_{Dmz+IhGDnorXrFH_+1kqQKqOPp-N*9i%pzuh* zfX-Ie&^)pO#jXJ2ojZH({DsSQJr&Ym-8 z&ca2De@x8iEprO>dwu)%U;NYUA!rpdv;;(K)(r z*Up_gwrt(LX~Tw%>(_5PeD>M-Lx9bYTC1?YIe!!0_qd1wIKJrAM>XrIRp)atWnCzE}q{ zC_#bUpH^WLSU7?^YBM^I1PpP;YM$6P#d=EOD-%nGqu#pu{yP#VP}4~M;VT_TdFAit zCvL72Q^BFk2*Pe5I#%qreiRzu-=39F*)F1@1};^EC7ks1A?>Fr)y~7;zvKPy;_BQI zcu)ZY%F<1MjXN;({*$O6BfybI0>)R#BLPz$2Qn>)wOi1U2topGHWp1R|HA*23nEJ; zV$OIO|I2W`r4zVCzUF^yOeP^@6X0u;dh;PbWV@C%F(U~=p%8ou*#a^;6O#}zB!a$@ zKwqwhpkz|&s|A3=(eO3>&j}3bsHM3pO;G)Xub8BzC8$h`M*35_}Byub9-_uoYOMmLw#M{{o=x+BmF&$&E7t`sjYqP`cqSpq_4R?FFhx_$lE2@ z%F)Kp&CDV2bnqqI~!S8+uphP;E~>iv%1%=+<9$o z3z*)X-tMM?P)CC>yO&0G@6fC0{=Ji$S8v|Xdt+v8hx|CmOL22?Vr-1hD|>rW90AUs zyR3KT-cv*50Xkz-GKtG00kc#Aa2^Yi!HJF_8K#gnbQrkwYwr6~|AWCX0X)Q& zL4ILh*atHGPl^vdEVMcNzwkdM{xAG5wSfM=@qeR?|Ec8ofAD{IYA(nBJ>-0m&ES!M zTS@{9uWns9OJ(()SE9lclf%1ru3EhQ%ww0Z#PpoJv=F;zcTR0uI%~>|6<42$Yh#^s z4;(taciYDGdrlrXr+el4y-QlVS1n&UYns}otB<=SnYK6g>^`ZbarE%s{YOu0oKQco zYt7mf^Jl5>NWhtyS+dGGXf~)lQhd;>6bgh35FWrns%ztNdNm3#~~8K_`y{OfDd_DHwQP5T6uf6OtGukec{FroE3@2ortnZN0+cl2X$EW()>{{m@5rYA1y_i=@4>C_fiHK=KQVii=By zXCiwyhBet|Kzp-g1 zj|BW=%gkw8pIf>61pT_rU+xFbnhY3wyHuA4kS3|jzewQ%@ zF%J%)Ey&D5HXB@Qvg@!4_baglw*csWBKgYZ5l>O4aF)#WKj=gyGQ5e*$M@&;%!nW= ztqx0rTYyIbroDlq4Gure;__tu1q<(qfC5JCL6H24|H;Ur{wPEJH@Z8P&)4thZtnu# zE9Het^*UL%@5ky0_;+{TIS*=x=1{nVbJ##og_57y||Nn?fu<660l*WAWtC3 z6N}0d0zCxo7Lj&uZ)#q+dUoBmlNx*Py}bQ6IyECFHwQ?d=|Sl&(YCg4ZfomU2bf*o zrKz!V->J*@0%McXGPChW>tX{_GyQE|o;h~f#qQ0MEgN@k+jRQuec#Bq#8gtCjdAXI zY3^n(_U^y;;*S2g6>Hb6Uaa-(<=t0dJQ6UE1T3>mq~;4JJa{Bv9trr7`XkiYLvq)| zdm2A;^nCfKF3kDW@k2ZJ@7#7ZBizR5;#F4G;!s1Uq8MlI@(?GZ6Gsjl*naw= z9k7zGzHoB!gkosTiL@^&2(mMc_py0%T4T$GEf>z~zI^xMrG>4tJ1(zD^s|kK^L4oV z#^v3)BRe;&-*oY^j^@QDC_{Dxy)8T3!^R=V`Qb^O3)=e+?%%&>`%&GqN6tNdZEEcV zBwtapFxx9U?Cp*7H*~eNb4%JT~iH@ko9=FM9?5-`PE z&``Bg#Bv2%pzC2ivjH;~hf%T-&_K66kF2fMR%Ts;OlBCxzF`kwOLU8C&vW{pj0KqA z>}^iN({W2Q6rS1(dE`S6t9rY@8J8`e8f57G{ zj|4o|%*~flRC#?jMPEa{BiqU7*q#kg-r&ssQr#ORS| zW&C&Fj!;(Qk$_XV0j^{AJL2vv0|}nZGI&6xa z4|LVkmkXtRnZ*t8p3rAQDPR@RfBO4x@DWSe znk#E+QbK$p(hC7U!6N}j*jo7p^GLv4&MB!a+3{5*=8=HG;1ouzV4ivs0^|e$cYoo3 zDgVM{D3uV9BmWEkgU;j!F*&h6IT&U9O*~4M6A3wt@QF2b5R{^fPAtqL0rN<}q<=_3 z&<^7U^0ex!Yvc4zonV`bn}A3*8#JrCtFf)E@Trz2ZBBL>Ey2<04N94G{4watM3 zCbnTexj)FiPFZgCmASbEAjt+tCHFlBDcG7yVG+Fbw3u|zU*uNf;`{(45VCi^dSNRw z_do0CS;kglbOIKE=7jEK;RP0es4Qo1QIWcoh?QCJfszC$i+~e~u)twqQ=$NZ+AYoa zUmG&PXbF;m4ny0*Xk^^aeF`uM)TyRjlG8U<~>o^I|wML3EPy{@tU zAAkAdmrw79dfRJ-g4CGsKwmF+54Xf(^!Wg}q50F_|N6(zpYie9fHfN*5gOp@?e6Ll zl$)8JhMEx`30N!{`Y?otUtd{}92tx<9ZxS0cMC&8@_SY zt&9x}9plQI8fZcVwF!~Y>nq|MUq8Hd{?yTZd$rATn^*-DawMfdqV>W+OA~{qw|OLB z9tn8O>W$mB?>KZ$_xfEzA{7!AX+@#SqZ>M>kM7;Nal^WeTej~$crDbJxHCz`XDR+|$pz=emiC3wx4B~;(jq&3P|JQsq zp&Y^iU0CP^oDUx*^_oCe7J@6FJA6Uuw`H_lcV1C(39l}Up%UPZU5$(3gd~)BLPoD9@pIMhcpQ7yR-y}_$9@Kg?Z-t=FXU^tfZ(s zdD;xMWxI}Oo<4u&#_f`ll41=q^%99cD2b(Y%vRojo8ICbX7i&t;lx(hlmzKaVB z@&tK#DSqZp^&Y)3Hhli@-ralm_4FP-F5)H-3b+7Wy)!dXlHx+Vooy^kjNZL7G@2N_7kQtIcQ0k~1wH1PQ|rT}gj+2DvQiry2{?)%~QpBmh+ z9p1Zjx5h)k$DiMW-bW!#M#uCIL&-Oes_$5_YRSCW%kQTTeq?mSJUVeX$SqFiHBW5b zwqoJ@c{69tn0>b916D`t_qJAAd|2#wT+d)BXAxN^R#ipq@Xn?hLt6z&|l`-g1v zdphd-cJE)gaLuY&GiRx)&RD@C0Vk(tgQw_m;&Ju$^tBg6c)9tEy3$OKl49Yb4&X5h*5NWgd^BEk{nkU$Bu zmHkDa3+TK;*#tZiFzufYna0;o9@w^V>wJ|(3ze0Wl$DhhBs63DVS^IVaG%fX`zLno z-Mn_yOw}ns@>H5KML|P|3Qw4}pbvN28ofTfbH~=j^HrxP0}B)jC@+f0&JpD07tr+* z;|KRotlPL|j@oqP$+Z6D$;!(EW8;%jGqMH%b^PAu$4grdteQK2p7NB*Q!r)n6orF+ zAu$Okv>^!Qp{57AhqkVmrwXLssnalJveN9wF8&d5Nhzu9p$->aJ9U6Z0>*x4Rj_dR zlX!p#si{VR5jUvU5N&w$6Q&3QIqa!imoTTG@!F63f%bY zdUEyyrB^nH%q95J9k4#pA(n^(iP=D9?tX4H{7FHT0UimMG{EquTD!Ab)-PMT=SIfh zFr^_$s4+)JD_0x#VIcba_C0IV=BQ3poOvZq+)sfIsGGJ{Ryy{f$y9yszU3=t&zSy$ zlF}sQW5Pi+ghM4rV?A43#3KPeQJ=SH*-QY1PnoQ;{NUw>ug&aSJiUDb>H8fV=o{z` zIJ;%h!nt#nY*xQ^|MAOr7Iw}a-az~Y9m*M80R124>FOUH9p>lh;q4m`92OZJ8=nCA zM#>${s8H+&_1#oan3Iv7mPQqZ$biWp`9@oug|SHgH#eYBcQKU!F`JsnFVMjv#5|<_ zc_d&?qL46>A$=l%2w{O70O?PbYyoN4^(PW2s3c=B5b0s@LRR2RLWTw-vV~~|0zzOQ zGL;yK%m0JLj*?9vi?bP&Q^+F$Q;4>w|L5V)zt<*uTD^OI{mk*B$2E>=>e>r2%F2XH zq!0DH9~4G7zkBxR(#fMoj;kM4KW~yF5WoP)C%OomyQwG`)wTC8oH%^==+Ps`E*PeO zLo&0dMgTQRoefp_{$@}0Zv3bLAkL%5G%p&)CZ-S@5nbE{*L%E+!TswOwT|xHcj(B` zlb4M`BQPU1mEFIkyf`z={`D>0vnLJ%$oJrpQ&-G=gTkWX5|f$E=_nPX_&FL}J$F+5 z@ZLT94;?*y)5Zf6B4gueWAyMyz+3@8Lf@>kpD@TFpYn2Z1(d*mK}QNG^aiS4u0m!H z9GB#`$j{@bl%zm)Kd>m6$DTTl3o)mphywe>e_R5@mtQC)h-C#44TF)t`W1h(m1X?R z){ybPbUK^BU4vI5a)a`LXuW@-|2zNF1WK-;E7`e;X;i?b_4IN4kLxM-f)-}Win>}V za^izM+#{uW#)*Y%*Z6RZ!UZ@T-(-+%w@eP?-EOt9lq-P4+< zb#A7Mxnw9p0u}fE`r9AB_BR(NMfh1izHnOefjbwk}ao}>v;IF^@_3wQRf|wv5 z^9MR7PiShMeURBeft9LCkR|W``o};1*;|_$>gT~D0rN<}=nzSK$0VX*}}(<>eIS9&sk1%)nqVnqZVCMwlDv?_M-rS$^WUQKKiyD@>kq$Qn?Z zUf$j~H1;$Hnm@jFVvX8lx$$F1j~q2oUVhrVqlTt7j_#gbu-PP{&?mRG_pX|!G-1q` zQ6opg!LG9K^z%0+7IrQk$U75D3iPjQZQZ<7e(Y#mK4$zR#hI&hA3ifMF}HEXQ|=OV zT3^)Mwqma0__5E7w)9YfpR>tf72_mA={$73thS$y{oDi=96_v~ z=(RP>z{gE!;LMLhKK1{n1sJ_r>KXvGx3{^pG%H=$F6toP8Kc*Hp-X!6OfMedk$`z5 z;H9g#?l?(KLNiMndk5xir#;)8VQ_ZaF63cfy>j8&!>6x}%&b6mVyczx>6W^jq^z9e z5O-TEE5aCdaq~d8Z}1PNeQ~%3ic4u>RzehD0N8*A50=t#19IpC;G-xnD>X4bE{@>< zMnw@Fg93{LaRtsRC7jQ+l%&MOgoJniEpk-K%*T#rIKBW2%gf1Rv3x1gCv({y8qN|CLj_PZWMI@CUJQ8qY*0Tfa7tK(TpD=NP9FGKi zPygAAH*XD%(9#PPEySR$^@7ygf^;4UnEXx%ACnD%K?OU~!6{veJ0m}jOA!_dWkmYP zCcwS`kmr{LzNV80k{x1j3(5edTX1wpFdFm&9o8uOg55$&XHN(s7Jawyps0sAhZIPk zkzGb2mSijtm&rT<^K%~(Nrbd2BXd(|0u@0KN&>R{g(WgNRbrv9C&fll;Y58P$w^wu z=vbf~Cq1+|>TXB#af0ov1JEGMEYNXVbl?|>dWJp@_IEZ{33KzRTbp2J5p-N>39ucS z+q%%W;OF0e8Um76RbEw&K6(t6(^}@{bU{BA83a(dd zaUme2+q=XAfBDPrpFi|>HP#EWl0rS)T>T2lkjj#uC&1ybqg(R(fBgQ-=V5?x)s$o= zg?PBS+B+wr)Ejl)u>8721AqVPub)5mx3|`o3)12PJzX3fY`xP{c_d&TUp(yAE;3Ff zlFpWf%Ho{lC?I`%dwCJmhp(?6?Vff_K*APwMtw~ga6XWP7Zni}2C&bNP=+&$-GP%l zwheY3j|5zU^n6M`A@KpEUJPPVGErbevBVXsA&^ess8aqX0m3HmNWeT2@RPUZwoa}% zNdmT}S(p;xW%uUQ!#e;FxNu4L^to&IpS`6%|BQ}vZC<#WmGLY6`?qi3ym9sXh3og9 zyn1J5Wo-*OaC&iGEl>2YHG28viT@NWV2Q zHZilXvaxe;qRJGu05k#>b!8?*LOytTdSJi<>Qe_Yj|5Ei6Wk1>e5pVeVo+uQP<1oC zPs$tKg%*5UEXAKiG9e$B!exq|X>V%&K?Vem1Waw<(TtYtla@B&hXrG4;d1O*aQ~;!v|sLYphFIFLmNaA{a~EqtqcbKyPV=2f3HJ z_#+j6P~6#2URYe+hM*NqkkWgR1pCv^9{`3cYAr9#$;wO>)O2wPemJf4^bQVw{{6Sl zLr4MdY^kp+D;A{3MI{vSNWeT2a86Ekj)X@71~YxdwqPt0GN`wJ_-Fx62IG6ixL@%< zwQ*r*aYmrjK=up0$OH+6Ra;s}w1CR;me~u7*OQcKD~3f`hQ!p4rrMJ9rL~*xO(7?$O;#JQDDUBS-da-Mo3t$`y+j&YwGX?z{zy_v+qz zAr|*#o9N%xIi-GJ|L!e2H?3W^bm^i6ixw?fx^nmV+fQC&pNWI@Zs;7_w|m=;Et@uO zSiNfXsufF@uiAL{!Y%z5Z=oM}Bwz>uh!8HBh&*vT5->~OZLN6XTtWr}pyrW)smPK?0_Kr`sf^G!kTtP|?4~xgRe334 zo*wY-ySaII`vr!Cql}Qc+%oqTZJXBShU(IS94~C-&J2F!EnfiN@d$*vVfaMnxe^Q50I+1emX&C3_)aI@jX?d)DD7`Rm zInn842?HM?^iuXdx!K7Z$M~D0bB%b)%gD`!_gNM(3xdmz5IhoaOWqk%0n^`b-bn|Y zr%;`XD(u$wimi&z1Bu?#Csps9YoP<*BjUns^%`^UnEku{r|X&LvZbSN>+vUsNTDb9 z#1{jDRaTR0vQ_M|@cFxUlGgGbYA8b9Pd2z75pb_ooLXRQ^0wvawtEo`eIi^zSqQM> z@IX7-JL`gUH!syUdYg0i@tJ*x471Ufsk*L_g<0Bwu#jh=yJ6LW{m%^QBZ8kETeE1} zL!acFLSbcXV{3Z{j|6=5>@~e-hUUOApd5bshi6uwALC~7;`!@$CT5n_4lbTlNE|^` zt}rdAo*PlFrn-t^0enFTY$PTnC9!8h6FQIqK+IcLMHp^*tffyTj|BWbkw97fFImXs zvG`w*K*@B*pdL9CWstH3e$cxTh6GA8XaRib?J_#^XWCBbZTy$BgSg!G5i2&`8$&{^Z5bHSCa<>*w#Ph=hm`2Fy}r-AOOL?0`oCwFh_ z8HWMLH#0X+Ajrq}(+3m%{m0J(t%XUUuI6ta-MafWFe*M369j@hh))3dqLc~QDFf=9p=jc>fO?0A4dfFuIHNHO{H4 zD4tzkxrM2z<_yJY^9&?(|2Aj?cw4yDdwK>-ydECfxlm0}L4JNpcUxOCG99UfDG48X z^!+x@kr!7ln5QTwr+lLpDG)%57XlEJt_OKgpncxz?8;eF6eh~auaC~jM_;;J9tk)a zNS`FEF`&n>2Ow>l;AdKOL|!+M|!pSDT`w zAg{RN?sF46XIFPG-#~giSOAY44P}H+*Q-pK$|C_I)g9f6NPW~2x5x%X35oOIYwEgQ0)uSbEVk>g@pzA zd1SG}n8rXW1ol(<0_91Vg`({e6~RAztGud zW$%dUuC_91CB2R)#EJMaaXvx!!Fc^1j?P^}aXCiqfPxnfbY&b&k}x_h!~em;gaj%U zm&GQFdi$O4J$laBAuZB%dpc{&n|u4a%OWgGT1?)edm^14=)aF$ENZAXbE6s`mD(@O+!sVKHWRk7#V(`Nli2sg?S`qEH%_i%JvyeN4>W*x46l zSYCdvxz zoJ|;?)a5WeozXe|r!r;a*H;KpKnxL*gAB#&EIJso0~B`>CGMhuvoXj2%ygzV@sF9{ zcmqkq*Og4YCV6wd2jJ*pc*pyYGzyS(gB<_tpuBNc` zsLt$(b2e^TadJ6a1lu%LuYZ@BnUhygBo-H5Up!^>l&R~FEm^j1%KX*xBSucvy+338 z;=t(WgtW|_7QaKvqZUn)H`*>gdbHw{Z!pF$-mExwjZbKJcx0TU+j`Nc?@ZULU6}O! zx8JHPoHAnERON~Cv>yKmICDSba;;<#~>)uzdf87((eZsg(% zpc`93{^wbp8F%8wv~Nbvo;G{KwoS_xtXlcQ_%U0r+<)=T3|)i(G$JAc;kA2j>c28Hv9trdtFzeN>z>-8dyj8iyRG-&;e!W{9zS{c#>m9N#=)7? zS68Q~vnDM)F~Gys&E3V$%FM*n!phbWT?V}Uhz@MLHt@etke-y75EmJO8Y6EXzrdhi z2mxuti1@#r>I((g=>P(Vr3OK9aq;o-(tK9v!Y}-vn+1F*ND?;Kd0EE)r2cs%;Jl>7 zq_|Ah3%;aOi2Bao|NC=OX-!jWQ%eVcr|ZfDNl4aB1W=#=4BFk@``4cvs)R+Az#;4E zZm;d?Y)DQ_kBW#%N=e5YQD0YhSw~q;Vr+6&RfnXvv$0*=oSmBzZ0Z^tAD^7o-*R@p zM}(`Ll@)-sqYB#xdOFM7TI#a=Jk5O~qhn$cjUS~&`}&5&r2v~SBR#uH^r5S#p{=x1 z7-r)i5fSm)Dm*kQAx8-G6tXvJtEweG{oK{o*HD!nVdE7R{=(irHaWkx1z8Q`lO2^?#)I;_jVy0bvjUY2AJ0hT-YH_C{~a z?SoUYa^id=Qi5I@zPPggrl((UWNdHN`t?R8dN;4%xOMNbiBD>Ac9dCwpPRwiGY9qD zJbeNz4)938?2w9sG#y5{frvVf1dL=N9toIs1&;*mpORNpRFIpMmK5t}_TuF^O`Xjv zRHse7XlAib=ZTebctI5vqc_yi=`K6a_`=b1KdxUiS#JJgBWsuN_>`i0R1#Aw73xJ* zQ;}?P>(qfgtENtnKk=fl3>b|~h^CPL2T>7BsjE%5ymRKp*4c{VmfS(Oh+14QKMN%{ zVM;c z#`oPd?S)EmKOp$R=y=K$nx;Fz@7mm)_w@eVEedin~LNLK$4GtxXMuxAv?ZGnQyoxE8Z(8Cz8DBF;!+4F3O!LWPzw%#Uc1)6qSG=PR4~H^Ji|1a>Z>dA1x4(8VH0o; zz+i4CJPCjVF=ripGS0U}r*4XRQ^p=WD0q*nsw;_3gVw7-0jTeWdcfzQ3m!fc zn}HW{zO}R46B!}qx*E(615TZ{Z z@B-7*lDJp|7>*7CG+3Cq)|t4c8))o{AD^op99^X8rg?`v;R;{E3)}M_{jE^UJ zO?pOQaf0;* z`;?mRp~DZnh&&Ro+zkMcmz9+;{%^9=xuU7Dbk>wfV@3g{mwNBR#g z9Nj)=^2AXizaKf0CX61pEUf}aEuh!bML)1~zIR~LV)?P7aQ%0H@*Od1yrO|nkef}w z>vitW+wPL{)X|3)gRBi^AG0zP4)hT$LG9<;RWyR`SS^ zV<%2hSg)b=!BmeO@+z90*u6N}A37d!>`u68-&ygzbi-6W#^bE+)LE z!7YI)L&JpB3*Rr^kwX1`;E{lrFPNpGGW`b?6;;($$=rz!U*5-l>!**-poV_wlDR4~ zRc6dkRh_B2F$v)hL`d+Ze)#Ns?e<=cO-om-oHcvqOchlX)mcBBN=!!za;cEM_>b)` zZ)@$}xNQEyd9&4K&s3c`bH)<$h@|ZN!je+1wQAy>#JRH8r(asxxM;*Y^xf z$jHqrC}i~E4<+w(R<7B&c+Q;Jv(y%DyJ+a-7m<*bB@hTW`rx1_?9zd?YgQ~@w^!$x zxxIT}WE_tKOlkamsBNJ^h=t$?!d;;pcS>ME`?$8YK6Jq125W*v=-0#;R3yOY;AVn3 zWW7Kr6_$nkrQAu*1;*|i7O4!7l+EG1B8)DhvUNDoKr7LQkzL;>{SXY-l)DSb4{$KU zA(ehY`oYa$dGVOQBLS1#<&l7U2f72#ZC|o@{=8*dweCK9V`}H>6Brg16PJh`Lz@OQ zUTt|H?#@0@F%d!jKnRVDiARVwBa`hZ&}p~9LD^DQUc^AOa&iTNJOCle_CMname)y+ zcdEaoQCdRm!SM&N4d;O4ym!=OM<`_bl{f%ALwto9BU)YVq1w0Zk+uGRCU63DKdM$zWF!{q| zkc^H3Le_E@8LE&nGWi3I%c)!}0Nu_41r&#%?JgZm1X3)Z69s^z|0z)MJqr#{c!dHT zD7}^DOAz}Yv>+|=qW}(z;9xf8+pM)%I0IIga#aq4CWR|CQ$0n=vdZOaJu zw0&`1=gg^N`*-aJ9M| z6@#yrHn&7zZ>WFcqPDi?;p3Vh+q-!A1yK?|+lQDSE_5|~@%-tXbEnVWdTa0MfgC+7 z9~F(e(=DL-+YtOJ&5a2`?tfT#1f_Sy#>FRKt5aVnF`49z^>Ce`ivaTT(l988Hx*2V z`%AO@fdp6qC?N#a1i86_96&K=f<-Wc85U$!@JPTs60qh8O|7ebl@yK@meSsR{~y2q z^&jH0ga8lwSJzIfYiJ(VykIF1z`)JR1G#7Lm(Rcd+@2rp>0tHb@~PwM8pqUsd>s>? zkcj<9^!`tuKK8X_hq~ICKGHpL?D(-`$90|tg<=;+MH9XE!?2{QDc#?}?DgIA8i$V@ zJAOjvu@_owNs9z~`y`#sWpSQXZ}hL9J4Q(1TIZiQxuTIyFp2C3N7F~JQvvS7g8p7y%Z z%m|k^4{l%A;gNuOBwz?km=u)nz#{nIYoKJxjYguj|5y?Ajla31tP7|re7!pi{Q4JkxzigAEJJY zPWCyWbaJjmN+Mzthz{H5%fJlgTZC(|g%CZ6&W;0EL~8bvsRQo^tFHr{kUYr%;s*Ni z;1nZb75=DlCVI`64~HqJJ8W%i=@#=yz&sK#j|9w$fwPdINwvFNXAY_qLS7$_1l&eR zjwC;-*!c=ZrYQXRCB4fhPHG+1I;?43-bPTwga*jbCEY>R?&j_)(eY7X!#NsP0%dVTG}w)rYkr>V|2 z2POzkYn61s6ZMq1hgPM!8yP>+)m%S!8hl{WPnVISSy)O5oFYkzM@DaW+vM_7z82K0mp6mD&_VIb{XK<=%p6M*^lCT?X?FC`jmPxcUf$4?Dhr68=#5Lv%JHUjVgA#xw?{fWx)O(vP8- zk@-`LR-=E?VQ^FWHMBSKlcAaG7+tFTxh2T=235dGjB-boXHH+(tM+Ebef2aM6UxV2tU7k9trrJ&hC9jk7-`L{NwqnH+dvrAu5VfBgEs zx2L17T$r5{?dRs^;^biC=Bon0L59o>B~ z0f$BS$OnJw7q`_G=4Ql427>P9Y-VO<=i=ob5RBR-lm|(M`@5RTa0bb_s%xvt@X3UEr{n8D(CE}Lyg3RQ&xX4g% zJ8L_8M`sk_b95dFnAry~5LlTGsckCsLGC05vmJ;|76c^&Gx@+ubcoC${j!Fa(g}p( z%_9MGks4%|(pN)9Pg_%caaLT2hXcIiw;anGI3!dVoxsIw^0Om-oK5ugFKhq!s(?oV z=8=G7W1^#BvQzU1PBVkC%8;8>Os+ew8BjdY*`U;X6b%9(40N;sgK|$#O-Tlg3hNj; zFV*@G0s#QT3eW%y$Ak1V_zt*1rH9BVfPTkpWG|s6060}bj?6l8lIc%DNE8ilHUQGE zh1|td7RhxD;*o&Y-c4w1#Is=M`x1Po&G_Cr@*f>qGJE>uNpj=HO`3V5q)A2xxuvza zBlGI9or{-Fk)JenBy!Z{4rSHzNWgl}jVx^IY4^5N>Frqh!<@+q<0nm;C_hbQ*2>-L z=dM3^W?*93K=iuW!spxP&QhE4!;I;wYI7HD+OKi$%AH3~U%fLSI%;97s?%N_*}8V+ z%C+ma?m2ey>_znJ(SQ1qM*`-NfKhG0BLROL>M1U3tZr^BD?%?SAfh@uI6B%|*#*T9 z|NP(o^`E~32Dzb$96P0D`Ki%izAn!8_V%_m4gulA?}z@+KYsf()YIMwTc^5IkdYMW z>+a%cW6L7}^GLw0E#x7j4i6Lolnv@kz@S%nBw!v1xVOH&qqZ*A4xF9+O@R$yluECmbQ&>P6WCx9^1ElJm zH8O(3OGdA+lLi1~`d>QX&jn<3+=YEi`Uqcrf-l$0LXlrBfIj>UU(^3z_TDl)%4BO7 z-ZR7C5FrEx0t5*NDa2~LO-cXxMp_jD5C?%f>^J_ED&%zpQE-gE9% zPY2lF+24=vx<32y)HTzDtg3pN?pjr=mfSZt1FN#4IAmi(TV`%07hk0M2Ws3i+M9VM z;J(VFKwEoTkFG!e=N}y{b!kyCIfa$A4e-}@^`h{%voa^#%FNQzwdc+M_^Yd-Q&=U) zNiVD^s%>r;5B7)}1bLahW>)6r&bZ_y zoPx#RF$Gxx&}wvGssp$;KT^_oc0IShf5`5 z>N}+WL)tCr@U^_?tDJTKdJ97x{(qzY?aA5T+4#QE2gNf1<0EM1nSgmF;4%dICC5AV z9x5V)8h?NKBHmBuGq*<&;3A+38N9&I`A+ho^Pe_4qW7dMpCS3bo&V?vD}?Sb*rA3D zh;RDIZDRk|&i}fh^Pe5?YzBG)qdEh}M^YB2{~SjMZLy^P*n~MIaGnYH`jgK5M5Dc1 zH!Ck#b?m-la9m1ePI92N-t{AEfN3;!$pt-ORg}HvgA2_pr_nJ)ywya#OvTpvY*)wNM znxwdB%@LIQQx$1@{>ziw)-KzzYT@D)zs{RBMRCUDY4ewFKXp^@$#Z<2IC$zpbyT-+ zQ(3Tl$pR%MrCGBVuHK??{+8ZTgIACOy}hlyKH2vA!Og3e%$+lD;quKq6EMXTE6Ynu zN0RA43eFvqoTb9fb9S7wB3#J1PL$3^JxG(7F2lAU3H_dHG!g58ACP^9_ASG2sK-zT zm?S(Ca2-Akq9N|@vCZhO&QFhbu`)6Y1I#V%HVk2mis*n^|0dfce^6YNALVBD?4fl* z*=rbYAQ~w{jwXuudizCg0->n2BE`?j;QoF6*zBDALIz_|#El=Uw(I%O*ODFSYOHtr zp00axYG!s$ZeD(VF3eKQKiCrf;ca(KhL5fOgFAQbnnxvo6Dgaj2-*04o(Wij2GI** zcN}tLq*J;am|T${4jZ-BUKiy9D?5t} zATo~G8j`%SGM6R-`RB0knfc2tGQ7f2W+kq;;DYI$khRIa{yGN*mM{T%`pE}Jjmfze zW@fW086=JV2RZA7`d0{fCgAL2sa=e-;p_E<92nxRl9*&scdyUWXZm7>h4_^Tw7vFq zV^4oaR-o-Lw0-BzN7r>uELpK~*#Zr{$2Xn?N5>`NWX=z=a`7=n-h z%rgNu(FROT2A&Dn&gj;$W9Lt--gM;f+H1P!ue%2Vi7HwsYEBDIZ;f|!e17}%UOjii z^IJ7ER&7!}bfHy1jnw7L}#Pk6pBM@c@n%PPMkQ zfU?41hv%EtU(vgI`{?48D}Plu{7Co8EeB8E0PGJH37#R59wt|phXPpsi9+*WF)j ztTG|E$?$1?Zx692BN9P_MP0(Gs^^xqO-TWd=1p8Z`&NG!Vy!f~p`PA!XJwthFHJ|? zI`qQa86%`;tT=e7s+ng3-fm#!=!X5}$)ZskHc$E6-=wbpI_!rZfYCoGyqh{#rA2)8)LK8Eqj?T8U>vMnHq_b-5`d14_ zKt6obuyHc;7LOevuWn#s25Q!Tos)kuyejuMxjoZ{4I49N_=umS$Bj{#y;ke~Q$y1Z zQRVY#!+zMhe8k^=P@FzywEUPKe;6hQFeH^TJQFbZKA?3J^dfYh2Fe{mVnInkZdL{X zNDyE6-zYtRkSO4kLt`00SoX*fl2im!iowt=g&cQWsShqN5|kgvhs1QzVMxg=!1q;7 z!|^8QdPIm8fD#yzQ$Zm&T-^6|ptrrUrc_W~Pwoo|h!ARx*wgzye*E-S++15*5z^DH9H;n<-R#vS@VWDjuL$S?cFYjWo${#;>35Dq{c2@Yx2@n=G8n$T2 z;VY*OgMa+d*H;y7Z%K=TEmG=0`14&|44C=drw{MjW9>lIO=BtIL{qRPW;49|@>_qB z?JKr8DJb<_2N87`{P^)L&jefsZ9)kuE7hZ)pnE|1Lhd~joXUC2C_#vHd`SJdV-NZ- z(O-BJB|8M$BmOu2hn(#n^fi(Cvs0G>{9nhDhSJy93WCV`R)P}wUjH$izS)+h%93R7 zATQr|L1QcA+;C_(#TYw<&DD)n84>EdQPi{a>(f0r^Z$Rw&$$iLccQy4qt0w31-}v&-$=_ zO>GScF|oSp8mBRmje-u-DZu8yubM@z+1W1Fj~*i-%}NKvkeGhOGXaC?lV<|1pjE*$ z0rO12%nZN_-OF%(@l3!-FF=Y2&jkGXwS?%jPT1Gi-O*HClo1!5T3XF&@PU?qWEqOI zR5ri=_->$A*ie|q<(SJYHhl${jj@8ubXD*Ted zoE&a+g(a= z;u>E@a>fMM{KwzF{PyAPAPSMn(j#I*e7)S9T|5hL6(f0dUC%%N`10x9V1HLjl^{DQ zGQ`iz-Nn^8t`MBA5Z5-m`|IytKEB7>YpxVz#e@d=dU?1wIr?V-#Q`-V4Naf^{PM?d zczkDlb#X>yNPxGeo3n$xdtzcjTy=F_W6PJnfBEw1ZC`g=U1dQEm~y;5Tpb-9Tq2_* zBdWj>-uCyufBxt%hSWX6a?I?2hRj-_w?>Xtt0z)Y(H+ARZlP+6s6_#v#LhmXJ+(V_u46q z13NaVtX6fYu4XVDMNIv{#T((}WMaTG0q@?ZvTW&6W&F2n_0CtGo}RDEE2}HQee7+_ zpWM29LUr5Pm5UcIUIO`Yl^xG)Z0xZ5sw*R$Y|WlOxOPTMb(6}n#fug#UZSkLV%=8V zSFbFwVsC3@c<=f-t^HfpDF3=};jc@UE?d2B|AS`+#za9~Swa*i4{lvLy?^s6 zEmuJ31s=nHuWcI(JfS)5@hwe#QLDRMzgiaQ%+%Be2&JiM5a6 zi@TT4XsT^r10LihOINO4zft|%Wnlf45=nj8>tg!{_b;42a&Y^))hl==V0Z?3Cg4hJ z4$d?% zS5orYe$BFFO0%c)Ou(rrNeOW=kwHFgj`nr{VzDj)q!*IC3jynuo0FZLm7bcMm=GTu z9qI$)z|GY~P+Y*0!3zrVb3uHVoq-O?DalE|Qw;F)_4fLahPl*7bWjD>cW&+gy8ZPki- zOO>W6C`_HQCWr+rZ-O=+Oc)#(s|33XH1_lZQ9f&$!+Att*ycQZ+iXo z?;qc@f7^z&^XJY~m@-vCamEx)fVB}m7BT$_?Tw$G+_h`_a-IpevF3GgVSYg&9K=Am z#NI+o!%zX}0Kyn7KaWA-f%F~?E@l?b0GnZNZ#RA4M8ijZbfWRZr(0D?Oo*(3rjjBk zfSzDUxbSmRDR1?VxDSk$STVF@s0Y4zj&9?7Ipemba0-h*({macfKbIXj3+sWk!S`Q z4?aJh2^dVT_=ND`NVYE`6DD>DCAfoZ~7w7Y}v7Dj?(lglV)9t z>*^sJm%^(RDb3fDkJQJ|_nPXd2R?SzMGUpc=Sy^eR#a``n!c$Cra(h zE6C5!%gcv+u*1gmrRK)gcqw!+n`}Z`KuToZ=HW`!4 z;0Y6C7dZL`g+|20(f;(t;>6jtJQFaTC#(d%o-tSu3Qu7Hoh9r%Ar&I+WT$^qeJwmt zgw#Vv4Qo&WtbOXh0`gQuNPzo8Vn$F+=R3~?Oj`uPe%MaPRAQkYvYY4^NCpt&io$v@ zL#TC#2@u(VEj0znVcyQJk=3x20Q?R}0(@VTn}UYpxV1Dn#MxNy>Uo{eb}>@ZID?N9 zclY-S8;fFtTn%+Do;-KSp_wvXB;#rHz+g{jU3Q4KqtR_`4UHq`Juy9#H{mS_MSXAI zz3F_N?Bitl^x6q^)uX3xq{AA7nby=q2#Df0Z{H6#7sh(nnLfC9^zb3IQ`%O|%!We_ zB8K<88T{>YV@`;Zg|W_A4OP`cswZ`GnmAl_`gik8z-4JsUX~BfYpSd5-??@B?){p# zt-ulx7!n@M<#6&$z_?Pd3nBKMa-#YngrP!Q`dNYxE2yRG1WZaW@X*x;hX?u;6_CL| z2fZXWW9SFU2Vq%3Btv83?|vjRf;H}UWD2k}5K>(<*8O`qCnQZI1Ay!BZ*r!k%vPZu zTn8#>q-oi`iS)l6m=Qb^FwX>hQ1kMmm!_6>j;@|`Qi!`cE3=bA9rSf>Ts?hg=k5b) zS~nlQFd?}|J4;a%H>O7gIK8}g_1amY`aW^x{$qnz)?f;OoN^RJ;@6=b)-Uc|zi~$W zu*RwDx=&x4S}-ONq(;$MBodYd+nX61KD>11?DeOHMkZz;8gX=SbElO~$#0z<%{b3X zv!jFje0{tFB`6L>*JQfy3gbW~JSSZH{7ghY>!NJZqCj0rrC zBD+aR$%(OXaq*;PNX(+rUVwjrdka=YaREGe8R;3R=){zW%iSgYM~IMEJctGi4#CW{ zR3`tX{{;O(;6Ky_M02^>IR9`E{)a~YFbpQ``wvq5ihJkZi)k9sK=NrvFu9`(o*1qN-X6~cjL(8+0v6?5*u8P(Y zHA!*t!3)>#Juxu0vO?-9(i%H`cCT5nKtWzcR%)!&lo`KnKYisk&jidf0fV!ME)}Q( z>KXj>`=|GP;*Q$12>WOHE~TyHz(tM~%zT3B`SAJAKYsf#*wbDW=VtOi?~z3%SXU7q zEXPd&7%+qX_{X20e|tM9t}hO=dGX-xty@7YROVe#hO?!sr|);jzq}t95Y=V480p== zrhV410&FVCQ-Zv=Z{UxA{Q1w{-uCymmnM3eJ-mHg>%{F$)pJ{GXe8Vz&sN$ksj0ifSRZ)$N0>iEvq+cJ@zQ?;F*BuTg1i0#U~_i zW?XD!S!03A_2Y-uEt)xLqKvG}q=hei{Q^V6!kN51!Zkg_&;00~-Rl(PCP+(5Pf%F= z!r8?QTz)Ws+uAzAUp%{c>->tvtCvoZmKirrYT|Sieb~n?u5M&tws&OfUOlg|dBNOy z)1}6aA3a)X(v0oTj4f;(U0h+=wgQgb`r3JojmzgvlN~EHZuDrGDN08lJx2#8S6uRd zUE7*>Ut`ZIC8ZfMQsdDk&su%u!BZo1YkOy^UV+=KHR{xcbxY??m7gdhEi+}_GR-@> z&yCHkZ0#XNg$t_2f>c+oUNV3F{DnLdFan!A6EHCe<0>TCAH+icONS0@S+zo0Npa5R zmjw+(c=k;$?(n>GQ~T`hjq4XHOr9vGusgni93?~JMdFx8_QqFE9NMNbf3oa&sfh|Z z@@p&UGA3a2i>vLd{7vp3*t22LEO{BJaWa#q>(^HiQE@TzUx-qTopao;?cB0*!3?AeQ7JbL=< z1u|!eh7pAW={yrK;}ayalK{qfCSa(qpa}Jf+!~>ShGlOwHBxv6(Kk3riYi)T>a&SOW6$tJs@c7~)u!%Iabqf2D#E%k#rW!$7N`RYNXc;QNFuX7y^_ng1 z!rnjr`27A&PkUVrlJJ9Eot?b%*!Gc=jg{TnA^QA}&!658fQ+lMC@ntF75ViJaVYgh zoj2BgduQ)oe@BjgPfJr(X?AjqpPQqdt(8X#5tCX{atO$SW!b#ZPs$q{M*atgITFLPb_un74zCnMV!S;y7Q=fVhG-Fx|lQz_{3onlUBhjji?J*EX!4 zH*1Cx&jkGQh%w`aFVK5tWNL0zQ`;D$vF?!S?iGp?$H|TxGjjN_;Uh*POKH!I2hWUO zSpqAfzCQSpy2cW@$rHwn9yNBv$kF3ur_A4?d4XpF4i5{(V&(8cv7!;Z$Js3?#^Iey z$^Hqk(NXyP(4Z2PjKQ0@fr^2DQCyUd_#aaJu~dmk9EJk{XCSAU^2SQY^u+BmD?Np0 z0!Dr!&jidf0SjB}D$4|g`8nw+@o~{%fqs60LBL6l=<6RC^Axj*cdI-OclJ{{7fb*xQAfJ`(AgPV0 zEPIhcNJ)fkFcmQcC_3nNh(7QTv9w&sD1#O-z(Db7fW~uaKE;H;!~rx>q71_^#+?(H zK8Q|HE&%gTQ4RwpK7t&Qs+$C(SuPRDOhz<)23vjkJ zGC zkAZ7fU0p*zSS)Vu>Zr+0i#0HFv-fnfGlO3RajUL>*dH&dOO%3(Kht)N2Ju?;aOu*0(=odMkni#t#ELUXtiB$&g~mkEnBj9{_L5G)2E}$>`UI_!obW(^E=nH*KXdo z^Wfgi>sK#Zx1eaR$AEE%%-ZMLZKQ6qvHXk-`TDAFicE%zKxb zf|ks}*PLO1D=g0hjDQzygm9bFWgnOMubG3A%s|>GB~*eu6R@j~PwU`kVMSIEydQ`| zkf8-*udBCz@LgwKs;`}kclW>k`?D}BB09IE9DtwA2sWVRp#SX$o(b67FDNWBl8k3E z;;E>+t*)vVVb_#6#6DtTV&f7L6O+igW+u9r>fqX%8meEH6p`%?q7X9iS+NS{AnP16 zz?3f@5Wd+xT5C>2>5oE&=)OZBIlJS9M|AR%1c zLvkbn04JMg0;cUr1pEd`qfH3a7zhV6wUn)wedtGWaX0Pm)Yu-?N*^PFf;FnMubZ&( z1Ypg{*?97tASRH%{@??BWC-JM7#fdGY?Ffp(m|VRl+#Tr6vG@$vEOJ7L=cG5|=s ztIJD^N&P8*D=jS@yN9GP-yQ5U!tdnLyJ1D-Q(YVNArAmn4R#3To}&O0_K4zQLYf$A zv;)90CIx5SJ4!LZnxUuDvB@+Y=j)jK914wQ^I2c$#A7P7-ocw_4JdA9Wbq3 zRRyVD*3Tc@HS!3Gj!#ZWO-;|D?YEbF?1Ml<>Z!?y@UVKOYvvIWlaQQ}0zPAs_rQP$ zS#duATv`QjUN%5D%@hY>2&A<6(C{8hhp;ot z=FX9ol9Ibz1rREn5eU_m(s+pbvX5(-Yc8E3FEdU`dR0VbZhn3ahztq}IeE9x?3L-| zHPdCsjvhO1yrOA%WL#o0G$1{L$wfU?k8OkREtZxVGivmhaWY!I{)i{g6*q~PKyd}G zb+bLMI!k)Ym{Frfj~lCoYbzTW{#fzfk7c*kTVkth7->OOqxSFMHW4~Ng%0@e)p}Bl*HUk z0^+Z7W<-#bj0ifK*aUTTL*~iQpNKTjJ%USm!wMhz{kK1HD6$@u6ep2?um3Qh=@D!O zk`EcS?2BgB3cG;P18BVD*Wdj?Pa=mY$6oSHfJ0Rz>Y>w_I^YS-06Y3R9!n6dExK3I zf#e-u=Vwx$37BUBZfO>T`veF0`UeDuMaL(mq;kuXUF@JnEP~E60b`q?(;aFIlp4}U z{GI$DP)4SOOa(hR7#k+1K9nL)`pxwbb_h_T4=wyEr_tCb$XI}VEpE8OF#)onG@2x& z?IiCM4x~QsX5%?IiAV~&nl95l9Xu1Tep)tS#5uyw(pW#&Y?oJI)-SH8oxY&Ca)Y|+ z_M4Bd-H%90&CJRKQ(uaIN@IkT)w65IPg(eyT-u_hx_QTub2t5>;*-(mlMrOdsj#0oVA2zP`SJ@sm4`ZCt(m zf=P~**;J4pWn<~-WNmHh=bX$X2wN^hew8oh6KU10Si0K zr!2G+LCkO1b;wTTD++hkr36lXEaI z?_xb&>M4Do5D*}_Ib0*}0_iU+LE!lFSaAt(f%6f_$l-hsWEj9nKz|H19G;~A%p4~H zJR!`8pcnR6l7mV6oAp6Ikvjt&VE^W_37Y;*&M7nX>#V-e#Owt63mQpC$(jD+=iV3{ zF(>E70A7TXw$d)s+n%~RUc}@WhX1gNff9nzB~b~TT|Evr?>*$~kgrVOZB?ZWT|FHo zp=L#mMlXP#NVf<2_p!m9wKXR8H4V5jI%%q{e%vc!xl)A0$=+2g6}57kx}=-iAet04IK@(M(V>f?=ckDqn* zw0Pxcf6Kz;s@A?Io|Z5Da&qztii$ght@+Uw$4}m}33W1kd{+JW{i`SU`@~y3x*wI0 zker?^?5N8Oak74@pA%%Gr@npDF0}(2mYnxCyQdo(6&)R$B|8oJn=?XOEp7cB?y8?UeSGKcojbQ}+^?;<@5KG5#uoOj z`2HIN8SWv$FD`3c);@mx)QKbNM>LPA9=h<*z#K)&(2vgc9PgkIlUrA>T)BGv=B-<| z@7}+8;rcTZOIrtLlDBs>78J(4df{OC;`vJ>V>D9`AP~uki@O(`i?qC3>x=VKBav9> z@8jX>Fm*l1c{3jtM0viO&KMV(xGt>@`fU61U#1*Ab{>(Ij z0w;hV1R)X=nFo@sb!-p>@gqR}5Xys6;Xa`x119J6pQW}}R+I@?f?@_`C}yP7#h4U_ zj&0loNxP`wZp`UFGo9&|_@6uc*)fm8a8Cd6J7#+0MMUR1>xSD}OAc<|*dXX~NR1|d zoDheg$vhMAs+VbLnb-k@!u(4MWA z_s9*OKVI5kqx6UovhsgJ8?#`o?5O3QK_MYw(V`BE`NMxSUZr??{7*mppfFE<*yu@e zY|MvNRk>Q`OBXt{X_MQsu5CfGh(GWv%RGv-YhHsWUh`41oa z%d7>9cqU*YGi#=ug*NUt73bf)fBE9I+js8XxpVLSgU8Pdj9y`f;MBLXtui?!&ezq+ z*~QV?+{DQEmARFjBgGj>j>KVP|5XdJk$oJC^kWbqd3bvJ`3E2gSrRcK{jZ_=!t4wb zf5)J_5YCn8n3xz2!i&I1zv@3K`%+LH$TfCf9@2jz(Bzqbc_v_<3Ah{Z>^*P$8w{?I z#mYiD>~zE7M)ywGmNJ{)D>Yg>#bCRtO4l&Vp-M(t)fAGN7T(WPDdwmX)0$fjv+5*Gkl49X^v?isQ(=#Yq&FieJ zJWhYvE9*e|N_t3f_{!IU0@CGgeduoxvpfKv37Cp~DQAXf0_K^3>D(k^n7qO`zj!8K z$|T~Mfb(-e1c`b_G@c2VX9BKyotF?6;0+2qcUPBJ`bH+E<~4x8X@(oN4@AR_Ri!xz zVIV5@@NjdtdG_4U*u)G@TvSG&gIGkxn3-{O0SAn?^NVN51vE9U1`99JCOcr?x7L&k zGGZe_0{sL0T@Ce(j7?0Jg0U3;BfM)`(<(YtO{e1jNN?%vzp4or=;?A|xWyTOw?+^I@r=NxoAFZJ0 zU}s-aR$h^(v1#$r<{MhIFlxI(yp{S(1ao^!1 z$4;KPaQW&D$f4PVRMMZ5ljv=#d;8uqL;Z($Z{E0h>o#JEMB|GNq?)X3AT=>QI?%(x z@|BUn%a{84FF77xHa?rwpJxJQ)T`j{1QRE@W05@$vReSP3{sjjwRa!he|RT>bO9<0c*Wq@{VIO{QCwa7y1w`Qdvw7J$OebAmzaKOdY%bbbg-29?q z_KmzNxvj0bY}ulDvlW34Fm3AeRXT1#v8h=(dHGB}@TTbHsin(R7R;Ij{DOHK&gk2F zhsGiSB0HOt_w{uKpWU@$`I5yex1Z88wQ=zai%v+%fKD+vN_fTHEqS5t&OTAGG2vn1 zv5Bb}m_4tcKq6-tJ(S+hGXYbS10j0&8By!j%&{0sh&ov=JO%8zQ~-)cfajBFZ@)H{ zCLrk{kq^nZj%+Y>K)3`Qxba|1WQ|S5J%s`#&@bW<<(NRf??KrJG(%?xjR#xkx5lPG zT@5EDreB^3*z@VF!&|nmU7jE6tIUpD2$m6Xj)gdk03wqR<8(e{X;N9qm2qm&^e+$mB_r(PiR0Mg{5Ns1-X+u?U&bADZ~aYOH!v9*&Y6(K}pJeC&GXm&T}1iCnQhDV0_ z`}p{yzCVUieABr3Ii_1=tu_M5m62$n3JAMLZZ4*0>y~K+%0C;ZikH>j(#c$0#F#p< z!f8PhwIliGi6MrAc@+r0DhF09=@ojQ5oZ_cQ#Pa=cn6$DPkh3&?o(Y&Lz(CZFDlHt^J@iH4a};&6ucMz5U8;brjc z+qZA}809MME;#>D`HexG{LTALVgm1a^QN2Iyhz^OED;a9tFqQyx9Zmw+b*Z}4N$hB zh}cG3n>hKK-UzLY+m*WL z^bueiEdhG?Kqt=x{NT`>`M*xb_8>n|Ve#&Bcb}SAJGyyz`O)Xw*W2CO;j6iB{=C_< z7Op*X@z(vvFR6sz19}cQzIJ@6IRAs)oO~i8g1y~bJ*a>e3Aiz_Y+cX>LB)VVq~ew3 z6FPP>RTxs99PMvJc8w%8eBBJH8$jK_@5l#_G7@Ro{slR908(!%)FlpIhOkGsYT7@z zJ%nu#3@ZSxJe~=dObDF+EQJw@hDON~tWml@)A~3) zQ`5L2LZq|QmgoAI=-$43Qg!$4z55TSozag%nIY7O#tWO_dXI5@e(Ta1js4qq?Af